aboutsummaryrefslogtreecommitdiff
path: root/core/src/Utils/IO/FileStream_Custom.inl
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/Utils/IO/FileStream_Custom.inl')
-rw-r--r--core/src/Utils/IO/FileStream_Custom.inl358
1 files changed, 358 insertions, 0 deletions
diff --git a/core/src/Utils/IO/FileStream_Custom.inl b/core/src/Utils/IO/FileStream_Custom.inl
new file mode 100644
index 0000000..004dd01
--- /dev/null
+++ b/core/src/Utils/IO/FileStream_Custom.inl
@@ -0,0 +1,358 @@
+// Note: included by FileStream.cpp conditionally, not compiled separately
+#include "FileStream.hpp"
+
+#include <cstring>
+#include <filesystem>
+#include <iostream>
+
+namespace fs = std::filesystem;
+
+#if defined(_WIN32)
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <Windows.h>
+
+InputFileStream::InputFileStream(const fs::path& path)
+ : mOsFileHandle{ 0 }
+{
+ auto handle = reinterpret_cast<HANDLE*>(mOsFileHandle);
+
+ *handle = CreateFileW(
+ path.c_str(), // fs::path::c_str() returns a wide string on Windows
+ GENERIC_READ,
+ /* No sharing */ 0,
+ /* Use default security*/ nullptr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ /* No attribute template */ nullptr);
+
+ // TODO handle error
+}
+
+InputFileStream::~InputFileStream()
+{
+ auto handle = reinterpret_cast<HANDLE*>(mOsFileHandle);
+ CloseHandle(*handle);
+}
+
+OutputFileStream::OutputFileStream(const fs::path& path, WriteMode mode)
+ : mOsFileHandle{ 0 }
+{
+ auto handle = reinterpret_cast<HANDLE*>(mOsFileHandle);
+
+ DWORD creationDisposition;
+ switch (mode) {
+ case AppendFile: creationDisposition = OPEN_ALWAYS; break;
+ case TruncateFile: creationDisposition = CREATE_ALWAYS; break;
+ }
+
+ *handle = CreateFileW(
+ path.c_str(),
+ GENERIC_WRITE,
+ /* No sharing */ 0,
+ /* Use default security*/ nullptr,
+ creationDisposition,
+ FILE_ATTRIBUTE_NORMAL,
+ /* No attribute template */ nullptr);
+
+ // TODO handle error
+}
+
+OutputFileStream::~OutputFileStream()
+{
+ auto handle = reinterpret_cast<HANDLE*>(mOsFileHandle);
+ CloseHandle(*handle);
+}
+
+static IoResult::ErrorKind MapErrorCodeToIoResult(DWORD error)
+{
+ switch (error) {
+ // TODO
+
+ default:
+ std::cerr << "Unimplemented win32 error code " << error << ", report bug immediately.\n";
+ std::abort();
+ }
+}
+
+static IoResult ReadBytesDirect(HANDLE hFile, size_t byteCount, std::byte* bytes)
+{
+ DWORD bytesRead;
+ BOOL result = ReadFile(hFile, bytes, byteCount, &bytesRead, nullptr);
+
+ if (result) {
+ return IoResult{
+ .Error = IoResult::ERR_None,
+ .SystemError = 0,
+ .BytesMoved = bytesRead,
+ };
+ } else {
+ DWORD errorCode = GetLastError();
+ return IoResult{
+ .Error = ::MapErrorCodeToIoResult(errorCode),
+ .SystemError = errorCode,
+ .BytesMoved = bytesRead,
+ };
+ }
+}
+
+static IoResult WriteBytesDirect(HANDLE hFile, size_t byteCount, const std::byte* bytes)
+{
+ DWORD bytesWritten;
+ BOOL result = WriteFile(hFile, bytes, byteCount, &bytesWritten, nullptr);
+
+ if (result) {
+ return IoResult{
+ .Error = IoResult::ERR_None,
+ .SystemError = 0,
+ .BytesMoved = bytesWritten,
+ };
+ } else {
+ DWORD errorCode = GetLastError();
+ return IoResult{
+ .Error = ::MapErrorCodeToIoResult(errorCode),
+ .SystemError = errorCode,
+ .BytesMoved = bytesWritten,
+ };
+ }
+}
+
+#elif defined(__APPLE__) || defined(__linux__)
+# include <fcntl.h>
+# include <sys/stat.h>
+# include <sys/types.h>
+# include <unistd.h>
+
+InputFileStream::InputFileStream(const fs::path& path)
+ : mOsFileHandle{ 0 }
+{
+ auto fd = reinterpret_cast<int*>(mOsFileHandle);
+ *fd = open(path.c_str(), O_RDONLY);
+}
+
+InputFileStream::~InputFileStream()
+{
+ auto fd = reinterpret_cast<int*>(mOsFileHandle);
+ close(*fd);
+}
+
+OutputFileStream::OutputFileStream(const fs::path& path, WriteMode mode)
+ : mOsFileHandle{ 0 }
+{
+ auto fd = reinterpret_cast<int*>(mOsFileHandle);
+
+ int flags = O_WRONLY | O_CREAT;
+ switch (mode) {
+ case AppendFile: flags |= O_APPEND; break;
+ case TruncateFile: flags |= O_TRUNC; break;
+ }
+
+ *fd = open(path.c_str(), flags, 0644);
+}
+
+OutputFileStream::~OutputFileStream()
+{
+ auto fd = reinterpret_cast<int*>(mOsFileHandle);
+ close(*fd);
+}
+
+static IoResult::ErrorKind MapErrnoToIoResult(int err)
+{
+ switch (err) {
+ // TODO
+ case EFAULT: return IoResult::ERR_UnexpectedEof;
+ case EPERM: return IoResult::ERR_PermissionDenied;
+ case ENOSPC: return IoResult::ERR_OutOfSpace;
+ case EIO: return IoResult::ERR_Other;
+
+ default:
+ std::cerr << "Unimplemented POSIX errno " << err << ", report bug immediately.\n";
+ std::abort();
+ }
+}
+
+static IoResult ReadBytesDirect(const char* osFileHandle, size_t byteCount, std::byte* bytes)
+{
+ int fd = *reinterpret_cast<const int*>(osFileHandle);
+ int status = read(fd, bytes, byteCount);
+
+ if (status == -1) {
+ int err = errno;
+ return IoResult{
+ .Error = ::MapErrnoToIoResult(err),
+ .SystemError = (uint32_t)err,
+ .BytesMoved = 0,
+ };
+ } else {
+ return IoResult{
+ .Error = IoResult::ERR_None,
+ .SystemError = 0,
+ .BytesMoved = (size_t)status, // Equal to number of bytes read
+ };
+ }
+}
+
+static IoResult WriteBytesDirect(const char* osFileHandle, size_t byteCount, const std::byte* bytes)
+{
+ int fd = *reinterpret_cast<const int*>(osFileHandle);
+ int status = write(fd, bytes, byteCount);
+
+ if (status == -1) {
+ int err = errno;
+ return IoResult{
+ .Error = ::MapErrnoToIoResult(err),
+ .SystemError = (uint32_t)err,
+ .BytesMoved = 0,
+ };
+ } else {
+ return IoResult{
+ .Error = IoResult::ERR_None,
+ .SystemError = 0,
+ .BytesMoved = (size_t)status, // Equal to number of bytes read
+ };
+ }
+}
+
+#else
+# error "Unsupported target platform."
+#endif
+
+int InputFileStream::GetReadInSize() const
+{
+ return mReadInSize;
+}
+
+void InputFileStream::SetReadInSize(int size)
+{
+ if (size > mReadInSize) {
+ mReadInSize = size;
+ mBuffer = std::make_unique<std::byte[]>(size);
+ }
+}
+
+bool InputFileStream::IsEof() const
+{
+ return mEof;
+}
+
+IoResult InputFileStream::ReadBytes(size_t bufferLength, std::byte* buffer)
+{
+ // TODO reduce duplicated code
+
+ auto bytesMoved = std::min<size_t>(mAvailableBytes, bufferLength);
+
+ // On first call after construction, mFirstByteIdx will equal to mReadInSize, i.e. bytesAvailable == 0
+ // and this call to std::memcpy will be no-op
+ std::memcpy(buffer, &mBuffer[mFirstByteIdx], bytesMoved);
+ mFirstByteIdx += (int)bytesMoved;
+ mAvailableBytes -= (int)bytesMoved;
+ buffer += bytesMoved;
+
+ size_t bytesLeft = bufferLength - bytesMoved;
+ if (bytesLeft > mReadInSize) {
+ // Our buffer can't handle rest of the request, just skip the buffering step
+
+ // Read rest of the data into buffer
+ {
+ auto result = ::ReadBytesDirect(mOsFileHandle, bytesLeft, buffer);
+ bytesMoved += result.BytesMoved;
+
+ if (result.Error == IoResult::ERR_None) {
+ if (result.BytesMoved < mReadInSize) {
+ mEof = true;
+ }
+ } else {
+ goto end;
+ }
+ }
+
+ // Refill our buffer
+ {
+ auto result = ::ReadBytesDirect(mOsFileHandle, mReadInSize, mBuffer.get());
+ mFirstByteIdx = 0;
+ mAvailableBytes = (int)result.BytesMoved;
+
+ if (result.Error == IoResult::ERR_None) {
+ if (result.BytesMoved < mReadInSize) {
+ mEof = true;
+ }
+ } else {
+ goto end;
+ }
+ }
+ } else if (bytesLeft > 0) {
+ // Our buffer can handle rest of the request, first buffer than supply the requested data
+
+ // Refill our buffer
+ {
+ auto result = ::ReadBytesDirect(mOsFileHandle, mReadInSize, mBuffer.get());
+ mFirstByteIdx = 0;
+ mAvailableBytes = (int)result.BytesMoved;
+
+ if (result.Error == IoResult::ERR_None) {
+ if (result.BytesMoved < mReadInSize) {
+ mEof = true;
+ }
+ } else {
+ goto end;
+ }
+ }
+
+ // Copy data into buffer
+ {
+ std::memcpy(buffer, &mBuffer[mFirstByteIdx], bytesLeft);
+ mFirstByteIdx += (int)bytesLeft;
+ bytesMoved += bytesLeft;
+ buffer += bytesLeft;
+ }
+ } else {
+ // Request completed already
+ }
+
+end:
+ return IoResult{
+ .Error = IoResult::ERR_None,
+ .SystemError = 0,
+ .BytesMoved = bytesMoved,
+ };
+}
+
+int OutputFileStream::GetMaxBufferSize() const
+{
+ return mMaxBufferSize;
+}
+
+void OutputFileStream::SetMaxBufferSize(int maxSize)
+{
+ FlushBuffer();
+ if (maxSize > mMaxBufferSize) {
+ mMaxBufferSize = maxSize;
+ mBuffer = std::make_unique<std::byte[]>(maxSize);
+ }
+}
+
+IoResult OutputFileStream::WriteBytes(size_t bufferLength, const std::byte* buffer)
+{
+ if (bufferLength + mCurrentBufferSize > mMaxBufferSize) {
+ FlushBuffer();
+
+ if (bufferLength > mMaxBufferSize) {
+ return ::WriteBytesDirect(mOsFileHandle, bufferLength, buffer);
+ }
+ }
+
+ std::memcpy(mBuffer.get() + mCurrentBufferSize, buffer, bufferLength);
+ mCurrentBufferSize += (int)bufferLength;
+
+ return IoResult{
+ .Error = IoResult::ERR_None,
+ .SystemError = 0,
+ .BytesMoved = bufferLength,
+ };
+}
+
+void OutputFileStream::FlushBuffer()
+{
+ ::WriteBytesDirect(mOsFileHandle, mCurrentBufferSize, mBuffer.get());
+ mCurrentBufferSize = 0;
+}