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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-04-11 07:04:17 -04:00
|
|
|
public virtual Result<int> 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))
|
|
|
|
return .Err;
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 07:04:17 -04:00
|
|
|
class UnbufferedFileStream : FileStreamBase
|
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;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2020-02-29 07:47:44 -08:00
|
|
|
mFileAccess = access;
|
2019-08-23 11:56:54 -07:00
|
|
|
|
|
|
|
return .Ok;
|
|
|
|
}
|
|
|
|
|
2020-02-29 07:47:44 -08:00
|
|
|
public 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;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
2021-04-11 07:04:17 -04:00
|
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
|
2019-08-23 11:56:54 -07:00
|
|
|
}
|
|
|
|
}
|