mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-10 12:32:20 +02:00
Stream buffering
This commit is contained in:
parent
2345d5d349
commit
4bf12e121c
11 changed files with 482 additions and 28 deletions
|
@ -215,10 +215,19 @@ namespace System
|
|||
{
|
||||
T mFirstElement;
|
||||
|
||||
public T* Ptr
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return &mFirstElement;
|
||||
}
|
||||
}
|
||||
|
||||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
[Inline]
|
||||
ref T GetRef(int idx)
|
||||
{
|
||||
|
@ -330,6 +339,15 @@ namespace System
|
|||
int_arsize mLength1;
|
||||
T mFirstElement;
|
||||
|
||||
public T* Ptr
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return &mFirstElement;
|
||||
}
|
||||
}
|
||||
|
||||
Array GetSelf()
|
||||
{
|
||||
return this;
|
||||
|
@ -338,7 +356,7 @@ namespace System
|
|||
public this()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public int GetLength(int dim)
|
||||
{
|
||||
if (dim == 0)
|
||||
|
@ -447,6 +465,15 @@ namespace System
|
|||
int_arsize mLength2;
|
||||
T mFirstElement;
|
||||
|
||||
public T* Ptr
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return &mFirstElement;
|
||||
}
|
||||
}
|
||||
|
||||
Array GetSelf()
|
||||
{
|
||||
return this;
|
||||
|
@ -569,6 +596,15 @@ namespace System
|
|||
int_arsize mLength3;
|
||||
T mFirstElement;
|
||||
|
||||
public T* Ptr
|
||||
{
|
||||
[Inline]
|
||||
get
|
||||
{
|
||||
return &mFirstElement;
|
||||
}
|
||||
}
|
||||
|
||||
Array GetSelf()
|
||||
{
|
||||
return this;
|
||||
|
|
183
BeefLibs/corlib/src/IO/BufferedStream.bf
Normal file
183
BeefLibs/corlib/src/IO/BufferedStream.bf
Normal file
|
@ -0,0 +1,183 @@
|
|||
namespace System.IO
|
||||
{
|
||||
abstract class BufferedStream : Stream
|
||||
{
|
||||
protected int64 mPos;
|
||||
protected int64 mUnderlyingLength;
|
||||
protected uint8[] mBuffer ~ delete _;
|
||||
protected int64 mBufferPos = -Int32.MinValue;
|
||||
protected int64 mBufferEnd = -Int32.MinValue;
|
||||
protected int64 mWriteDirtyPos = -Int32.MinValue;
|
||||
protected int64 mWriteDirtyEnd = -Int32.MinValue;
|
||||
|
||||
public override int64 Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return mPos;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
mPos = Math.Min(value, Length);
|
||||
}
|
||||
}
|
||||
|
||||
public override int64 Length
|
||||
{
|
||||
get
|
||||
{
|
||||
UpdateLength();
|
||||
return Math.Max(mUnderlyingLength, mWriteDirtyEnd);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void UpdateLength();
|
||||
protected abstract Result<int> TryReadUnderlying(int64 pos, Span<uint8> data);
|
||||
protected abstract Result<int> TryWriteUnderlying(int64 pos, Span<uint8> data);
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
|
||||
public override Result<void> Seek(int64 pos, SeekKind seekKind = .Absolute)
|
||||
{
|
||||
int64 length = Length;
|
||||
|
||||
int64 newPos;
|
||||
switch (seekKind)
|
||||
{
|
||||
case .Absolute:
|
||||
mPos = Math.Min(pos, length);
|
||||
if (pos > length)
|
||||
return .Err;
|
||||
case .FromEnd:
|
||||
newPos = length - pos;
|
||||
case .Relative:
|
||||
mPos = Math.Min(mPos + pos, length);
|
||||
}
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public void MakeBuffer(int size)
|
||||
{
|
||||
delete mBuffer;
|
||||
mBuffer = new uint8[size];
|
||||
}
|
||||
|
||||
public override Result<int> TryRead(Span<uint8> data)
|
||||
{
|
||||
int64 spaceLeft = mBufferEnd - mPos;
|
||||
if (data.Length <= spaceLeft)
|
||||
{
|
||||
Internal.MemCpy(data.Ptr, mBuffer.Ptr + (mPos - mBufferPos), data.Length);
|
||||
mPos += data.Length;
|
||||
return data.Length;
|
||||
}
|
||||
|
||||
int64 readStart = mPos;
|
||||
|
||||
var data;
|
||||
if (spaceLeft > 0)
|
||||
{
|
||||
Internal.MemCpy(data.Ptr, mBuffer.Ptr + (mPos - mBufferPos), spaceLeft);
|
||||
mPos += spaceLeft;
|
||||
data.RemoveFromStart(spaceLeft);
|
||||
}
|
||||
|
||||
if (mWriteDirtyPos >= 0)
|
||||
Try!(Flush());
|
||||
|
||||
if ((mBuffer == null) || (data.Length > mBuffer.Count))
|
||||
{
|
||||
var result = TryReadUnderlying(mPos, data);
|
||||
if (result case .Ok(let len))
|
||||
mPos += len;
|
||||
return mPos - readStart;
|
||||
}
|
||||
|
||||
var result = TryReadUnderlying(mPos, mBuffer);
|
||||
switch (result)
|
||||
{
|
||||
case .Ok(let len):
|
||||
mBufferPos = mPos;
|
||||
mBufferEnd = mPos + len;
|
||||
int readLen = Math.Min(len, data.Length);
|
||||
Internal.MemCpy(data.Ptr, mBuffer.Ptr, readLen);
|
||||
mPos += readLen;
|
||||
return mPos - readStart;
|
||||
case .Err:
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public override Result<int> TryWrite(Span<uint8> data)
|
||||
{
|
||||
var data;
|
||||
|
||||
if ((mWriteDirtyEnd >= 0) && (mWriteDirtyEnd != mPos))
|
||||
{
|
||||
Try!(Flush());
|
||||
}
|
||||
|
||||
int writeCount = 0;
|
||||
if (mWriteDirtyEnd >= 0)
|
||||
{
|
||||
int64 spaceLeft = (mBufferPos + mBuffer.Count) - mPos;
|
||||
if (data.Length <= spaceLeft)
|
||||
writeCount = data.Length;
|
||||
else
|
||||
writeCount = spaceLeft;
|
||||
|
||||
if (writeCount > 0)
|
||||
{
|
||||
Internal.MemCpy(mBuffer.Ptr + (mPos - mBufferPos), data.Ptr, writeCount);
|
||||
mPos += writeCount;
|
||||
mWriteDirtyEnd = Math.Max(mWriteDirtyEnd, mPos);
|
||||
mBufferEnd = Math.Max(mBufferEnd, mPos);
|
||||
if (writeCount == data.Length)
|
||||
return writeCount;
|
||||
data.RemoveFromStart(writeCount);
|
||||
}
|
||||
}
|
||||
|
||||
Try!(Flush());
|
||||
|
||||
if ((mBuffer == null) || (data.Length > mBuffer.Count))
|
||||
{
|
||||
var result = TryWriteUnderlying(mPos, data);
|
||||
if (result case .Ok(let len))
|
||||
mPos += len;
|
||||
writeCount += result;
|
||||
return writeCount;
|
||||
}
|
||||
|
||||
mBufferPos = mPos;
|
||||
mWriteDirtyPos = mPos;
|
||||
Internal.MemCpy(mBuffer.Ptr, data.Ptr, data.Length);
|
||||
mPos += data.Length;
|
||||
mBufferEnd = mPos;
|
||||
mWriteDirtyEnd = mPos;
|
||||
writeCount += data.Length;
|
||||
return writeCount;
|
||||
}
|
||||
|
||||
public override Result<void> Flush()
|
||||
{
|
||||
if (mWriteDirtyPos >= 0)
|
||||
{
|
||||
Try!(TryWriteUnderlying(mWriteDirtyPos, .(mBuffer.Ptr + (mWriteDirtyPos - mBufferPos), (.)(mWriteDirtyEnd - mWriteDirtyPos))));
|
||||
mWriteDirtyPos = -Int32.MinValue;
|
||||
mWriteDirtyEnd = -Int32.MinValue;
|
||||
}
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public override Result<void> Close()
|
||||
{
|
||||
return Flush();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -101,9 +101,9 @@ namespace System.IO
|
|||
return .Ok(count);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
public override Result<void> Close()
|
||||
{
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public void RemoveFromStart(int count)
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace System.IO
|
|||
|
||||
public ~this()
|
||||
{
|
||||
Close();
|
||||
Delete();
|
||||
}
|
||||
|
||||
public override Result<void> Seek(int64 pos, SeekKind seekKind = .Absolute)
|
||||
|
@ -52,7 +52,7 @@ namespace System.IO
|
|||
return numBytesRead;
|
||||
}
|
||||
|
||||
public Result<int> TryRead(Span<uint8> data, int timeoutMS)
|
||||
public virtual Result<int> TryRead(Span<uint8> data, int timeoutMS)
|
||||
{
|
||||
Platform.BfpFileResult result = .Ok;
|
||||
int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result);
|
||||
|
@ -70,21 +70,28 @@ namespace System.IO
|
|||
return numBytesWritten;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
public override Result<void> Close()
|
||||
{
|
||||
if (mBfpFile != null)
|
||||
Platform.BfpFile_Release(mBfpFile);
|
||||
mBfpFile = null;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
public override Result<void> Flush()
|
||||
{
|
||||
if (mBfpFile != null)
|
||||
Platform.BfpFile_Flush(mBfpFile);
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
protected virtual void Delete()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
class FileStream : FileStreamBase
|
||||
class UnbufferedFileStream : FileStreamBase
|
||||
{
|
||||
FileAccess mFileAccess;
|
||||
|
||||
|
@ -211,10 +218,228 @@ namespace System.IO
|
|||
mFileAccess = access;
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
public override Result<void> Close()
|
||||
{
|
||||
base.Close();
|
||||
mFileAccess = default;
|
||||
if (base.Close() case .Err)
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
|
||||
class BufferedFileStream : BufferedStream
|
||||
{
|
||||
protected Platform.BfpFile* mBfpFile;
|
||||
protected int64 mBfpFilePos;
|
||||
FileAccess mFileAccess;
|
||||
|
||||
public this()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
Delete();
|
||||
}
|
||||
|
||||
protected virtual void Delete()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
public this(Platform.BfpFile* handle, FileAccess access, int32 bufferSize, bool isAsync)
|
||||
{
|
||||
mBfpFile = handle;
|
||||
mFileAccess = access;
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
{
|
||||
get
|
||||
{
|
||||
return mFileAccess.HasFlag(FileAccess.Read);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get
|
||||
{
|
||||
return mFileAccess.HasFlag(FileAccess.Write);
|
||||
}
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Create(StringView path, FileAccess access = .ReadWrite, FileShare share = .None, int bufferSize = 4096, FileOptions options = .None, SecurityAttributes* secAttrs = null)
|
||||
{
|
||||
return Open(path, FileMode.Create, access, share, bufferSize, options, secAttrs);
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Open(StringView path, FileAccess access = .ReadWrite, FileShare share = .None, int bufferSize = 4096, FileOptions options = .None, SecurityAttributes* secAttrs = null)
|
||||
{
|
||||
return Open(path, FileMode.Open, access, share, bufferSize, options, secAttrs);
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> OpenStd(Platform.BfpFileStdKind stdKind)
|
||||
{
|
||||
Platform.BfpFileResult fileResult = .Ok;
|
||||
mBfpFile = Platform.BfpFile_GetStd(stdKind, &fileResult);
|
||||
mFileAccess = .ReadWrite;
|
||||
|
||||
if ((mBfpFile == null) || (fileResult != .Ok))
|
||||
{
|
||||
switch (fileResult)
|
||||
{
|
||||
case .ShareError:
|
||||
return .Err(.SharingViolation);
|
||||
case .NotFound:
|
||||
return .Err(.NotFound);
|
||||
default:
|
||||
return .Err(.Unknown);
|
||||
}
|
||||
}
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public Result<void, FileOpenError> Open(StringView path, FileMode mode, FileAccess access, FileShare share = .None, int bufferSize = 4096, FileOptions options = .None, SecurityAttributes* secAttrs = null)
|
||||
{
|
||||
Runtime.Assert(mBfpFile == null);
|
||||
|
||||
Platform.BfpFileCreateKind createKind = .CreateAlways;
|
||||
Platform.BfpFileCreateFlags createFlags = .None;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case .CreateNew:
|
||||
createKind = .CreateIfNotExists;
|
||||
case .Create:
|
||||
createKind = .CreateAlways;
|
||||
case .Open:
|
||||
createKind = .OpenExisting;
|
||||
case .OpenOrCreate:
|
||||
createKind = .CreateAlways;
|
||||
case .Truncate:
|
||||
createKind = .CreateAlways;
|
||||
createFlags |= .Truncate;
|
||||
case .Append:
|
||||
createKind = .CreateAlways;
|
||||
createFlags |= .Append;
|
||||
}
|
||||
|
||||
if (access.HasFlag(.Read))
|
||||
createFlags |= .Read;
|
||||
if (access.HasFlag(.Write))
|
||||
createFlags |= .Write;
|
||||
|
||||
if (share.HasFlag(.Read))
|
||||
createFlags |= .ShareRead;
|
||||
if (share.HasFlag(.Write))
|
||||
createFlags |= .ShareWrite;
|
||||
if (share.HasFlag(.Delete))
|
||||
createFlags |= .ShareDelete;
|
||||
|
||||
Platform.BfpFileAttributes fileFlags = .Normal;
|
||||
|
||||
Platform.BfpFileResult fileResult = .Ok;
|
||||
mBfpFile = Platform.BfpFile_Create(path.ToScopeCStr!(128), createKind, createFlags, fileFlags, &fileResult);
|
||||
|
||||
if ((mBfpFile == null) || (fileResult != .Ok))
|
||||
{
|
||||
switch (fileResult)
|
||||
{
|
||||
case .ShareError:
|
||||
return .Err(.SharingViolation);
|
||||
case .NotFound:
|
||||
return .Err(.NotFound);
|
||||
default:
|
||||
return .Err(.Unknown);
|
||||
}
|
||||
}
|
||||
mFileAccess = access;
|
||||
|
||||
MakeBuffer(bufferSize);
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public void Attach(Platform.BfpFile* bfpFile, FileAccess access = .ReadWrite)
|
||||
{
|
||||
Close();
|
||||
mBfpFile = bfpFile;
|
||||
mFileAccess = access;
|
||||
}
|
||||
|
||||
public override Result<void> Close()
|
||||
{
|
||||
var hadError = Flush() case .Err;
|
||||
if (mBfpFile != null)
|
||||
Platform.BfpFile_Release(mBfpFile);
|
||||
mBfpFile = null;
|
||||
mFileAccess = default;
|
||||
if (hadError)
|
||||
return .Err;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
protected override void UpdateLength()
|
||||
{
|
||||
mUnderlyingLength = Platform.BfpFile_GetFileSize(mBfpFile);
|
||||
}
|
||||
|
||||
protected override Result<int> TryReadUnderlying(int64 pos, Span<uint8> data)
|
||||
{
|
||||
if (mBfpFilePos != pos)
|
||||
{
|
||||
int64 newPos = Platform.BfpFile_Seek(mBfpFile, pos, .Absolute);
|
||||
if (newPos != pos)
|
||||
return .Err;
|
||||
mBfpFilePos = pos;
|
||||
}
|
||||
Platform.BfpFileResult result = .Ok;
|
||||
int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, -1, &result);
|
||||
if ((result != .Ok) && (result != .PartialData))
|
||||
return .Err;
|
||||
mBfpFilePos += numBytesRead;
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
protected override Result<int> TryWriteUnderlying(int64 pos, Span<uint8> data)
|
||||
{
|
||||
if (mBfpFilePos != pos)
|
||||
{
|
||||
int64 newPos = Platform.BfpFile_Seek(mBfpFile, pos, .Absolute);
|
||||
if (newPos != pos)
|
||||
return .Err;
|
||||
mBfpFilePos = pos;
|
||||
}
|
||||
Platform.BfpFileResult result = .Ok;
|
||||
int numBytesRead = Platform.BfpFile_Write(mBfpFile, data.Ptr, data.Length, -1, &result);
|
||||
if ((result != .Ok) && (result != .PartialData))
|
||||
return .Err;
|
||||
mBfpFilePos += numBytesRead;
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
public Result<int> TryRead(Span<uint8> data, int timeoutMS)
|
||||
{
|
||||
if (mBfpFilePos != mPos)
|
||||
{
|
||||
int64 newPos = Platform.BfpFile_Seek(mBfpFile, mPos, .Absolute);
|
||||
if (newPos != mPos)
|
||||
return .Err;
|
||||
mBfpFilePos = mPos;
|
||||
}
|
||||
|
||||
Platform.BfpFileResult result = .Ok;
|
||||
int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result);
|
||||
if ((result != .Ok) && (result != .PartialData))
|
||||
return .Err;
|
||||
return numBytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
class FileStream : BufferedFileStream
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,9 +71,9 @@ namespace System.IO
|
|||
return .Ok(count);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
public override Result<void> Close()
|
||||
{
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,9 +56,9 @@ namespace System.IO
|
|||
return .Ok(data.Length);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
public override Result<void> Close()
|
||||
{
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace System.IO
|
|||
|
||||
public abstract Result<int> TryRead(Span<uint8> data);
|
||||
public abstract Result<int> TryWrite(Span<uint8> data);
|
||||
public abstract void Close();
|
||||
public abstract Result<void> Close();
|
||||
|
||||
//Read value from stream without changing position. Position won't change even if it returns .Err
|
||||
public Result<T> Peek<T>() where T : struct
|
||||
|
@ -134,10 +134,16 @@ namespace System.IO
|
|||
public Result<T> Read<T>() where T : struct
|
||||
{
|
||||
T val = ?;
|
||||
int size = Try!(TryRead(.((uint8*)&val, sizeof(T))));
|
||||
if (size != sizeof(T))
|
||||
var result = TryRead(.((uint8*)&val, sizeof(T)));
|
||||
switch (result)
|
||||
{
|
||||
case .Ok(let size):
|
||||
if (size != sizeof(T))
|
||||
return .Err;
|
||||
return .Ok(val);
|
||||
case .Err:
|
||||
return .Err;
|
||||
return .Ok(val);
|
||||
}
|
||||
}
|
||||
|
||||
public Result<void> Write<T>(T val) where T : struct
|
||||
|
@ -186,8 +192,9 @@ namespace System.IO
|
|||
return .Ok;
|
||||
}
|
||||
|
||||
public virtual void Flush()
|
||||
public virtual Result<void> Flush()
|
||||
{
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
public void Align(int alignSize)
|
||||
|
|
|
@ -157,9 +157,9 @@ namespace System.IO
|
|||
return .Ok(count);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
public override Result<void> Close()
|
||||
{
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,14 +90,14 @@ namespace System.IO
|
|||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
public override Result<void> Close()
|
||||
{
|
||||
mChildStream.Close();
|
||||
return mChildStream.Close();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
public override Result<void> Flush()
|
||||
{
|
||||
mChildStream.Flush();
|
||||
return mChildStream.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,9 +54,9 @@ namespace System
|
|||
return .Ok(data.Length);
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
public override Result<void> Close()
|
||||
{
|
||||
|
||||
return .Ok;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1451,6 +1451,9 @@ namespace System
|
|||
[CLink, CallingConvention(.Stdcall)]
|
||||
public static extern Handle OpenFileMappingA(uint32 dwDesiredAccess, IntBool bInheritHandle, char8* lpName);
|
||||
|
||||
[CLink, CallingConvention(.Stdcall)]
|
||||
public static extern Handle CreateFileMappingA(Handle hFile, SecurityAttributes* securityAttrs, uint32 flProtect, uint32 dwMaximumSizeHigh, uint32 dwMaximumSizeLow, char8* lpName);
|
||||
|
||||
[CLink, CallingConvention(.Stdcall)]
|
||||
public static extern void* MapViewOfFile(Handle hFileMappingObject, uint32 dwDesiredAccess, uint32 dwFileOffsetHigh, uint32 dwFileOffsetLow, int dwNumberOfBytesToMap);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue