wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
record_bridge.cpp
Go to the documentation of this file.
1
5
7
8#include <bit>
9#include <cstring>
10
11namespace wowlib::db {
12 namespace {
16
19 std::byte* elemPtr(std::byte* rec, const ColumnAccess& a, std::size_t element) {
20 return rec + a.offset + element * a.elemStride;
21 }
22
23 const std::byte* elemPtr(const std::byte* rec, const ColumnAccess& a, std::size_t element) {
24 return rec + a.offset + element * a.elemStride;
25 }
26
31 void storeInt(std::byte* rec, const ColumnAccess& a, std::size_t element, std::int64_t v) {
32 switch (a.kind) {
33 case AccessKind::I8: {
34 auto x = static_cast<std::int8_t>(v);
35 std::memcpy(elemPtr(rec, a, element), &x, sizeof x);
36 break;
37 }
38 case AccessKind::U8: {
39 auto x = static_cast<std::uint8_t>(v);
40 std::memcpy(elemPtr(rec, a, element), &x, sizeof x);
41 break;
42 }
43 case AccessKind::I16: {
44 auto x = static_cast<std::int16_t>(v);
45 std::memcpy(elemPtr(rec, a, element), &x, sizeof x);
46 break;
47 }
48 case AccessKind::U16: {
49 auto x = static_cast<std::uint16_t>(v);
50 std::memcpy(elemPtr(rec, a, element), &x, sizeof x);
51 break;
52 }
53 case AccessKind::I32: {
54 auto x = static_cast<std::int32_t>(v);
55 std::memcpy(elemPtr(rec, a, element), &x, sizeof x);
56 break;
57 }
58 case AccessKind::U32: {
59 auto x = static_cast<std::uint32_t>(v);
60 std::memcpy(elemPtr(rec, a, element), &x, sizeof x);
61 break;
62 }
63 case AccessKind::I64: {
64 auto x = static_cast<std::int64_t>(v);
65 std::memcpy(elemPtr(rec, a, element), &x, sizeof x);
66 break;
67 }
68 case AccessKind::U64: {
69 auto x = static_cast<std::uint64_t>(v);
70 std::memcpy(elemPtr(rec, a, element), &x, sizeof x);
71 break;
72 }
73 case AccessKind::LocStr: {
74 auto x = static_cast<std::uint32_t>(v);
75 std::memcpy(rec + a.flagsOffset, &x, sizeof x);
76 break;
77 }
78 case AccessKind::F32:
79 case AccessKind::Str:
80 break; // float / string members never receive setInt (as before).
81 }
82 }
83
85 std::int64_t loadInt(const std::byte* rec, const ColumnAccess& a, std::size_t element) {
86 const std::byte* p = elemPtr(rec, a, element);
87 switch (a.kind) {
88 case AccessKind::I8: {
89 std::int8_t x;
90 std::memcpy(&x, p, sizeof x);
91 return x;
92 }
93 case AccessKind::U8: {
94 std::uint8_t x;
95 std::memcpy(&x, p, sizeof x);
96 return x;
97 }
98 case AccessKind::I16: {
99 std::int16_t x;
100 std::memcpy(&x, p, sizeof x);
101 return x;
102 }
103 case AccessKind::U16: {
104 std::uint16_t x;
105 std::memcpy(&x, p, sizeof x);
106 return x;
107 }
108 case AccessKind::I32: {
109 std::int32_t x;
110 std::memcpy(&x, p, sizeof x);
111 return x;
112 }
113 case AccessKind::U32: {
114 std::uint32_t x;
115 std::memcpy(&x, p, sizeof x);
116 return x;
117 }
118 case AccessKind::I64:
119 case AccessKind::U64: {
120 std::int64_t x;
121 std::memcpy(&x, p, sizeof x);
122 return x;
123 }
124 case AccessKind::LocStr: {
125 std::uint32_t x;
126 std::memcpy(&x, rec + a.flagsOffset, sizeof x);
127 return x;
128 }
129 case AccessKind::F32:
130 case AccessKind::Str:
131 return 0; // as the typed bridge answered for non-int members.
132 }
133 return 0;
134 }
135
138 std::string& stringAt(std::byte* rec, const ColumnAccess& a, std::size_t element) {
139 return *reinterpret_cast<std::string*>(elemPtr(rec, a, element));
140 }
141
142 const std::string& stringAt(const std::byte* rec, const ColumnAccess& a, std::size_t element) {
143 return *reinterpret_cast<const std::string*>(elemPtr(rec, a, element));
144 }
145
148 std::uint32_t idOfRecord(const RecordOps& ops, const std::byte* rec) {
149 if (ops.idColumn == SIZE_MAX) return 0;
150 return static_cast<std::uint32_t>(loadInt(rec, ops.columns[ops.idColumn], 0));
151 }
152 }
153
154 // --- ErasedRecordSink -----------------------------------------------------
155
156 void ErasedRecordSink::clear() { _ops->clear(_vec); }
157 void ErasedRecordSink::reserve(std::size_t n) { _ops->reserve(_vec, n); }
158 std::size_t ErasedRecordSink::add() { return _ops->add(_vec); }
159 std::size_t ErasedRecordSink::size() const { return _ops->size(_vec); }
160
161 std::uint32_t ErasedRecordSink::idOf(std::size_t record) const {
162 return idOfRecord(*_ops, _ops->cat(_vec, record));
163 }
164
165 void ErasedRecordSink::setInt(std::size_t record, std::size_t column, std::size_t element, std::int64_t value) {
166 storeInt(_ops->at(_vec, record), _ops->columns[column], element, value);
167 }
168
169 void ErasedRecordSink::setFloat(std::size_t record, std::size_t column, std::size_t element, float value) {
170 const ColumnAccess& a = _ops->columns[column];
171 if (a.kind == AccessKind::F32)
172 std::memcpy(elemPtr(_ops->at(_vec, record), a, element), &value, sizeof value);
173 }
174
175 void ErasedRecordSink::setString(std::size_t record,
176 std::size_t column,
177 std::size_t element,
178 std::string_view value) {
179 const ColumnAccess& a = _ops->columns[column];
180 if (a.kind == AccessKind::Str || a.kind == AccessKind::LocStr) stringAt(_ops->at(_vec, record), a, element) =
181 std::string{value};
182 }
183
184 void ErasedRecordSink::cloneWithId(std::size_t src, std::uint32_t newId) {
185 _ops->clonePush(_vec, src);
186 if (_ops->idColumn != SIZE_MAX)
187 storeInt(_ops->at(_vec, _ops->size(_vec) - 1), _ops->columns[_ops->idColumn], 0, newId);
188 }
189
190 std::size_t ErasedRecordSink::findById(std::uint32_t recordId) const {
191 const std::size_t n = _ops->size(_vec);
192 for (std::size_t i = 0; i < n; ++i)
193 if (idOfRecord(*_ops, _ops->cat(_vec, i)) == recordId) return i;
194 return n;
195 }
196
197 // --- ErasedRecordSource ---------------------------------------------------
198
199 std::size_t ErasedRecordSource::size() const { return _ops->size(_vec); }
200
201 std::uint32_t ErasedRecordSource::idOf(std::size_t record) const {
202 return idOfRecord(*_ops, _ops->cat(_vec, record));
203 }
204
205 std::int64_t ErasedRecordSource::getInt(std::size_t record, std::size_t column, std::size_t element) const {
206 return loadInt(_ops->cat(_vec, record), _ops->columns[column], element);
207 }
208
209 std::uint32_t ErasedRecordSource::getSlot(std::size_t record, std::size_t column, std::size_t element) const {
210 const ColumnAccess& a = _ops->columns[column];
211 const std::byte* rec = _ops->cat(_vec, record);
212 switch (a.kind) {
213 case AccessKind::F32: {
214 float f;
215 std::memcpy(&f, elemPtr(rec, a, element), sizeof f);
216 return std::bit_cast<std::uint32_t>(f);
217 }
218 case AccessKind::Str:
219 case AccessKind::LocStr:
220 return 0; // string refs are journaled, never slot-read (as before).
221 default:
222 return static_cast<std::uint32_t>(loadInt(rec, a, element));
223 }
224 }
225
226 std::string_view ErasedRecordSource::getString(std::size_t record, std::size_t column, std::size_t element) const {
227 const ColumnAccess& a = _ops->columns[column];
228 if (a.kind != AccessKind::Str && a.kind != AccessKind::LocStr) return {};
229 return stringAt(_ops->cat(_vec, record), a, element);
230 }
231}
std::uint32_t idOf(std::size_t record) const override
void setInt(std::size_t record, std::size_t column, std::size_t element, std::int64_t value) override
void setFloat(std::size_t record, std::size_t column, std::size_t element, float value) override
std::size_t size() const override
void setString(std::size_t record, std::size_t column, std::size_t element, std::string_view value) override
void reserve(std::size_t n) override
std::size_t findById(std::uint32_t id) const override
The first record whose id equals id, or size() when none (copy source lookup).
void cloneWithId(std::size_t src, std::uint32_t newId) override
Clone record src into a fresh record and set its id column to newId (WDC copy-table materialization).
std::size_t add() override
std::string_view getString(std::size_t record, std::size_t column, std::size_t element) const override
std::uint32_t idOf(std::size_t record) const override
std::int64_t getInt(std::size_t record, std::size_t column, std::size_t element) const override
std::uint32_t getSlot(std::size_t record, std::size_t column, std::size_t element) const override
std::size_t size() const override
AccessKind
The storage kind of one record member, as the erased accessors see it: exact width and signedness for...
The record bridge: ErasedRecordSink / ErasedRecordSource implement the non-templated RecordSink / Rec...
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.
The one thing that stays per-record: a table of thunks over the record VECTOR (only they know sizeof(...
std::size_t idColumn
Index of the $id$ column, or SIZE_MAX.
std::span< const ColumnAccess > columns
Per-column access facts.
Everything the erased core needs to touch one column of a record: where it lives, what it is,...
The one thing that stays per-record: a table of thunks over the record VECTOR (only they know sizeof(...