@@ -613,6 +613,7 @@ public class BufferedReader : _BufferedIOBase, IDynamicMetaObjectProvider {
613613 private int _bufSize ;
614614 private Bytes _readBuf ;
615615 private int _readBufPos ;
616+ private long _absPos = - 1 ;
616617
617618 internal static BufferedReader Create ( CodeContext /*!*/ context , object raw , int buffer_size = DEFAULT_BUFFER_SIZE ) {
618619 var res = new BufferedReader ( context , raw , buffer_size ) ;
@@ -714,12 +715,7 @@ public override bool readable(CodeContext/*!*/ context) {
714715 return PythonOps . IsTrue ( PythonOps . Invoke ( context , _raw , "readable" ) ) ;
715716 }
716717
717- public override bool writable ( CodeContext /*!*/ context ) {
718- if ( _rawIO != null ) {
719- return _rawIO . writable ( context ) ;
720- }
721- return PythonOps . IsTrue ( PythonOps . Invoke ( context , _raw , "writable" ) ) ;
722- }
718+ public override bool writable ( CodeContext /*!*/ context ) => false ;
723719
724720 public override bool closed {
725721 get {
@@ -773,28 +769,33 @@ public override object read(CodeContext/*!*/ context, object length = null) {
773769 }
774770 }
775771
776- private Bytes ReadNoLock ( CodeContext /*!*/ context , int length , bool read1 = false ) {
772+ #nullable enable
773+
774+ private Bytes ? CallRawRead ( CodeContext /*!*/ context , object length ) {
775+ object ? obj = _rawIO != null ? _rawIO . read ( context , length ) : PythonOps . Invoke ( context , _raw , "read" , length ) ;
776+ if ( obj is null ) return null ;
777+ if ( obj is Bytes bytes ) {
778+ _absPos += bytes . Count ;
779+ return bytes ;
780+ }
781+ throw PythonOps . TypeError ( "'read()' should have returned bytes" ) ;
782+ }
783+
784+ private Bytes ? ReadNoLock ( CodeContext /*!*/ context , int length , bool read1 = false ) {
777785 if ( length == 0 ) {
778786 return Bytes . Empty ;
779787 }
780788
781789 if ( length < 0 ) {
782790 List < Bytes > chunks = new List < Bytes > ( ) ;
783791 int count = 0 ;
784- if ( _readBuf . Count > 0 ) {
785- chunks . Add ( ResetReadBuf ( ) ) ;
792+ if ( TryResetReadBuf ( out Bytes res ) ) {
793+ chunks . Add ( res ) ;
786794 count += chunks [ 0 ] . Count ;
787795 }
788796 for ( ; ; )
789797 {
790- object chunkObj ;
791- if ( _rawIO != null ) {
792- chunkObj = _rawIO . read ( context , - 1 ) ;
793- } else {
794- chunkObj = PythonOps . Invoke ( context , _raw , "read" , - 1 ) ;
795- }
796-
797- Bytes chunk = GetBytes ( chunkObj , "read()" ) ;
798+ var chunk = CallRawRead ( context , - 1 ) ;
798799 if ( chunk == null || chunk . Count == 0 ) {
799800 if ( count == 0 ) {
800801 return chunk ;
@@ -825,28 +826,23 @@ private Bytes ReadNoLock(CodeContext/*!*/ context, int length, bool read1 = fals
825826 // a read is required to provide requested amount of data
826827 List < Bytes > chunks = new List < Bytes > ( ) ;
827828 int remaining = length ;
828- if ( _readBuf . Count > 0 ) {
829- chunks . Add ( ResetReadBuf ( ) ) ;
829+ if ( _readBuf . Count > 0 && TryResetReadBuf ( out Bytes res ) ) {
830+ chunks . Add ( res ) ;
830831 remaining -= chunks [ 0 ] . Count ;
831832 }
832833
833834 while ( remaining > 0 ) {
834- object chunkObj ;
835- if ( _rawIO != null ) {
836- chunkObj = _rawIO . read ( context , _bufSize ) ;
837- } else {
838- chunkObj = PythonOps . Invoke ( context , _raw , "read" , _bufSize ) ;
839- }
840-
841- Bytes chunk = chunkObj != null ? GetBytes ( chunkObj , "read()" ) : Bytes . Empty ;
835+ var chunk = CallRawRead ( context , _bufSize ) ?? Bytes . Empty ;
842836
843837 _readBuf = chunk ;
844838 if ( _readBuf . Count == 0 ) {
845839 break ;
846840 }
847841 if ( remaining >= _readBuf . Count - _readBufPos ) {
848842 remaining -= _readBuf . Count - _readBufPos ;
849- chunks . Add ( ResetReadBuf ( ) ) ;
843+ if ( TryResetReadBuf ( out Bytes res2 ) ) {
844+ chunks . Add ( res2 ) ;
845+ }
850846 } else {
851847 byte [ ] bytes = new byte [ remaining ] ;
852848 Array . Copy ( _readBuf . UnsafeByteArray , 0 , bytes , 0 , remaining ) ;
@@ -862,6 +858,8 @@ private Bytes ReadNoLock(CodeContext/*!*/ context, int length, bool read1 = fals
862858 }
863859 }
864860
861+ #nullable restore
862+
865863 public override Bytes peek ( CodeContext /*!*/ context , int length = 0 ) {
866864 _checkClosed ( ) ;
867865
@@ -883,16 +881,13 @@ private Bytes PeekNoLock(CodeContext/*!*/ context, int length) {
883881 return Bytes . Make ( bytes ) ;
884882 }
885883
886- object nextObj ;
887- if ( _rawIO != null ) {
888- nextObj = _rawIO . read ( context , length - _readBuf . Count + _readBufPos ) ;
884+ var next = CallRawRead ( context , length - _readBuf . Count + _readBufPos ) ?? Bytes . Empty ;
885+
886+ if ( TryResetReadBuf ( out Bytes res ) ) {
887+ _readBuf = res + next ;
889888 } else {
890- nextObj = PythonOps . Invoke ( context , _raw , "read" , length - _readBuf . Count + _readBufPos ) ;
889+ _readBuf = next ;
891890 }
892-
893- Bytes next = nextObj != null ? GetBytes ( nextObj , "read()" ) : Bytes . Empty ;
894-
895- _readBuf = ResetReadBuf ( ) + next ;
896891 return _readBuf ;
897892 }
898893
@@ -952,7 +947,9 @@ public override object readline(CodeContext context, int limit) {
952947 return Bytes . Concat ( chunks , cnt ) ;
953948 }
954949
955- ( chunks ??= new List < Bytes > ( ) ) . Add ( ResetReadBuf ( ) ) ;
950+ if ( TryResetReadBuf ( out Bytes res ) ) {
951+ ( chunks ??= new List < Bytes > ( ) ) . Add ( res ) ;
952+ }
956953 cnt += buf . Length ;
957954 }
958955
@@ -968,22 +965,14 @@ public override object readline(CodeContext context, int limit) {
968965 }
969966
970967 bool TryReadNextChunk ( CodeContext context ) {
971- object chunkObj ;
972- if ( _rawIO != null ) {
973- chunkObj = _rawIO . read ( context , _bufSize ) ;
974- } else {
975- chunkObj = PythonOps . Invoke ( context , _raw , "read" , _bufSize ) ;
976- }
977-
978- Bytes chunk = chunkObj != null ? GetBytes ( chunkObj , "read()" ) : Bytes . Empty ;
979-
968+ var chunk = CallRawRead ( context , _bufSize ) ?? Bytes . Empty ;
980969 _readBuf = chunk ;
981970 return chunk . Count != 0 ;
982971 }
983972 }
984973
985974 public override BigInteger tell ( CodeContext /*!*/ context ) {
986- BigInteger res = _rawIO != null ?
975+ var res = _rawIO != null ?
987976 _rawIO . tell ( context ) :
988977 GetBigInt (
989978 PythonOps . Invoke ( context , _raw , "tell" ) ,
@@ -993,6 +982,7 @@ public override BigInteger tell(CodeContext/*!*/ context) {
993982 throw InvalidPosition ( res ) ;
994983 }
995984
985+ _absPos = checked ( ( long ) res ) ;
996986 return res - _readBuf . Count + _readBufPos ;
997987 }
998988
@@ -1009,6 +999,23 @@ public override BigInteger seek(CodeContext/*!*/ context, BigInteger pos, [Optio
1009999 }
10101000
10111001 lock ( this ) {
1002+ // fast-path to seek within the buffer
1003+ if ( _readBuf . Count > 0 && _absPos != - 1 ) {
1004+ if ( whenceInt == 0 ) {
1005+ var readBufPos = pos - _absPos + _readBuf . Count ;
1006+ if ( 0 <= readBufPos && readBufPos < _readBuf . Count ) {
1007+ _readBufPos = unchecked ( ( int ) readBufPos ) ;
1008+ return _absPos - _readBuf . Count + _readBufPos ;
1009+ }
1010+ } else if ( whenceInt == 1 ) {
1011+ var readBufPos = _readBufPos + pos ;
1012+ if ( 0 <= readBufPos && readBufPos < _readBuf . Count ) {
1013+ _readBufPos = unchecked ( ( int ) readBufPos ) ;
1014+ return _absPos - _readBuf . Count + _readBufPos ;
1015+ }
1016+ }
1017+ }
1018+
10121019 if ( whenceInt == 1 ) {
10131020 pos -= _readBuf . Count - _readBufPos ;
10141021 }
@@ -1021,6 +1028,7 @@ public override BigInteger seek(CodeContext/*!*/ context, BigInteger pos, [Optio
10211028 }
10221029
10231030 pos = GetBigInt ( posObj , "seek() should return integer" ) ;
1031+ _absPos = checked ( ( long ) pos ) ;
10241032 ResetReadBuf ( ) ;
10251033 if ( pos < 0 ) {
10261034 throw InvalidPosition ( pos ) ;
@@ -1051,8 +1059,12 @@ DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter)
10511059
10521060 #region Private implementation details
10531061
1054- private Bytes ResetReadBuf ( ) {
1055- Bytes res ;
1062+ private void ResetReadBuf ( ) {
1063+ _readBufPos = 0 ;
1064+ _readBuf = Bytes . Empty ;
1065+ }
1066+
1067+ private bool TryResetReadBuf ( out Bytes res ) {
10561068 if ( _readBufPos == 0 ) {
10571069 res = _readBuf ;
10581070 } else {
@@ -1063,7 +1075,7 @@ private Bytes ResetReadBuf() {
10631075 }
10641076 _readBuf = Bytes . Empty ;
10651077
1066- return res ;
1078+ return res . Count > 0 ;
10671079 }
10681080
10691081 #endregion
@@ -3093,6 +3105,8 @@ public static PythonType BlockingIOError {
30933105
30943106 #region Private implementation details
30953107
3108+ #nullable enable
3109+
30963110 private static readonly HashSet < char > _validModes = MakeSet ( "abrtwxU+" ) ;
30973111
30983112 private static HashSet < char > MakeSet ( string chars ) {
@@ -3104,8 +3118,7 @@ private static HashSet<char> MakeSet(string chars) {
31043118 }
31053119
31063120 private static BigInteger GetBigInt ( object i , string msg ) {
3107- BigInteger res ;
3108- if ( TryGetBigInt ( i , out res ) ) {
3121+ if ( TryGetBigInt ( i , out BigInteger res ) ) {
31093122 return res ;
31103123 }
31113124
@@ -3137,15 +3150,7 @@ private static bool TryGetBigInt(object i, out BigInteger res) {
31373150 return false ;
31383151 }
31393152
3140- private static int GetInt ( object i ) {
3141- return GetInt ( i , null , null ) ;
3142- }
3143-
3144- private static int GetInt ( object i , int defaultValue ) {
3145- return GetInt ( i , defaultValue , null , null ) ;
3146- }
3147-
3148- private static int GetInt ( object i , string msg , params object [ ] args ) {
3153+ private static int GetInt ( object ? i , string ? msg = null ) {
31493154 if ( i == Missing . Value ) return 0 ;
31503155
31513156 int res ;
@@ -3157,18 +3162,18 @@ private static int GetInt(object i, string msg, params object[] args) {
31573162 throw PythonOps . TypeError ( "integer argument expected, got '{0}'" , PythonOps . GetPythonTypeName ( i ) ) ;
31583163 }
31593164
3160- throw PythonOps . TypeError ( msg , args ) ;
3165+ throw PythonOps . TypeError ( msg ) ;
31613166 }
31623167
3163- private static int GetInt ( object i , int defaultValue , string msg , params object [ ] args ) {
3168+ private static int GetInt ( object ? i , int defaultValue , string ? msg = null ) {
31643169 if ( i == null ) {
31653170 return defaultValue ;
31663171 }
31673172
3168- return GetInt ( i , msg , args ) ;
3173+ return GetInt ( i , msg ) ;
31693174 }
31703175
3171- private static bool TryGetInt ( object i , out int value ) {
3176+ private static bool TryGetInt ( object ? i , out int value ) {
31723177 if ( i == null ) {
31733178 value = int . MinValue ;
31743179 return false ;
@@ -3190,15 +3195,15 @@ private static bool TryGetInt(object i, out int value) {
31903195 /// <summary>
31913196 /// Convert string or bytes into bytes
31923197 /// </summary>
3193- private static Bytes GetBytes ( object o , string name ) {
3198+ private static Bytes ? GetBytes ( object ? o , string name ) {
31943199 if ( o == null )
31953200 return null ;
31963201
31973202 if ( o is Bytes bytes ) {
31983203 return bytes ;
31993204 }
32003205
3201- string s = o as string ;
3206+ string ? s = o as string ;
32023207 if ( s == null ) {
32033208 if ( o is Extensible < string > es ) {
32043209 s = es . Value ;
@@ -3212,6 +3217,8 @@ private static Bytes GetBytes(object o, string name) {
32123217 throw PythonOps . TypeError ( "'" + name + "' should have returned bytes" ) ;
32133218 }
32143219
3220+ #nullable restore
3221+
32153222 #endregion
32163223 }
32173224}
0 commit comments