wowlib 0.0.0
Read & write World of Warcraft client files — a C++26 core
Loading...
Searching...
No Matches
codec_common.cpp
Go to the documentation of this file.
1
5
7
8#include <bit>
9#include <cstring>
10
11namespace wowlib::db::detail {
12 std::int64_t readInt(std::span<const std::byte> image, std::size_t& pos, std::size_t bytes, bool isSigned) {
13 std::uint64_t raw = 0;
14 std::memcpy(&raw, image.data() + pos, bytes);
15 pos += bytes;
16 if (isSigned && bytes < 8) {
17 const std::uint64_t signBit = std::uint64_t{1} << (bytes * 8 - 1);
18 if (raw & signBit) raw |= ~((std::uint64_t{1} << (bytes * 8)) - 1);
19 }
20 return static_cast<std::int64_t>(raw);
21 }
22
23 void writeInt(FileBuffer& out, std::int64_t value, std::size_t bytes) {
24 const auto raw = static_cast<std::uint64_t>(value);
25 for (std::size_t i = 0; i < bytes; ++i) out.push_back(static_cast<std::byte>((raw >> (i * 8)) & 0xff));
26 }
27
28 namespace {
30 std::uint32_t readU32(std::span<const std::byte> image, std::size_t& pos) {
31 std::uint32_t v = 0;
32 std::memcpy(&v, image.data() + pos, 4);
33 pos += 4;
34 return v;
35 }
36
37 void writeU32(FileBuffer& out, std::uint32_t v) {
38 const auto* b = reinterpret_cast<const std::byte*>(&v);
39 out.insert(out.end(), b, b + 4);
40 }
41 }
42
44 if (block.empty()) std::ignore = block.add("");
45 // Blizzard blocks always lead with a zero byte
46 for (const formats::StringBlock::Entry& entry : block.entries()) lookup.try_emplace(entry.value, entry.offset);
47 }
48
49 std::uint32_t StringPool::resolve(std::string_view value,
50 const std::vector<std::uint32_t>& journal,
51 std::size_t& cursor) {
52 const std::size_t slot = cursor++;
53 const std::string owned{value};
54 if (slot < journal.size()) {
55 const std::uint32_t original = journal[slot];
56 if (block.at(original) == owned) return original;
57 }
58 if (const auto it = lookup.find(owned); it != lookup.end()) return it->second;
59 const std::uint32_t offset = block.add(owned);
60 lookup.emplace(owned, offset);
61 return offset;
62 }
63
64 void decodeInlineRecord(std::span<const Column> schema,
65 std::size_t record,
66 std::span<const std::byte> image,
67 RecordSink& sink,
68 TableState& state) {
69 std::size_t pos = 0;
70 std::size_t col = 0;
71 for (const Column& c : schema) {
72 if (c.noninline) {
73 ++col;
74 continue;
75 }
76 switch (c.type) {
77 case ColumnType::Int:
78 for (std::uint16_t e = 0; e < c.arrayLen; ++e)
79 sink.setInt(record, col, e, readInt(image, pos, c.bits / 8u, c.isSigned));
80 break;
82 for (std::uint16_t e = 0; e < c.arrayLen; ++e)
83 sink.setFloat(record, col, e, std::bit_cast<float>(readU32(image, pos)));
84 break;
86 for (std::uint16_t e = 0; e < c.arrayLen; ++e) {
87 const std::uint32_t off = readU32(image, pos);
88 state.stringOffsets.push_back(off);
89 sink.setString(record, col, e, state.strings.at(off));
90 }
91 break;
93 for (std::uint8_t loc = 0; loc < c.localeCount; ++loc) {
94 const std::uint32_t off = readU32(image, pos);
95 state.stringOffsets.push_back(off);
96 sink.setString(record, col, loc, state.strings.at(off));
97 }
98 sink.setInt(record, col, c.localeCount, readU32(image, pos));
99 break;
100 }
101 ++col;
102 }
103 }
104
105 void encodeInlineRecord(std::span<const Column> schema,
106 const RecordSource& source,
107 std::size_t record,
108 FileBuffer& out,
109 StringPool& pool,
110 const TableState& state,
111 std::size_t& cursor) {
112 std::size_t col = 0;
113 for (const Column& c : schema) {
114 if (c.noninline) {
115 ++col;
116 continue;
117 }
118 switch (c.type) {
119 case ColumnType::Int:
120 for (std::uint16_t e = 0; e < c.arrayLen; ++e) writeInt(out, source.getInt(record, col, e), c.bits / 8u);
121 break;
123 for (std::uint16_t e = 0; e < c.arrayLen; ++e) writeU32(out, source.getSlot(record, col, e));
124 break;
126 for (std::uint16_t e = 0; e < c.arrayLen; ++e)
127 writeU32(out, pool.resolve(source.getString(record, col, e), state.stringOffsets, cursor));
128 break;
130 for (std::uint8_t loc = 0; loc < c.localeCount; ++loc)
131 writeU32(out, pool.resolve(source.getString(record, col, loc), state.stringOffsets, cursor));
132 writeU32(out, static_cast<std::uint32_t>(source.getInt(record, col, c.localeCount)));
133 break;
134 }
135 ++col;
136 }
137 }
138}
The decode target: the codecs build the record vector through this, one field at a time.
Definition codec.hpp:110
virtual void setFloat(std::size_t record, std::size_t column, std::size_t element, float value)=0
virtual void setString(std::size_t record, std::size_t column, std::size_t element, std::string_view value)=0
virtual void setInt(std::size_t record, std::size_t column, std::size_t element, std::int64_t value)=0
The encode source: the codecs read the record vector through this.
Definition codec.hpp:135
virtual std::int64_t getInt(std::size_t record, std::size_t column, std::size_t element) const =0
virtual std::uint32_t getSlot(std::size_t record, std::size_t column, std::size_t element) const =0
virtual std::string_view getString(std::size_t record, std::size_t column, std::size_t element) const =0
std::string_view at(std::uint32_t offset) const
The string at a byte offset another chunk stored; empty if the offset lands on padding or out of rang...
Shared codec internals for the fixed-stride formats (WDBC, WDB2): the string pool that reproduces byt...
std::int64_t readInt(std::span< const std::byte > image, std::size_t &pos, std::size_t bytes, bool isSigned)
Read a little-endian integer of bytes bytes at pos, sign-extended when isSigned, advancing pos.
void decodeInlineRecord(std::span< const Column > schema, std::size_t record, std::span< const std::byte > image, RecordSink &sink, TableState &state)
Decode one fixed-stride record's inline fields into sink and journal its string offsets (record then ...
void encodeInlineRecord(std::span< const Column > schema, const RecordSource &source, std::size_t record, FileBuffer &out, StringPool &pool, const TableState &state, std::size_t &cursor)
Encode one fixed-stride record's inline fields from source, appending to out and resolving strings th...
void writeInt(FileBuffer &out, std::int64_t value, std::size_t bytes)
Append value's low bytes bytes (little-endian) to out.
@ Int
An integer column; Column::bits / Column::isSigned give the shape.
Definition schema.hpp:35
@ Float
A 32-bit IEEE float column.
Definition schema.hpp:36
@ String
A string-block reference column (u32 offset on disk).
Definition schema.hpp:37
@ LocString
A pre-Cataclysm localized string column (see LocString).
Definition schema.hpp:38
std::vector< std::byte > FileBuffer
Owning byte buffer for file contents read out of a client storage.
Definition buffer.hpp:16
One column of a record schema, derived from a record member by reflection (typed records) or from the...
Definition schema.hpp:46
The preserved decode state a table carries between read() and write() so a WDBC/WDB2 write is byte-pe...
Definition codec.hpp:68
formats::StringBlock strings
Preserved string block; offsets never move.
Definition codec.hpp:72
std::vector< std::uint32_t > stringOffsets
The original string-block offset of every string field, row-major in record then schema order — the b...
Definition codec.hpp:76
A writable string block resolving values to byte-perfect offsets: the journaled original offset while...
StringPool(const formats::StringBlock &source)
Copy source (leading with the mandatory zero byte) and index it.
std::unordered_map< std::string, std::uint32_t > lookup
std::uint32_t resolve(std::string_view value, const std::vector< std::uint32_t > &journal, std::size_t &cursor)
The offset to store for value: journal[cursor] when it still holds value, else a deduped/appended off...
One decoded string and the blob byte offset other chunks reference it by.