1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-15 14:54:09 +02:00
Beef/BeefLibs/corlib/src/IO/FileStream.bf

596 lines
13 KiB
Beef
Raw Normal View History

2019-08-23 11:56:54 -07:00
using System.Threading;
using System.Diagnostics.Contracts;
using System.Diagnostics;
namespace System.IO
{
abstract class FileStreamBase : Stream
{
protected Platform.BfpFile* mBfpFile;
public override int64 Position
{
get
{
return Platform.BfpFile_Seek(mBfpFile, 0, .Relative);
}
set
{
Platform.BfpFile_Seek(mBfpFile, value, .Absolute);
}
}
public override int64 Length
{
get
{
return Platform.BfpFile_GetFileSize(mBfpFile);
}
}
2021-07-03 05:29:50 -07:00
public int Handle
{
get
{
if (mBfpFile == null)
return 0;
return Platform.BfpFile_GetSystemHandle(mBfpFile);
}
}
2019-08-23 11:56:54 -07:00
public ~this()
{
2021-04-11 07:04:17 -04:00
Delete();
2019-08-23 11:56:54 -07:00
}
2019-09-10 11:25:53 -07:00
public override Result<void> Seek(int64 pos, SeekKind seekKind = .Absolute)
2019-08-23 11:56:54 -07:00
{
int64 newPos = Platform.BfpFile_Seek(mBfpFile, pos, (Platform.BfpFileSeekKind)seekKind);
// Ensure position is what was requested
if ((seekKind == .Absolute) && (newPos != pos))
return .Err;
return .Ok;
}
public override Result<int> TryRead(Span<uint8> data)
{
Platform.BfpFileResult result = .Ok;
int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, -1, &result);
if ((result != .Ok) && (result != .PartialData))
return .Err;
return numBytesRead;
}
public override Result<int, FileError> TryRead(Span<uint8> data, int timeoutMS)
2019-08-23 11:56:54 -07:00
{
Platform.BfpFileResult result = .Ok;
int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result);
if ((result != .Ok) && (result != .PartialData))
2021-12-31 10:26:32 -05:00
{
switch (result)
{
case .Timeout:
return .Err(.ReadError(.Timeout));
default:
return .Err(.ReadError(.Unknown));
}
}
2019-08-23 11:56:54 -07:00
return numBytesRead;
}
public override Result<int> TryWrite(Span<uint8> data)
{
Platform.BfpFileResult result = .Ok;
int numBytesWritten = Platform.BfpFile_Write(mBfpFile, data.Ptr, data.Length, -1, &result);
if ((result != .Ok) && (result != .PartialData))
return .Err;
return numBytesWritten;
}
2021-04-11 07:04:17 -04:00
public override Result<void> Close()
2019-08-23 11:56:54 -07:00
{
if (mBfpFile != null)
Platform.BfpFile_Release(mBfpFile);
mBfpFile = null;
2021-04-11 07:04:17 -04:00
return .Ok;
2019-08-23 11:56:54 -07:00
}
2021-04-11 07:04:17 -04:00
public override Result<void> Flush()
2019-08-23 11:56:54 -07:00
{
if (mBfpFile != null)
Platform.BfpFile_Flush(mBfpFile);
2021-04-11 07:04:17 -04:00
return .Ok;
}
protected virtual void Delete()
{
Close();
2019-08-23 11:56:54 -07:00
}
}
2023-01-13 07:00:11 -05:00
interface IFileStream
{
Result<void> Attach(Platform.BfpFile* bfpFile, FileAccess access = .ReadWrite);
}
class UnbufferedFileStream : FileStreamBase, IFileStream
2019-08-23 11:56:54 -07:00
{
FileAccess mFileAccess;
public this()
{
}
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);
}
}
2020-02-29 07:47:44 -08:00
public Result<void, FileOpenError> Create(StringView path, FileAccess access = .ReadWrite, FileShare share = .None, int bufferSize = 4096, FileOptions options = .None, SecurityAttributes* secAttrs = null)
2019-08-23 11:56:54 -07:00
{
return Open(path, FileMode.Create, access, share, bufferSize, options, secAttrs);
}
2020-02-29 07:47:44 -08:00
public Result<void, FileOpenError> Open(StringView path, FileAccess access = .ReadWrite, FileShare share = .None, int bufferSize = 4096, FileOptions options = .None, SecurityAttributes* secAttrs = null)
2019-08-23 11:56:54 -07:00
{
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);
2020-02-29 07:47:44 -08:00
mFileAccess = .ReadWrite;
2019-08-23 11:56:54 -07:00
if ((mBfpFile == null) || (fileResult != .Ok))
{
switch (fileResult)
{
case .ShareError:
return .Err(.SharingViolation);
case .NotFound:
return .Err(.NotFound);
default:
return .Err(.Unknown);
}
}
return .Ok;
}
2020-02-29 07:47:44 -08:00
public Result<void, FileOpenError> Open(StringView path, FileMode mode, FileAccess access, FileShare share = .None, int bufferSize = 4096, FileOptions options = .None, SecurityAttributes* secAttrs = null)
2019-08-23 11:56:54 -07:00
{
Runtime.Assert(mBfpFile == null);
Platform.BfpFileCreateKind createKind = .CreateAlways;
Platform.BfpFileCreateFlags createFlags = .None;
switch (mode)
{
case .CreateNew:
createKind = .CreateIfNotExists;
case .Create:
createKind = .CreateAlways;
2021-09-15 11:18:32 -07:00
createFlags |= .Truncate;
2019-08-23 11:56:54 -07:00
case .Open:
createKind = .OpenExisting;
case .OpenOrCreate:
2021-08-01 17:30:40 -03:00
createKind = .OpenAlways;
2019-08-23 11:56:54 -07:00
case .Truncate:
2021-09-15 11:18:32 -07:00
createKind = .OpenExisting;
2019-08-23 11:56:54 -07:00
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);
}
}
2020-02-29 07:47:44 -08:00
mFileAccess = access;
2019-08-23 11:56:54 -07:00
return .Ok;
}
2023-01-13 07:00:11 -05:00
public Result<void> Attach(Platform.BfpFile* bfpFile, FileAccess access = .ReadWrite)
2019-08-23 11:56:54 -07:00
{
Close();
mBfpFile = bfpFile;
2020-02-29 07:47:44 -08:00
mFileAccess = access;
2023-01-13 07:00:11 -05:00
return .Ok;
2020-02-29 07:47:44 -08:00
}
2021-04-11 07:04:17 -04:00
public override Result<void> Close()
2020-02-29 07:47:44 -08:00
{
mFileAccess = default;
2021-04-11 07:04:17 -04:00
if (base.Close() case .Err)
return .Err;
return .Ok;
}
2021-08-01 17:38:30 -03:00
public override Result<void> SetLength(int64 length)
{
int64 pos = Position;
if (pos != length)
Seek(length);
Platform.BfpFileResult result = .Ok;
Platform.BfpFile_Truncate(mBfpFile, &result);
if (result != .Ok)
{
Seek(pos);
return .Err;
}
if (pos != length)
{
if (pos < length)
Seek(pos);
else
Seek(0, .FromEnd);
}
return .Ok;
}
2021-04-11 07:04:17 -04:00
}
2023-01-13 07:00:11 -05:00
class BufferedFileStream : BufferedStream, IFileStream
2021-04-11 07:04:17 -04:00
{
2023-08-18 12:04:01 -07:00
public struct PositionRestorer : this(BufferedFileStream stream, int prevPosition), IDisposable
{
public void Dispose() => stream.Position = prevPosition;
}
2021-04-11 07:04:17 -04:00
protected Platform.BfpFile* mBfpFile;
protected int64 mBfpFilePos;
FileAccess mFileAccess;
2021-07-03 05:29:50 -07:00
public int Handle
{
get
{
if (mBfpFile == null)
return 0;
return Platform.BfpFile_GetSystemHandle(mBfpFile);
}
}
2021-08-01 17:37:37 -03:00
public override bool CanRead
{
get
{
return mFileAccess.HasFlag(FileAccess.Read);
}
}
public override bool CanWrite
{
get
{
return mFileAccess.HasFlag(FileAccess.Write);
}
}
public override int64 Position
{
set
{
// Matches the behavior of Platform.BfpFile_Seek(mBfpFile, value, .Absolute);
mPos = Math.Max(value, 0);
}
}
2021-04-11 07:04:17 -04:00
public this()
{
}
public ~this()
{
Delete();
}
public this(Platform.BfpFile* handle, FileAccess access, int32 bufferSize, bool isAsync)
{
mBfpFile = handle;
mFileAccess = access;
}
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:
2021-08-01 17:30:40 -03:00
createKind = .OpenAlways;
2021-04-11 07:04:17 -04:00
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;
}
2023-01-13 07:00:11 -05:00
public Result<void> Attach(Platform.BfpFile* bfpFile, FileAccess access = .ReadWrite)
2021-04-11 07:04:17 -04:00
{
Close();
mBfpFile = bfpFile;
mFileAccess = access;
2023-01-13 07:00:11 -05:00
return .Ok;
2021-04-11 07:04:17 -04:00
}
2023-08-18 12:04:01 -07:00
public PositionRestorer PushPosition(int position)
{
2023-09-05 06:14:25 -07:00
PositionRestorer restorer = .(this, (.)Position);
2023-08-18 12:04:01 -07:00
Position = position;
return restorer;
}
protected virtual void Delete()
{
Close();
}
public override Result<void> Seek(int64 pos, SeekKind seekKind = .Absolute)
{
int64 newPos;
switch (seekKind)
{
case .Absolute:
newPos = pos;
case .FromEnd:
newPos = Length + pos;
case .Relative:
newPos = mPos + pos;
}
// Matches the behaviour of Platform.BfpFile_Seek(mBfpFile, value, .Absolute);
mPos = Math.Max(newPos, 0);
if (seekKind == .Absolute && newPos < 0)
return .Err;
return .Ok;
}
2021-04-11 07:04:17 -04:00
public override Result<void> Close()
{
2021-04-13 09:30:59 +02:00
let ret = base.Close();
2021-04-11 07:04:17 -04:00
if (mBfpFile != null)
Platform.BfpFile_Release(mBfpFile);
2021-04-13 09:30:59 +02:00
2021-04-11 07:04:17 -04:00
mBfpFile = null;
mFileAccess = default;
2021-04-13 09:30:59 +02:00
mBfpFilePos = 0;
return ret;
2021-04-11 07:04:17 -04:00
}
protected override void UpdateLength()
{
mUnderlyingLength = Platform.BfpFile_GetFileSize(mBfpFile);
}
2021-12-31 10:26:32 -05:00
protected Result<void, FileError> SeekUnderlying(int64 offset, Platform.BfpFileSeekKind seekKind = .Absolute)
2021-08-01 17:37:37 -03:00
{
int64 newPos = Platform.BfpFile_Seek(mBfpFile, offset, seekKind);
2021-12-31 10:26:32 -05:00
Result<void, FileError> result = ((seekKind == .Absolute) && (newPos != offset)) ? .Err(.SeekError) : .Ok;
2021-08-01 17:37:37 -03:00
if (result case .Ok)
mBfpFilePos = newPos;
return result;
}
2021-04-11 07:04:17 -04:00
protected override Result<int> TryReadUnderlying(int64 pos, Span<uint8> data)
{
if (mBfpFilePos != pos)
2021-08-01 17:37:37 -03:00
Try!(SeekUnderlying(pos));
2021-04-11 07:04:17 -04:00
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)
2021-08-01 17:37:37 -03:00
Try!(SeekUnderlying(pos));
2021-04-11 07:04:17 -04:00
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 override Result<int, FileError> TryRead(Span<uint8> data, int timeoutMS)
2021-04-11 07:04:17 -04:00
{
if (mBfpFilePos != mPos)
2021-08-01 17:37:37 -03:00
Try!(SeekUnderlying(mPos));
2021-04-11 07:04:17 -04:00
Platform.BfpFileResult result = .Ok;
int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result);
if ((result != .Ok) && (result != .PartialData))
2021-12-31 10:26:32 -05:00
{
switch (result)
{
case .Timeout:
return .Err(.ReadError(.Timeout));
default:
return .Err(.ReadError(.Unknown));
}
}
2021-04-11 07:04:17 -04:00
return numBytesRead;
}
2021-08-01 17:38:30 -03:00
public override Result<void> SetLength(int64 length)
{
Try!(Flush());
int64 pos = Position;
if (pos != length || pos != mBfpFilePos)
{
Try!(SeekUnderlying(length));
mPos = length;
}
Platform.BfpFileResult result = .Ok;
Platform.BfpFile_Truncate(mBfpFile, &result);
if (result != .Ok)
{
Try!(SeekUnderlying(pos));
return .Err;
}
mUnderlyingLength = length;
mPos = Math.Min(pos, Length);
if (pos != length)
{
if (pos < length)
Try!(SeekUnderlying(pos));
else
Try!(SeekUnderlying(0, .FromEnd));
}
return .Ok;
}
2023-01-13 07:00:11 -05:00
public override Result<void> Flush()
{
var result = base.Flush();
if (mBfpFile != null)
Platform.BfpFile_Flush(mBfpFile);
return result;
}
2021-04-11 07:04:17 -04:00
}
class FileStream : BufferedFileStream
{
2019-08-23 11:56:54 -07:00
}
}