Skip to content

Commit 9266db0

Browse files
committed
Seek in buffer
1 parent e1071fa commit 9266db0

File tree

2 files changed

+79
-67
lines changed

2 files changed

+79
-67
lines changed

Src/IronPython/Modules/_io.cs

Lines changed: 74 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

Src/IronPython/Runtime/Bytes.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,6 +1060,11 @@ private static Bytes JoinOne(object? curVal) {
10601060
}
10611061

10621062
internal static Bytes Concat(IList<Bytes> list, int length) {
1063+
if (list.Count == 1) {
1064+
Debug.Assert(list[0].Count == length);
1065+
return list[0];
1066+
}
1067+
10631068
byte[] res = new byte[length];
10641069
int count = 0;
10651070
for (int i = 0; i < list.Count; i++) {

0 commit comments

Comments
 (0)