diff options
Diffstat (limited to 'c/data.h')
-rw-r--r-- | c/data.h | 100 |
1 files changed, 86 insertions, 14 deletions
@@ -1,15 +1,21 @@ #ifndef DATA_H #define DATA_H +#include <stdexcept> + #include "pl.h" #include "util.h" #include "wcharptr.h" +#include "win.h" + +/* The structs ending with A are written as-is to disk. As such, they + * should be regarded as immutable. If the format needs to be changed + * in the future, then new structs ending with B should be added. */ -/* ElvDataA and DlvDataA are written as-is to disk; note the careful - * alignment. As such, they should be regarded as immutable. If the - * format needs to be changed in the future, then new structs called - * ElvDataB and DlvDataB should be added. */ +/* Note that unsigned chars are 1-byte-aligned, wchar_t are + * 2-byte-aligned and ints are 4-byte-aligned (on x86 Windows). */ +/* Basic episode data presented in episode list view. */ struct ElvDataA { unsigned char version = 'a'; @@ -21,6 +27,7 @@ struct ElvDataA wchar_t title[128] = {0}; }; +/* Extra episode data presented in data list view. */ struct DlvDataA { wchar_t date[32] = {0}; @@ -30,21 +37,86 @@ struct DlvDataA wchar_t wiki[192] = {0}; }; +/* Variable template for obtaining the version of a given struct. */ +template <typename T> +constexpr inline unsigned char Version = T{}.version; + +template <typename T, typename = void> +constexpr inline bool HasVersion = false; + +template <typename T> +constexpr inline bool HasVersion<T, std::void_t<typename T::version>> = true; + +/* FileView objects manage a memory-mapped file. The view buffer may + * be treated as an array of a given type T. Note that reading and + * writing a view can throw structured exceptions. We ignore these. */ +template <typename T> struct FileView { - FileView(const wchar_t* filename, size_t cb); - ~FileView(); - inline operator unsigned char*() { return view; } - inline operator ElvDataA*() { return reinterpret_cast<ElvDataA*>(view); } + FileView(const wchar_t* filename, size_t c_) : c(c_) + { + hf = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, + 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hf == INVALID_HANDLE_VALUE) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + hf = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, + 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hf == INVALID_HANDLE_VALUE) + throw Win32Error{}; + } else + throw Win32Error{}; + } + + LARGE_INTEGER cbMap; + cbMap.QuadPart = c*sizeof(T); + hm = CreateFileMapping(hf, nullptr, PAGE_READWRITE, + cbMap.HighPart, cbMap.LowPart, nullptr); + if (!hm) + throw Win32Error{}; + + view = reinterpret_cast<T*>(MapViewOfFile(hm, FILE_MAP_ALL_ACCESS, 0, 0, 0)); + if (!view) + throw Win32Error{}; + } + + ~FileView() + { + FlushViewOfFile(view, 0); + UnmapViewOfFile(view); + CloseHandle(hm); + CloseHandle(hf); + } + + /* Access element by index, performing bounds check and, if + * applicable for T, version validation. */ + T& At(size_t i) + { + if (i >= c) + throw std::out_of_range("index larger than buffer"); + + T& t = view[i]; + + if constexpr (HasVersion<T>) + if (t.version != Version<T>) + throw std::runtime_error("invalid struct version"); + + /* TODO: Use a custom exception type that informs the + * user of possible data corruption. */ + + return t; + } + + inline operator T*() noexcept + { + return view; + } + HANDLE hf; HANDLE hm; - unsigned char* view; - /* TODO: Handle exceptions on read and write... */ + T* view; + size_t c; }; - -void Write(unsigned char* buf, const ElvDataA& e); -ElvDataA* Read(unsigned char* buf); - + inline int FromWeb(const int iEp, ElvDataA& e, DlvDataA& d) noexcept { WcharPtr title, wiki, date, source, hint; |