@@ -21,7 +21,7 @@ mod _struct {
2121 use std:: io:: { Cursor , Read , Write } ;
2222 use std:: iter:: Peekable ;
2323
24- use crate :: byteslike:: PyBuffer ;
24+ use crate :: byteslike:: { PyBuffer , PyBytesLike } ;
2525 use crate :: exceptions:: PyBaseExceptionRef ;
2626 use crate :: function:: Args ;
2727 use crate :: obj:: {
@@ -100,11 +100,11 @@ mod _struct {
100100 fn parse ( fmt : & str ) -> Result < FormatSpec , String > {
101101 let mut chars = fmt. chars ( ) . peekable ( ) ;
102102
103- // First determine "<", ">","!" or "="
104- let endianness = parse_endiannes ( & mut chars) ;
103+ // First determine "@", " <", ">","!" or "="
104+ let ( is_native_size , endianness) = parse_size_and_endiannes ( & mut chars) ;
105105
106106 // Now, analyze struct string furter:
107- let codes = parse_format_codes ( & mut chars) ?;
107+ let codes = parse_format_codes ( & mut chars, is_native_size ) ?;
108108
109109 Ok ( FormatSpec { endianness, codes } )
110110 }
@@ -190,36 +190,36 @@ mod _struct {
190190
191191 /// Parse endianness
192192 /// See also: https://docs.python.org/3/library/struct.html?highlight=struct#byte-order-size-and-alignment
193- fn parse_endiannes < I > ( chars : & mut Peekable < I > ) -> Endianness
193+ fn parse_size_and_endiannes < I > ( chars : & mut Peekable < I > ) -> ( bool , Endianness )
194194 where
195195 I : Sized + Iterator < Item = char > ,
196196 {
197197 match chars. peek ( ) {
198198 Some ( '@' ) => {
199199 chars. next ( ) . unwrap ( ) ;
200- Endianness :: Native
200+ ( true , Endianness :: Native )
201201 }
202202 Some ( '=' ) => {
203203 chars. next ( ) . unwrap ( ) ;
204- Endianness :: Native
204+ ( false , Endianness :: Native )
205205 }
206206 Some ( '<' ) => {
207207 chars. next ( ) . unwrap ( ) ;
208- Endianness :: Little
208+ ( false , Endianness :: Little )
209209 }
210210 Some ( '>' ) => {
211211 chars. next ( ) . unwrap ( ) ;
212- Endianness :: Big
212+ ( false , Endianness :: Big )
213213 }
214214 Some ( '!' ) => {
215215 chars. next ( ) . unwrap ( ) ;
216- Endianness :: Network
216+ ( false , Endianness :: Network )
217217 }
218- _ => Endianness :: Native ,
218+ _ => ( true , Endianness :: Native ) ,
219219 }
220220 }
221221
222- fn parse_format_codes < I > ( chars : & mut Peekable < I > ) -> Result < Vec < FormatCode > , String >
222+ fn parse_format_codes < I > ( chars : & mut Peekable < I > , is_native_size : bool ) -> Result < Vec < FormatCode > , String >
223223 where
224224 I : Sized + Iterator < Item = char > ,
225225 {
@@ -243,6 +243,9 @@ mod _struct {
243243 // determine format char:
244244 let c = chars. next ( ) ;
245245 match c {
246+ Some ( 'n' ) | Some ( 'N' ) if !is_native_size => {
247+ return Err ( "bad char in struct format" . to_owned ( ) )
248+ }
246249 Some ( c) if is_supported_format_character ( c) => {
247250 codes. push ( FormatCode { repeat, code : c } )
248251 }
@@ -667,11 +670,11 @@ mod _struct {
667670 #[ pyfunction]
668671 fn unpack (
669672 fmt : Either < PyStringRef , PyBytesRef > ,
670- buffer : PyBytesRef ,
673+ buffer : PyBytesLike ,
671674 vm : & VirtualMachine ,
672675 ) -> PyResult < PyTuple > {
673676 let format_spec = FormatSpec :: decode_and_parse ( vm, & fmt) ?;
674- format_spec. unpack ( buffer . get_value ( ) , vm)
677+ buffer . with_ref ( |buf| format_spec. unpack ( buf , vm) )
675678 }
676679
677680 fn unpack_code < Endianness > (
@@ -721,26 +724,49 @@ mod _struct {
721724 Ok ( ( ) )
722725 }
723726
727+ #[ derive( FromArgs ) ]
728+ struct UpdateFromArgs {
729+ buffer : PyBytesLike ,
730+ #[ pyarg( positional_or_keyword, default = "0" ) ]
731+ offset : isize
732+ }
733+
724734 #[ pyfunction]
725735 fn unpack_from (
726736 fmt : Either < PyStringRef , PyBytesRef > ,
727- buffer : PyBytesRef ,
728- offset : isize ,
737+ args : UpdateFromArgs ,
729738 vm : & VirtualMachine ,
730739 ) -> PyResult < PyTuple > {
731740 let format_spec = FormatSpec :: decode_and_parse ( vm, & fmt) ?;
732- let offset = get_buffer_offset ( buffer. len ( ) , offset, format_spec. size ( ) , vm) ?;
733- format_spec. unpack ( & buffer. get_value ( ) [ offset..] , vm)
741+ let size = format_spec. size ( ) ;
742+ let offset = get_buffer_offset ( args. buffer . len ( ) , args. offset , size, vm) ?;
743+ args. buffer . with_ref ( |buf| format_spec. unpack ( & buf[ offset..offset+size] , vm) )
734744 }
735745
736746 #[ pyclass( name = "unpack_iterator" ) ]
737747 #[ derive( Debug ) ]
738748 struct UnpackIterator {
739749 format_spec : FormatSpec ,
740- buffer : PyBytesRef ,
750+ buffer : PyBytesLike ,
741751 offset : AtomicCell < usize > ,
742752 }
743753
754+ impl UnpackIterator {
755+ fn new ( vm : & VirtualMachine , format_spec : FormatSpec , buffer : PyBytesLike ) -> PyResult < UnpackIterator > {
756+ if format_spec. size ( ) == 0 {
757+ Err ( new_struct_error ( vm, "cannot iteratively unpack with a struct of length 0" . to_owned ( ) ) )
758+ } else if buffer. len ( ) % format_spec. size ( ) != 0 {
759+ Err ( new_struct_error ( vm, format ! ( "iterative unpacking requires a buffer of a multiple of {} bytes" , format_spec. size( ) ) ) )
760+ } else {
761+ Ok ( UnpackIterator {
762+ format_spec,
763+ buffer,
764+ offset : AtomicCell :: new ( 0 ) ,
765+ } )
766+ }
767+ }
768+ }
769+
744770 impl PyValue for UnpackIterator {
745771 fn class ( vm : & VirtualMachine ) -> PyClassRef {
746772 vm. class ( "_struct" , "unpack_iterator" )
@@ -756,24 +782,30 @@ mod _struct {
756782 if offset + size > self . buffer . len ( ) {
757783 Err ( objiter:: new_stop_iteration ( vm) )
758784 } else {
759- self . format_spec
760- . unpack ( & self . buffer . get_value ( ) [ offset..offset + size] , vm)
785+ self . buffer . with_ref ( |buf| self . format_spec
786+ . unpack ( & buf [ offset..offset + size] , vm) )
761787 }
762788 }
789+
790+ #[ pymethod( magic) ]
791+ fn iter ( zelf : PyRef < Self > ) -> PyRef < Self > {
792+ zelf
793+ }
794+
795+ #[ pymethod( magic) ]
796+ fn length_hint ( & self ) -> usize {
797+ self . buffer . len ( ) . saturating_sub ( self . offset . load ( ) ) / self . format_spec . size ( )
798+ }
763799 }
764800
765801 #[ pyfunction]
766802 fn iter_unpack (
767803 fmt : Either < PyStringRef , PyBytesRef > ,
768- buffer : PyBytesRef ,
804+ buffer : PyBytesLike ,
769805 vm : & VirtualMachine ,
770806 ) -> PyResult < UnpackIterator > {
771807 let format_spec = FormatSpec :: decode_and_parse ( vm, & fmt) ?;
772- Ok ( UnpackIterator {
773- format_spec,
774- buffer,
775- offset : AtomicCell :: new ( 0 ) ,
776- } )
808+ UnpackIterator :: new ( vm, format_spec, buffer)
777809 }
778810
779811 #[ pyfunction]
@@ -843,28 +875,24 @@ mod _struct {
843875 }
844876
845877 #[ pymethod]
846- fn unpack ( & self , data : PyBytesRef , vm : & VirtualMachine ) -> PyResult < PyTuple > {
847- self . spec . unpack ( data . get_value ( ) , vm)
878+ fn unpack ( & self , data : PyBytesLike , vm : & VirtualMachine ) -> PyResult < PyTuple > {
879+ data . with_ref ( |buf| self . spec . unpack ( buf , vm) )
848880 }
849881
850882 #[ pymethod]
851883 fn unpack_from (
852884 & self ,
853- buffer : PyBytesRef ,
854- offset : isize ,
885+ args : UpdateFromArgs ,
855886 vm : & VirtualMachine ,
856887 ) -> PyResult < PyTuple > {
857- let offset = get_buffer_offset ( buffer. len ( ) , offset, self . size ( ) , vm) ?;
858- self . spec . unpack ( & buffer. get_value ( ) [ offset..] , vm)
888+ let size = self . size ( ) ;
889+ let offset = get_buffer_offset ( args. buffer . len ( ) , args. offset , size, vm) ?;
890+ args. buffer . with_ref ( |buf| self . spec . unpack ( & buf[ offset..offset+size] , vm) )
859891 }
860892
861893 #[ pymethod]
862- fn iter_unpack ( & self , buffer : PyBytesRef ) -> PyResult < UnpackIterator > {
863- Ok ( UnpackIterator {
864- format_spec : self . spec . clone ( ) ,
865- buffer,
866- offset : AtomicCell :: new ( 0 ) ,
867- } )
894+ fn iter_unpack ( & self , buffer : PyBytesLike , vm : & VirtualMachine ) -> PyResult < UnpackIterator > {
895+ UnpackIterator :: new ( vm, self . spec . clone ( ) , buffer)
868896 }
869897 }
870898
0 commit comments