aboutsummaryrefslogtreecommitdiff
path: root/c/data.h
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-08-17 02:33:26 +0200
committerJohn Ankarström <john@ankarstrom.se>2022-08-17 02:33:26 +0200
commitbd857d24443b8c8f5d2f58047c3f8ac5f058acea (patch)
tree0ef5f3c9abf5ccd6ee6a9e376ceac985de2409fd /c/data.h
parent8351d4c42c76584415e02a40d41f497a8f042c72 (diff)
downloadEpisodeBrowser-bd857d24443b8c8f5d2f58047c3f8ac5f058acea.tar.gz
Make FileView more type-safe.
Diffstat (limited to 'c/data.h')
-rw-r--r--c/data.h100
1 files changed, 86 insertions, 14 deletions
diff --git a/c/data.h b/c/data.h
index 2cc4c14..a93b256 100644
--- a/c/data.h
+++ b/c/data.h
@@ -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;