#pragma once
#include <string>
#include <memory>
class WinFile
{
public:
WinFile(const std::string& path = "");
WinFile(WinFile&& file);
~WinFile();
void open(std::string path);
operator bool() const;
WinFile& read(char* data, size_t size);
WinFile& write(char* data, size_t size);
size_t gcount() const;
WinFile& seekg(size_t pos);
WinFile& seekp(size_t pos);
bool eof() const;
private:
class Private;
std::unique_ptr<Private> d;
};
#include "win_file.h"
#include <Windows.h>
#include <vector>
class WinFile::Private
{
public:
Private(std::string path)
{
m_hFile = nullptr;
m_offset = 0;
m_gcount = 0;
m_size = 0;
m_complete = true;
m_pageSize = 512;
if (path != "") {
m_hFile = CreateFileA(path.c_str(),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
NULL);
if (m_hFile) {
LARGE_INTEGER size;
if (GetFileSizeEx(m_hFile, &size))
m_size = size.QuadPart;
}
}
}
~Private()
{
if (m_hFile)
CloseHandle(m_hFile);
}
size_t read(char *data, size_t offset, size_t size)
{
auto event = CreateEvent(NULL, TRUE, FALSE, NULL);
OVERLAPPED overlap = OVERLAPPED();
overlap.Offset = offset & ((1LL << 32) - 1);
overlap.OffsetHigh = offset >> 32;
overlap.hEvent = event;
DWORD readSize = 0;
BOOL rc = ReadFile(m_hFile, data, size, &readSize, &overlap);
if (rc) {
// done
}
else if (GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(&event, INFINITE);
bool rc = GetOverlappedResult(m_hFile, &overlap, &readSize, FALSE);
if (!rc && GetLastError() == ERROR_IO_INCOMPLETE) {
bool rc = GetOverlappedResult(m_hFile, &overlap, &readSize, TRUE);
}
}
else {
// failed
}
return readSize;
}
size_t write(char *data, size_t offset, size_t size)
{
auto event = CreateEvent(NULL, TRUE, FALSE, NULL);
OVERLAPPED overlap = OVERLAPPED();
overlap.Offset = offset & ((1LL << 32) - 1);
overlap.OffsetHigh = offset >> 32;
overlap.hEvent = event;
DWORD written = 0;
BOOL rc = WriteFile(m_hFile, data, size, &written, &overlap);
if (rc) {
// done
}
else if (GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(&event, INFINITE);
bool rc = GetOverlappedResult(m_hFile, &overlap, &written, FALSE);
if (!rc && GetLastError() == ERROR_IO_INCOMPLETE) {
bool rc = GetOverlappedResult(m_hFile, &overlap, &written, TRUE);
}
}
else {
// failed
}
return written;
}
HANDLE m_hFile;
size_t m_offset;
size_t m_gcount;
size_t m_size;
size_t m_pageSize;
bool m_complete;
std::vector<char> m_buf;
};
WinFile::WinFile(const std::string& path)
{
d = std::make_unique<Private>(path);
}
WinFile::WinFile(WinFile && file)
{
d = std::move(file.d);
}
WinFile::~WinFile()
{
}
void WinFile::open(std::string path)
{
d = std::make_unique<Private>(path);
}
WinFile::operator bool() const
{
if (d->m_hFile == INVALID_HANDLE_VALUE)
return false;
return d->m_complete;
}
WinFile & WinFile::read(char * data, size_t size)
{
size_t buffedSize = d->m_buf.size();
if (buffedSize > 0 || size < d->m_pageSize || (size & (size - 1) != 0)) {
if (buffedSize < size) {
size_t npage = round(1. * (size - buffedSize) / d->m_pageSize);
size_t pagesSize = npage * d->m_pageSize;
d->m_buf.resize(buffedSize + pagesSize);
char *buf = d->m_buf.data() + buffedSize;
size_t readSize = d->read(buf, d->m_offset, pagesSize);
if (readSize < pagesSize)
d->m_buf.resize(buffedSize + readSize);
d->m_offset += readSize;
buffedSize = d->m_buf.size();
}
size_t copySize = min(size, buffedSize);
if (copySize > 0) {
memcpy(data, d->m_buf.data(), copySize);
size_t leftSize = buffedSize - copySize;
if (leftSize > 0)
memcpy(d->m_buf.data(), d->m_buf.data() + copySize, leftSize);
d->m_buf.resize(leftSize);
}
d->m_gcount = copySize;
d->m_complete = size == copySize;
}
else {
size_t readSize = d->read(data, d->m_offset, size);
d->m_gcount = readSize;
d->m_complete = size == readSize;
d->m_offset += readSize;
}
return *this;
}
WinFile & WinFile::write(char * data, size_t size)
{
size_t written = d->write(data, d->m_offset, size);
d->m_gcount = written;
d->m_complete = size == written;
d->m_offset += written;
return *this;
}
size_t WinFile::gcount() const
{
return d->m_gcount;
}
WinFile & WinFile::seekg(size_t pos)
{
d->m_offset = pos;
return *this;
}
WinFile & WinFile::seekp(size_t pos)
{
d->m_offset = pos;
return *this;
}
bool WinFile::eof() const
{
return d->m_offset >= d->m_size;
}