wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
record_bridge.hpp
Go to the documentation of this file.
1#pragma once
2
24
25#include <array>
26#include <cstddef>
27#include <cstdint>
28#include <span>
29#include <string>
30#include <string_view>
31#include <type_traits>
32#include <vector>
33
34#include <meta>
35
36#include <wowlib/db/codec.hpp>
37#include <wowlib/db/schema.hpp>
38
39namespace wowlib::db {
40 namespace detail {
45 enum class AccessKind : std::uint8_t {
58 };
59
62 struct ColumnAccess {
63 std::uint32_t offset = 0;
64 std::uint32_t flagsOffset = 0;
65 std::uint16_t elemStride = 0;
67 };
68
71 template <typename E>
73 if constexpr (std::same_as<E, float>) return AccessKind::F32;
74 else if constexpr (std::same_as<E, std::string>) return AccessKind::Str;
75 else if constexpr (std::signed_integral<E>) {
76 switch (sizeof(E)) {
77 case 1: return AccessKind::I8;
78 case 2: return AccessKind::I16;
79 case 4: return AccessKind::I32;
80 default: return AccessKind::I64;
81 }
82 }
83 else {
84 switch (sizeof(E)) {
85 case 1: return AccessKind::U8;
86 case 2: return AccessKind::U16;
87 case 4: return AccessKind::U32;
88 default: return AccessKind::U64;
89 }
90 }
91 }
92
94 template <typename Struct>
95 consteval std::uint32_t memberOffset(std::string_view name) {
96 for (auto m : std::meta::nonstatic_data_members_of(^^Struct, std::meta::access_context::unchecked()))
97 if (std::meta::identifier_of(m) == name) return static_cast<std::uint32_t>(std::meta::offset_of(m).bytes);
98 return 0;
99 }
100
103 template <typename Record>
104 consteval auto columnAccessOf() {
105 std::vector<ColumnAccess> out;
106 static constexpr auto Members = recordMembers<Record>();
107 template for (constexpr auto m : Members) {
108 using M = [:std::meta::type_of(m):];
109 using E = typename ElementTraits<M>::Element;
110 ColumnAccess a{};
111 a.offset = static_cast<std::uint32_t>(std::meta::offset_of(m).bytes);
112 if constexpr (LocstringTraits<M>::Value) {
114 a.elemStride = sizeof(std::string);
115 a.flagsOffset = a.offset + memberOffset<M>("flags");
116 }
117 else {
118 a.kind = accessKindOf<E>();
119 a.elemStride = sizeof(E);
120 }
121 out.push_back(a);
122 }
123 return std::define_static_array(out);
124 }
125
130 struct RecordOps {
131 std::span<const ColumnAccess> columns;
132 std::size_t idColumn = SIZE_MAX;
133
134 void (*clear)(void*) = nullptr;
135 void (*reserve)(void*, std::size_t) = nullptr;
136 std::size_t (*add)(void*) = nullptr;
137 std::size_t (*size)(const void*) = nullptr;
138 std::byte* (*at)(void*, std::size_t) = nullptr;
139 const std::byte* (*cat)(const void*, std::size_t) = nullptr;
140 void (*clonePush)(void*, std::size_t) = nullptr;
141 // The three below serve the binding-side live record views (a mutable
142 // sequence over the typed vector, erased the same way the sink is).
143 void (*pushCopy)(void*, const void*) = nullptr;
144 void (*assignAt)(void*, std::size_t, const void*) = nullptr;
145 void (*eraseAt)(void*, std::size_t) = nullptr;
146 };
147
149 template <typename Record>
150 consteval std::size_t idColumnOf() {
151 std::size_t i = 0;
152 for (const Column& col : schemaOf<Record>()) {
153 if (col.isId) return i;
154 ++i;
155 }
156 return SIZE_MAX;
157 }
158
160 template <typename Record>
161 inline constexpr RecordOps RecordOpsFor{
164 [](void* v) { static_cast<std::vector<Record>*>(v)->clear(); },
165 [](void* v, std::size_t n) {
166 static_cast<std::vector<Record>*>(v)->reserve(n);
167 },
168 [](void* v) {
169 auto& vec = *static_cast<std::vector<Record>*>(v);
170 vec.emplace_back();
171 return vec.size() - 1;
172 },
173 [](const void* v) {
174 return static_cast<const std::vector<Record>*>(v)->size();
175 },
176 [](void* v, std::size_t i) {
177 return reinterpret_cast<std::byte*>(&(*static_cast<std::vector<Record>*>(v))[i]);
178 },
179 [](const void* v, std::size_t i) {
180 return reinterpret_cast<const std::byte*>(&(*static_cast<const std::vector<Record>*>(v))[i]);
181 },
182 [](void* v, std::size_t src) {
183 auto& vec = *static_cast<std::vector<Record>*>(v);
184 vec.push_back(vec[src]);
185 },
186 [](void* v, const void* src) {
187 static_cast<std::vector<Record>*>(v)->push_back(*static_cast<const Record*>(src));
188 },
189 [](void* v, std::size_t i, const void* src) {
190 (*static_cast<std::vector<Record>*>(v))[i] = *static_cast<const Record*>(src);
191 },
192 [](void* v, std::size_t i) {
193 auto& vec = *static_cast<std::vector<Record>*>(v);
194 vec.erase(vec.begin() + static_cast<std::ptrdiff_t>(i));
195 }
196 };
197 }
198
203 class ErasedRecordSink final : public RecordSink {
204 public:
205 template <typename Record>
206 explicit ErasedRecordSink(std::vector<Record>& records)
207 : _vec{&records}, _ops{&detail::RecordOpsFor<Record>} {}
208
210 ErasedRecordSink(void* recordsVec, const detail::RecordOps* ops)
211 : _vec{recordsVec}, _ops{ops} {}
212
213 void clear() override;
214 void reserve(std::size_t n) override;
215 std::size_t add() override;
216 std::size_t size() const override;
217 std::uint32_t idOf(std::size_t record) const override;
218
219 void setInt(std::size_t record, std::size_t column, std::size_t element, std::int64_t value) override;
220 void setFloat(std::size_t record, std::size_t column, std::size_t element, float value) override;
221 void setString(std::size_t record, std::size_t column, std::size_t element, std::string_view value) override;
222
223 void cloneWithId(std::size_t src, std::uint32_t newId) override;
224 std::size_t findById(std::uint32_t id) const override;
225
226 private:
227 void* _vec;
228 const detail::RecordOps* _ops;
229 };
230
233 class ErasedRecordSource final : public RecordSource {
234 public:
235 template <typename Record>
236 explicit ErasedRecordSource(const std::vector<Record>& records)
237 : _vec{&records}, _ops{&detail::RecordOpsFor<Record>} {}
238
240 ErasedRecordSource(const void* recordsVec, const detail::RecordOps* ops)
241 : _vec{recordsVec}, _ops{ops} {}
242
243 std::size_t size() const override;
244 std::uint32_t idOf(std::size_t record) const override;
245
246 std::int64_t getInt(std::size_t record, std::size_t column, std::size_t element) const override;
247 std::uint32_t getSlot(std::size_t record, std::size_t column, std::size_t element) const override;
248 std::string_view getString(std::size_t record, std::size_t column, std::size_t element) const override;
249
250 private:
251 const void* _vec;
252 const detail::RecordOps* _ops;
253 };
254}
ErasedRecordSink(void *recordsVec, const detail::RecordOps *ops)
from pre-erased parts (TableCore's path — it holds exactly these).
ErasedRecordSink(std::vector< Record > &records)
ErasedRecordSource(const std::vector< Record > &records)
ErasedRecordSource(const void *recordsVec, const detail::RecordOps *ops)
from pre-erased parts (TableCore's path).
The decode target: the codecs build the record vector through this, one field at a time.
Definition codec.hpp:110
The encode source: the codecs read the record vector through this.
Definition codec.hpp:135
The type-erased boundary between the templated Table<Record> facade (table.hpp) and the per-format co...
consteval std::uint32_t memberOffset(std::string_view name)
The byte offset of Struct's member named name (consteval).
consteval auto columnAccessOf()
The per-column access table of Record: one ColumnAccess per member, declaration order — the same 1:1 ...
consteval AccessKind accessKindOf()
The AccessKind of element type E (consteval; bool is rejected by classifyMember long before this runs...
constexpr RecordOps RecordOpsFor
The RecordOps instance of Record — the entire per-record residue.
consteval auto recordMembers()
The reflected non-static data members of record Record, declaration order.
Definition schema.hpp:207
consteval std::size_t idColumnOf()
The $id$ column index of Record (consteval), or SIZE_MAX.
AccessKind
The storage kind of one record member, as the erased accessors see it: exact width and signedness for...
@ U64
Integer elements, by shape.
@ LocStr
A LocString<N>: strings at offset, flags on the side.
@ Str
A std::string element.
consteval auto schemaOf()
The column schema of record Record, derived by reflection: one Column per member, declaration order,...
Definition schema.hpp:220
Schema reflection over generated client-database record structs: the column list, record stride and f...
Everything the erased core needs to touch one column of a record: where it lives, what it is,...
One column of a record schema, derived from a record member by reflection (typed records) or from the...
Definition schema.hpp:46
Everything the erased core needs to touch one column of a record: where it lives, what it is,...
std::uint16_t elemStride
Bytes between array elements.
std::uint32_t offset
Member byte offset inside Record.
std::uint32_t flagsOffset
LocStr only: byte offset of .flags.
AccessKind kind
The element's storage kind.
static constexpr bool Value
Definition schema.hpp:127
The one thing that stays per-record: a table of thunks over the record VECTOR (only they know sizeof(...
std::size_t(* add)(void *)
emplace_back() → index.
std::size_t(* size)(const void *)
void(* eraseAt)(void *, std::size_t)
vec.erase(begin+i).
void(* clonePush)(void *, std::size_t)
push_back(vec[src]).
std::size_t idColumn
Index of the $id$ column, or SIZE_MAX.
void(* assignAt)(void *, std::size_t, const void *)
vec[i] = *src.
void(* pushCopy)(void *, const void *)
push_back(*src).
std::span< const ColumnAccess > columns
Per-column access facts.
void(* reserve)(void *, std::size_t)