@@ -4,7 +4,11 @@ use crossbeam_utils::atomic::AtomicCell;
44use num_bigint:: BigInt ;
55use num_traits:: Zero ;
66
7- use super :: int:: PyIntRef ;
7+ use super :: int:: { try_to_primitive, PyInt , PyIntRef } ;
8+ use super :: iter:: {
9+ IterStatus ,
10+ IterStatus :: { Active , Exhausted } ,
11+ } ;
812use super :: pytype:: PyTypeRef ;
913use crate :: function:: OptionalArg ;
1014use crate :: slots:: PyIter ;
@@ -64,7 +68,8 @@ impl PyIter for PyEnumerate {
6468#[ pyclass( module = false , name = "reversed" ) ]
6569#[ derive( Debug ) ]
6670pub struct PyReverseSequenceIterator {
67- pub position : AtomicCell < isize > ,
71+ pub position : AtomicCell < usize > ,
72+ pub status : AtomicCell < IterStatus > ,
6873 pub obj : PyObjectRef ,
6974}
7075
@@ -76,32 +81,77 @@ impl PyValue for PyReverseSequenceIterator {
7681
7782#[ pyimpl( with( PyIter ) ) ]
7883impl PyReverseSequenceIterator {
79- pub fn new ( obj : PyObjectRef , len : isize ) -> Self {
84+ pub fn new ( obj : PyObjectRef , len : usize ) -> Self {
8085 Self {
81- position : AtomicCell :: new ( len - 1 ) ,
86+ position : AtomicCell :: new ( len. saturating_sub ( 1 ) ) ,
87+ status : AtomicCell :: new ( if len == 0 { Exhausted } else { Active } ) ,
8288 obj,
8389 }
8490 }
8591
8692 #[ pymethod( magic) ]
87- fn length_hint ( & self ) -> PyResult < isize > {
88- Ok ( self . position . load ( ) + 1 )
93+ fn length_hint ( & self , vm : & VirtualMachine ) -> PyResult < usize > {
94+ Ok ( match self . status . load ( ) {
95+ Active => {
96+ let position = self . position . load ( ) ;
97+ if position > vm. obj_len ( & self . obj ) ? {
98+ 0
99+ } else {
100+ position + 1
101+ }
102+ }
103+ Exhausted => 0 ,
104+ } )
105+ }
106+
107+ #[ pymethod( magic) ]
108+ fn setstate ( & self , state : PyObjectRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
109+ // When we're exhausted, just return.
110+ if let Exhausted = self . status . load ( ) {
111+ return Ok ( ( ) ) ;
112+ }
113+ let len = vm. obj_len ( & self . obj ) ?;
114+ let pos = state
115+ . payload :: < PyInt > ( )
116+ . ok_or_else ( || vm. new_type_error ( "an integer is required." . to_owned ( ) ) ) ?;
117+ let pos = std:: cmp:: min (
118+ try_to_primitive ( pos. as_bigint ( ) , vm) . unwrap_or ( 0 ) ,
119+ len. saturating_sub ( 1 ) ,
120+ ) ;
121+ self . position . store ( pos) ;
122+ Ok ( ( ) )
123+ }
124+
125+ #[ pymethod( magic) ]
126+ fn reduce ( & self , vm : & VirtualMachine ) -> PyResult {
127+ let iter = vm. get_attribute ( vm. builtins . clone ( ) , "reversed" ) ?;
128+ Ok ( vm. ctx . new_tuple ( match self . status . load ( ) {
129+ Exhausted => vec ! [ iter, vm. ctx. new_tuple( vec![ vm. ctx. new_tuple( vec![ ] ) ] ) ] ,
130+ Active => vec ! [
131+ iter,
132+ vm. ctx. new_tuple( vec![ self . obj. clone( ) ] ) ,
133+ vm. ctx. new_int( self . position. load( ) ) ,
134+ ] ,
135+ } ) )
89136 }
90137}
91138
92139impl PyIter for PyReverseSequenceIterator {
93140 fn next ( zelf : & PyRef < Self > , vm : & VirtualMachine ) -> PyResult {
141+ if let Exhausted = zelf. status . load ( ) {
142+ return Err ( vm. new_stop_iteration ( ) ) ;
143+ }
94144 let pos = zelf. position . fetch_sub ( 1 ) ;
95- if pos > = 0 {
96- match zelf. obj . get_item ( pos , vm ) {
97- Err ( ref e ) if e . isinstance ( & vm . ctx . exceptions . index_error ) => {
98- Err ( vm . new_stop_iteration ( ) )
99- }
100- // also catches stop_iteration => stop_iteration
101- ret => ret ,
145+ if pos = = 0 {
146+ zelf. status . store ( Exhausted ) ;
147+ }
148+ match zelf . obj . get_item ( pos , vm ) {
149+ Err ( ref e ) if e . isinstance ( & vm . ctx . exceptions . index_error ) => {
150+ zelf . status . store ( Exhausted ) ;
151+ Err ( vm . new_stop_iteration ( ) )
102152 }
103- } else {
104- Err ( vm . new_stop_iteration ( ) )
153+ // also catches stop_iteration => stop_iteration
154+ ret => ret ,
105155 }
106156 }
107157}
0 commit comments