AuroraRuntime/Source/IO/FS/FileStream.NT.cpp
Jamie Reece Wilson 83f34b0c47 [*] I was right. String views are [mostly] pointless (*)
03:28:55:638  17>2 of 53388 functions (<0.1%) were compiled, the rest were copied from previous compilation.
03:28:55:638  17>  0 functions were new in current compilation
03:28:55:638  17>  65 functions had inline decision re-evaluated but remain unchanged
03:28:56:749  17>Finished generating code

the header of const AuString & is the same as std::string_view therefore nothing changes. in fact, we still need to alloc strings a bunch of times for a zero terminated string. worse, <c++20 always allocs each time we want to access a hashmap with o(1) lookup, making small hashmaps kinda pointless when we always have to alloc+copy (thx std)

perhaps this will help some language binders
2024-04-19 05:58:08 +01:00

653 lines
17 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: FileStream.NT.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "FS.hpp"
#include "FileStream.NT.hpp"
#include "FileAdvisory.NT.hpp"
#include <Source/IO/AuIOHandle.hpp>
#if !defined(_AURUNTIME_GENERICFILESTREAM)
namespace Aurora::IO::FS
{
static const AuUInt64 kFileCopyBlock = 0xFFFF * 128;
WinFileStream::WinFileStream() :
reader_(AuUnsafeRaiiToShared(this)),
writer_(AuUnsafeRaiiToShared(this)),
sreader_(AuUnsafeRaiiToShared(this)),
swriter_(AuUnsafeRaiiToShared(this))
{
}
WinFileStream::~WinFileStream()
{
Close();
}
void WinFileStream::Init(AuSPtr<IIOHandle> pHandle)
{
this->pHandle_ = pHandle;
}
AuUInt64 WinFileStream::GetOffset()
{
AU_LOCK_GUARD(this->mutex_);
LARGE_INTEGER distance {};
LARGE_INTEGER pos {};
HANDLE hHandle {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
}
hHandle = this->GetWin32Handle();
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{
SysPushErrorUninitialized();
return 0;
}
if (::SetFilePointerEx(hHandle,
distance,
&pos,
FILE_CURRENT) == INVALID_SET_FILE_POINTER)
{
SysPushErrorIO("SetFilePointerEx IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
return 0;
}
return pos.QuadPart;
}
bool WinFileStream::SetOffset(AuUInt64 offset)
{
AU_LOCK_GUARD(this->mutex_);
LARGE_INTEGER distance {};
LARGE_INTEGER pos {};
HANDLE hHandle {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
}
hHandle = this->GetWin32Handle();
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{
SysPushErrorUninitialized();
return 0;
}
distance.QuadPart = offset;
if (::SetFilePointerEx(hHandle, distance, &pos, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
{
SysPushErrorIO("SetFilePointerEx IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
return false;
}
return true;
}
AuUInt64 WinFileStream::GetLength()
{
AU_LOCK_GUARD(this->mutex_);
LARGE_INTEGER length;
HANDLE hHandle {};
if (auto pHandle = this->pHandle_)
{
if (!pHandle->IsFile())
{
SysPushErrorIOResourceRejected();
return 0;
}
hHandle = this->GetWin32Handle();
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorInvalidFd();
return 0;
}
}
else
{
SysPushErrorUninitialized();
return 0;
}
if (!::GetFileSizeEx(hHandle, &length))
{
SysPushErrorIO();
return 0;
}
return length.QuadPart;
}
bool WinFileStream::Read(const Memory::MemoryViewStreamWrite &parameters)
{
AU_LOCK_GUARD(this->mutex_);
OVERLAPPED overlapped {};
AuUInt64 uOffset {};
parameters.outVariable = 0;
if (!this->pHandle_)
{
SysPushErrorUninitialized();
return 0;
}
auto hHandle = this->GetWin32Handle(true);
HANDLE hEventHandle { INVALID_HANDLE_VALUE };
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorIOResourceRejected();
return {};
}
AuUInt64 uLength = parameters.length;
if (this->pHandle_->IsPipe())
{
DWORD uAvail {};
if (!PeekNamedPipe(hHandle, NULL, NULL, NULL, &uAvail, NULL))
{
return false;
}
uLength = AuMin(uLength, uAvail);
if (!uLength)
{
return true;
}
}
bool bIsAsync = this->pHandle_->IsAsync();
if (bIsAsync)
{
hEventHandle = ::CreateEventA(nullptr, false, false, nullptr);
if (!hEventHandle)
{
SysPushErrorGeneric();
return false;
}
}
while (uLength)
{
DWORD read {};
auto blockSize = AuMin<AuUInt64>(AuUInt(kFileCopyBlock), uLength);
if (this->pHandle_->IsAsync())
{
overlapped.hEvent = hEventHandle;
if (!::ReadFile(hHandle,
reinterpret_cast<char *>(parameters.ptr) + uOffset,
AuUInt32(blockSize),
NULL,
&overlapped))
{
if (::GetLastError() == ERROR_IO_PENDING)
{
::WaitForSingleObject(hEventHandle, 0);
}
else
{
SysPushErrorIO("ReadFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
if (!::GetOverlappedResult(hHandle, &overlapped, &read, true))
{
if (::GetLastError() != ERROR_HANDLE_EOF)
{
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
}
else
{
if (!::ReadFile(hHandle,
reinterpret_cast<char *>(parameters.ptr) + uOffset,
blockSize,
&read,
NULL))
{
SysPushErrorIO("ReadFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
if (read == 0)
{
break;
}
uOffset += read;
uLength -= read;
}
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return true;
}
bool WinFileStream::Write(const Memory::MemoryViewStreamRead &parameters)
{
AU_LOCK_GUARD(this->mutex_);
OVERLAPPED overlapped {};
AuUInt64 uOffset {};
parameters.outVariable = 0;
auto optHandle = this->GetHandle()->GetOSWriteHandleSafe();
if (!optHandle)
{
SysPushErrorIOResourceRejected();
return false;
}
auto hHandle = (HANDLE)optHandle.value();
HANDLE hEventHandle { INVALID_HANDLE_VALUE };
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorUninitialized();
return 0;
}
bool bIsAsync = this->pHandle_->IsAsync();
if (bIsAsync)
{
hEventHandle = ::CreateEventA(nullptr, false, false, nullptr);
if (!hEventHandle)
{
SysPushErrorGeneric();
return false;
}
}
AuUInt64 uLength = parameters.length;
while (uLength)
{
DWORD written {};
auto uBlockSize = AuMin<AuUInt64>(AuUInt(kFileCopyBlock), uLength);
if (bIsAsync)
{
overlapped.hEvent = hEventHandle;
if (!::WriteFile(hHandle,
reinterpret_cast<const char *>(parameters.ptr) + uOffset,
AuUInt32(uBlockSize),
NULL,
&overlapped))
{
if (GetLastError() == ERROR_IO_PENDING)
{
::WaitForSingleObject(hEventHandle, 0);
}
else
{
SysPushErrorIO("WriteFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
if (!::GetOverlappedResult(hHandle, &overlapped, &written, true))
{
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return false;
}
}
else
{
if (!::WriteFile(hHandle, reinterpret_cast<const char *>(parameters.ptr) + uOffset, uBlockSize, &written, NULL))
{
SysPushErrorIO("WriteFile IO Error: 0x{:x}, {}", GetLastError(), this->pHandle_ ? this->pHandle_->GetPath() : "");
parameters.outVariable = uOffset;
return false;
}
}
if (!written)
{
break;
}
uOffset += written;
uLength -= written;
}
AuWin32CloseHandle(hEventHandle);
parameters.outVariable = uOffset;
return true;
}
void WinFileStream::WriteEoS()
{
AU_LOCK_GUARD(this->mutex_);
auto hHandle = this->GetWin32Handle();
if (hHandle == INVALID_HANDLE_VALUE)
{
SysPushErrorUninitialized();
return;
}
NTWriteEoS(hHandle);
}
void WinFileStream::Close()
{
AU_LOCK_GUARD(this->mutex_);
auto hHandle = this->GetWin32Handle();
bool bDeleteFailed {};
AuString path;
if ((hHandle != INVALID_HANDLE_VALUE) &&
(this->bShouldDelete))
{
FILE_DISPOSITION_INFO rm {};
rm.DeleteFile = true;
if (!(pSetFileInformationByHandle &&
pSetFileInformationByHandle(hHandle, _FILE_INFO_BY_HANDLE_CLASS::FileDispositionInfo, &rm, sizeof(rm))))
{
path = this->pHandle_ ? this->pHandle_->GetPath() : "";
SysPushErrorIO("Couldn't delete temporary file {}", path);
if (path.length())
{
bDeleteFailed = true;
}
}
else
{
bDeleteFailed = true;
}
}
AuResetMember(this->pHandle_);
// a bit of a hack for problematic NT-like platforms and Windows XP
if (bDeleteFailed)
{
Aurora::RuntimeWaitForSecondaryTick();
AuFS::Remove(path);
}
}
void WinFileStream::Flush()
{
auto hHandle = this->GetWin32Handle();
::FlushFileBuffers(hHandle);
}
void WinFileStream::MakeTemporary()
{
this->bShouldDelete = true;
}
bool WinFileStream::IsFlushOnClose() const
{
AU_LOCK_GUARD(this->mutex_);
if (auto &pHandle = this->pHandle_)
{
return pHandle->IsFlushOnClose();
}
else
{
return false;
}
}
void WinFileStream::SetFlushOnClose(bool bFlushOnClose)
{
AU_LOCK_GUARD(this->mutex_);
if (auto &pHandle = this->pHandle_)
{
pHandle->SetFlushOnClose(bFlushOnClose);
}
}
bool WinFileStream::IsWriteEoSOnClose() const
{
AU_LOCK_GUARD(this->mutex_);
if (auto &pHandle = this->pHandle_)
{
return pHandle->IsWriteEoSOnClose();
}
else
{
return false;
}
}
void WinFileStream::SetWriteEoSOnClose(bool bShouldWriteEoS)
{
AU_LOCK_GUARD(this->mutex_);
if (auto &pHandle = this->pHandle_)
{
pHandle->SetWriteEoSOnClose(bShouldWriteEoS);
}
}
AuSPtr<IIOHandle> WinFileStream::GetHandle()
{
return this->pHandle_;
}
IO::IStreamReader *WinFileStream::ToStreamReader()
{
return &this->reader_;
}
IO::IStreamWriter *WinFileStream::ToStreamWriter()
{
return &this->writer_;
}
IO::ISeekingReader *WinFileStream::ToStreamSeekingReader()
{
return &this->sreader_;
}
IO::ISeekingWriter *WinFileStream::ToStreamSeekingWriter()
{
return &this->swriter_;
}
HANDLE WinFileStream::GetWin32Handle(bool bReadOnly)
{
if (auto pHandle = this->pHandle_)
{
if (bReadOnly)
{
return (HANDLE)pHandle->GetOSReadHandleSafe().ValueOr((AuUInt)INVALID_HANDLE_VALUE);
}
else
{
return (HANDLE)pHandle->GetOSWriteHandleSafe().ValueOr(pHandle->GetOSReadHandleSafe().ValueOr((AuUInt)INVALID_HANDLE_VALUE));
}
}
return INVALID_HANDLE_VALUE;
}
AUKN_SYM IFileStream *OpenBlockingFileStreamFromHandleNew(const AuSPtr<IIOHandle> &pIOHandle)
{
SysCheckArgNotNull(pIOHandle, {});
auto pStream = _new WinFileStream();
SysCheckNotNullMemory(pStream, {});
if (!AuStaticCast<AFileHandle>(pIOHandle)->pIPCPipe &&
AuStaticCast<AFileHandle>(pIOHandle)->ShouldClone())
{
auto pIOHandle2 = AuMakeShared<AFileHandle>();
SysCheckNotNullMemory(pIOHandle2, {});
if (!pIOHandle2->InitFromPair(pIOHandle->GetOSReadHandleSafe(), pIOHandle->GetOSWriteHandle()))
{
SysPushErrorIOResourceFailure();
return {};
}
pStream->Init(pIOHandle2);
}
else
{
pStream->Init(pIOHandle);
}
return pStream;
}
AUKN_SYM void OpenBlockingFileStreamFromHandleRelease(IFileStream *that)
{
AuSafeDelete<WinFileStream *>(that);
}
static IFileStream *OpenNewEx(const AuROString &path,
EFileOpenMode openMode,
EFileAdvisoryLockLevel lock,
bool bCheck)
{
try
{
auto pHandle = AuIO::IOHandleShared();
if (!pHandle)
{
SysPushErrorMemory();
return nullptr;
}
AuIO::IIOHandle::HandleCreate createhandle(path);
createhandle.eAdvisoryLevel = lock;
createhandle.eMode = openMode;
createhandle.bFailIfNonEmptyFile = bCheck;
createhandle.bDirectIOMode = false;
createhandle.bAsyncHandle = false;
if (!pHandle->InitFromPath(createhandle))
{
SysPushErrorNested();
return nullptr;
}
auto pStream = _new WinFileStream();
if (!pStream)
{
SysPushErrorMemory();
return nullptr;
}
pStream->Init(pHandle);
return pStream;
}
catch (...)
{
return nullptr;
}
}
AUKN_SYM IFileStream *CreateNew(const AuROString &path)
{
return OpenNewEx(path, EFileOpenMode::eWrite, EFileAdvisoryLockLevel::eBlockReadWrite, true);
}
AUKN_SYM void CreateRelease(IFileStream *that)
{
AuSafeDelete<WinFileStream *>(that);
}
AUKN_SYM IFileStream *OpenNew(const AuROString &path, EFileOpenMode openMode, EFileAdvisoryLockLevel lock)
{
return OpenNewEx(path, openMode, lock, false);
}
AUKN_SYM void OpenRelease(IFileStream *that)
{
AuSafeDelete<WinFileStream *>(that);
}
AUKN_SYM IFileStream *OpenReadNew(const AuROString &path, EFileAdvisoryLockLevel level)
{
return OpenNew(path, EFileOpenMode::eRead, level);
}
AUKN_SYM void OpenReadRelease(IFileStream * that)
{
AuSafeDelete<WinFileStream *>(that);
}
AUKN_SYM IFileStream *OpenWriteNew(const AuROString &path, EFileAdvisoryLockLevel level)
{
return OpenNew(path, EFileOpenMode::eWrite, level);
}
AUKN_SYM void OpenWriteRelease(IFileStream *that)
{
AuSafeDelete<WinFileStream *>(that);
}
}
#endif