/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: FileStream.NT.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "FS.hpp" #include "FileStream.NT.hpp" #include "FileAdvisory.NT.hpp" #if !defined(_AURUNTIME_GENERICFILESTREAM) namespace Aurora::IO::FS { static const AuUInt64 kFileCopyBlock = 0xFFFF; // 64KiB, 1k iterations to max out 64MB/s disk, 2k iteration to make out 128MB/s disk, is this number still way too low? cpu go brr WinFileStream::~WinFileStream() { Close(); } void WinFileStream::Init(HANDLE handle, const AuString &path) { this->handle_ = handle; this->path_ = path; } AuUInt64 WinFileStream::GetOffset() { LARGE_INTEGER distance {}; LARGE_INTEGER pos {}; if (this->handle_ == INVALID_HANDLE_VALUE) { SysPushErrorUninitialized(); return 0; } if (SetFilePointerEx(this->handle_, distance, &pos, FILE_CURRENT) == INVALID_SET_FILE_POINTER) { AuLogWarn("SetFilePointerEx IO Error: 0x{:x}, {}", GetLastError(), path_); SysPushErrorIO(); return 0; } return pos.QuadPart; } bool WinFileStream::SetOffset(AuUInt64 offset) { LARGE_INTEGER distance {}; LARGE_INTEGER pos {}; if (this->handle_ == INVALID_HANDLE_VALUE) { SysPushErrorUninitialized(); return false; } distance.QuadPart = offset; if (SetFilePointerEx(this->handle_, distance, &pos, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { AuLogWarn("SetFilePointerEx IO Error: 0x{:x}, {}", GetLastError(), path_); SysPushErrorIO(); return false; } return true; } AuUInt64 WinFileStream::GetLength() { LARGE_INTEGER length; if (handle_ == INVALID_HANDLE_VALUE) { SysPushErrorUninitialized(); return 0; } if (!GetFileSizeEx(this->handle_, &length)) { SysPushErrorIO(); return 0; } return length.QuadPart; } bool WinFileStream::Read(const Memory::MemoryViewStreamWrite ¶meters) { if (this->handle_ == INVALID_HANDLE_VALUE) { SysPushErrorUninitialized(); return {}; } auto length = parameters.length; parameters.outVariable = 0; AuUInt64 offset {0}; while (length) { DWORD read; int blockSize = AuMin(AuUInt(kFileCopyBlock), length); if (!::ReadFile(this->handle_, reinterpret_cast(parameters.ptr) + offset, blockSize, &read, NULL)) { AuLogWarn("ReadFile IO Error: 0x{:x}, {}", GetLastError(), path_); SysPushErrorIO(); return false; } if (read == 0) { break; } offset += read; length -= read; } if (!offset) { return false; } parameters.outVariable = offset; return true; } bool WinFileStream::Write(const Memory::MemoryViewStreamRead ¶meters) { if (this->handle_ == INVALID_HANDLE_VALUE) { SysPushErrorUninitialized(); return 0; } auto length = parameters.length; parameters.outVariable = 0; AuUInt offset {0}; while (length) { DWORD written; int blockSize = AuMin(AuUInt(kFileCopyBlock), length); if (!::WriteFile(this->handle_, reinterpret_cast(parameters.ptr) + offset, blockSize, &written, NULL)) { SysPushErrorIO("WriteFileEx IO Error: 0x{:x}, {}", GetLastError(), this->path_); return false; } if (!written) { SysPushErrorIO(); parameters.outVariable = offset; return true; } offset += written; length -= written; } if (!offset) { return false; } parameters.outVariable = offset; return true; } void WinFileStream::WriteEoS() { if (this->handle_ == INVALID_HANDLE_VALUE) { SysPushErrorUninitialized(); return; } SetEndOfFile(this->handle_); } void WinFileStream::Close() { if ((this->handle_ != INVALID_HANDLE_VALUE) && (this->bShouldDelete)) { FILE_DISPOSITION_INFO rm {}; rm.DeleteFile = true; if (!SetFileInformationByHandle(this->handle_, _FILE_INFO_BY_HANDLE_CLASS::FileDispositionInfo, &rm, sizeof(rm))) { SysPushErrorIO("Couldn't delete temporary file {}", this->path_); } } AuWin32CloseHandle(this->handle_); } void WinFileStream::Flush() { FlushFileBuffers(this->handle_); } void WinFileStream::MakeTemporary() { this->bShouldDelete = true; } HANDLE WinFileStream::GetHandle() { return this->handle_; } static IFileStream *OpenNewEx(const AuString &path, EFileOpenMode openMode, EFileAdvisoryLockLevel lock, bool bCheck) { try { auto pathex = NormalizePathRet(path); if (pathex.empty()) { return nullptr; } auto win32Path = Locale::ConvertFromUTF8(pathex); if (win32Path.empty()) { return nullptr; } HANDLE fileHandle; fileHandle = INVALID_HANDLE_VALUE; auto dwShare = NtLockAdvisoryToShare(lock); switch (openMode) { case EFileOpenMode::eRead: { fileHandle = CreateFileW(win32Path.c_str(), GENERIC_READ, dwShare, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); break; } case EFileOpenMode::eReadWrite: { CreateDirectories(pathex, true); if (bCheck) { fileHandle = ::CreateFileW(win32Path.c_str(), GENERIC_WRITE | GENERIC_READ, NULL, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); } else { fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE | GENERIC_READ, dwShare, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, dwShare, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); } } break; } case EFileOpenMode::eWrite: { CreateDirectories(pathex, true); if (bCheck) { fileHandle = ::CreateFileW(win32Path.c_str(), GENERIC_WRITE, NULL, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); } else { fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE | FILE_READ_ATTRIBUTES, dwShare, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (fileHandle == INVALID_HANDLE_VALUE) { fileHandle = CreateFileW(win32Path.c_str(), GENERIC_WRITE, dwShare, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); } } break; } } if (fileHandle == INVALID_HANDLE_VALUE) { SysPushErrorIO("Invalid filepath, couldn't open: {}", path); return nullptr; } auto stream = _new WinFileStream(); if (!stream) { CloseHandle(fileHandle); return nullptr; } stream->Init(fileHandle, pathex); return stream; } catch (...) { return nullptr; } } AUKN_SYM IFileStream *CreateNew(const AuString &path) { return OpenNewEx(path, EFileOpenMode::eWrite, EFileAdvisoryLockLevel::eBlockReadWrite, true); } AUKN_SYM void CreateRelease(IFileStream *that) { AuSafeDelete(that); } AUKN_SYM IFileStream *OpenNew(const AuString &path, EFileOpenMode openMode, EFileAdvisoryLockLevel lock) { return OpenNewEx(path, openMode, lock, false); } AUKN_SYM void OpenRelease(IFileStream *that) { AuSafeDelete(that); } AUKN_SYM IFileStream *OpenReadNew(const AuString &path, EFileAdvisoryLockLevel level) { return OpenNew(path, EFileOpenMode::eRead, level); } AUKN_SYM void OpenReadRelease(IFileStream * that) { AuSafeDelete(that); } AUKN_SYM IFileStream *OpenWriteNew(const AuString &path, EFileAdvisoryLockLevel level) { return OpenNew(path, EFileOpenMode::eWrite, level); } AUKN_SYM void OpenWriteRelease(IFileStream *that) { AuSafeDelete(that); } } #endif