aboutsummaryrefslogtreecommitdiff
path: root/src/common/DataStores/DB2FileLoader.cpp
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2017-03-08 18:10:02 +0100
committerShauren <shauren.trinity@gmail.com>2017-03-08 18:10:02 +0100
commit7b235ce6e4da0e9c19fa9c6306bc7a71c7fb905d (patch)
tree28611e02e44e55d28f60a9b1b3433831ed6772b8 /src/common/DataStores/DB2FileLoader.cpp
parentf585c831248f24c93697b0c314dd015897febe39 (diff)
Core/DataStores: Refactor DB2 loaders to be reusable by extractors
Diffstat (limited to 'src/common/DataStores/DB2FileLoader.cpp')
-rw-r--r--src/common/DataStores/DB2FileLoader.cpp1156
1 files changed, 1156 insertions, 0 deletions
diff --git a/src/common/DataStores/DB2FileLoader.cpp b/src/common/DataStores/DB2FileLoader.cpp
new file mode 100644
index 00000000000..6ec065aef27
--- /dev/null
+++ b/src/common/DataStores/DB2FileLoader.cpp
@@ -0,0 +1,1156 @@
+/*
+ * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "DB2FileLoader.h"
+#include "ByteConverter.h"
+#include "Common.h"
+#include "DB2Meta.h"
+#include "Errors.h"
+#include "Log.h"
+
+DB2FileLoadInfo::DB2FileLoadInfo() : Fields(nullptr), FieldCount(0), Meta(nullptr)
+{
+}
+
+DB2FileLoadInfo::DB2FileLoadInfo(DB2FieldMeta const* fields, std::size_t fieldCount, DB2Meta const* meta)
+ : Fields(fields), FieldCount(fieldCount), Meta(meta)
+{
+ TypesString.reserve(FieldCount);
+ for (std::size_t i = 0; i < FieldCount; ++i)
+ TypesString += char(Fields[i].Type);
+}
+
+uint32 DB2FileLoadInfo::GetStringFieldCount(bool localizedOnly) const
+{
+ uint32 stringFields = 0;
+ for (std::size_t i = 0; i < TypesString.length(); ++i)
+ if (TypesString[i] == FT_STRING || (TypesString[i] == FT_STRING_NOT_LOCALIZED && !localizedOnly))
+ ++stringFields;
+
+ return stringFields;
+}
+
+DB2FileSource::~DB2FileSource()
+{
+}
+
+class DB2FileLoaderImpl
+{
+public:
+ virtual ~DB2FileLoaderImpl() { }
+ virtual bool Load(DB2FileSource* source, DB2FileLoadInfo const* loadInfo, DB2Header const* header) = 0;
+ virtual char* AutoProduceData(uint32& count, char**& indexTable, std::vector<char*>& stringPool) = 0;
+ virtual char* AutoProduceStrings(char* dataTable, uint32 locale) = 0;
+ virtual void AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable) = 0;
+ virtual DB2Record GetRecord(uint32 recordNumber) const = 0;
+ virtual DB2RecordCopy GetRecordCopy(uint32 copyNumber) const = 0;
+ virtual uint32 GetRecordCount() const = 0;
+ virtual uint32 GetRecordCopyCount() const = 0;
+ virtual uint32 GetMaxId() const = 0;
+
+private:
+ friend class DB2Record;
+ virtual unsigned char const* GetRawRecordData(uint32 recordNumber) const = 0;
+ virtual uint32 RecordGetId(unsigned char const* record, uint32 recordIndex) const = 0;
+ virtual uint8 RecordGetUInt8(unsigned char const* record, uint32 field, uint32 arrayIndex) const = 0;
+ virtual uint16 RecordGetUInt16(unsigned char const* record, uint32 field, uint32 arrayIndex) const = 0;
+ virtual uint32 RecordGetUInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const = 0;
+ virtual int32 RecordGetInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const = 0;
+ virtual float RecordGetFloat(unsigned char const* record, uint32 field, uint32 arrayIndex) const = 0;
+ virtual char const* RecordGetString(unsigned char const* record, uint32 field, uint32 arrayIndex) const = 0;
+ virtual std::size_t* RecordCreateDetachedFieldOffsets(std::size_t* oldOffsets) const = 0;
+ virtual void RecordDestroyFieldOffsets(std::size_t*& fieldOffsets) const = 0;
+};
+
+class DB2FileLoaderRegularImpl final : public DB2FileLoaderImpl
+{
+public:
+ DB2FileLoaderRegularImpl();
+ ~DB2FileLoaderRegularImpl();
+
+ bool Load(DB2FileSource* source, DB2FileLoadInfo const* loadInfo, DB2Header const* header) override;
+ char* AutoProduceData(uint32& count, char**& indexTable, std::vector<char*>& stringPool) override;
+ char* AutoProduceStrings(char* dataTable, uint32 locale) override;
+ void AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable) override;
+ DB2Record GetRecord(uint32 recordNumber) const override;
+ DB2RecordCopy GetRecordCopy(uint32 copyNumber) const override;
+ uint32 GetRecordCount() const override;
+ uint32 GetRecordCopyCount() const override;
+ uint32 GetMaxId() const override;
+
+private:
+ unsigned char const* GetRawRecordData(uint32 recordNumber) const override;
+ uint32 RecordGetId(unsigned char const* record, uint32 recordIndex) const override;
+ uint8 RecordGetUInt8(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ uint16 RecordGetUInt16(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ uint32 RecordGetUInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ int32 RecordGetInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ float RecordGetFloat(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ char const* RecordGetString(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ uint32 RecordGetVarInt(unsigned char const* record, uint32 field, uint32 arrayIndex, bool isSigned) const;
+ uint16 GetFieldOffset(uint32 field) const;
+ uint16 GetFieldSize(uint32 field) const;
+ std::size_t* RecordCreateDetachedFieldOffsets(std::size_t* oldOffsets) const override;
+ void RecordDestroyFieldOffsets(std::size_t*& fieldOffsets) const override;
+
+#pragma pack(push, 1)
+ struct FieldEntry
+ {
+ uint16 UnusedBits;
+ uint16 Offset;
+ };
+#pragma pack(pop)
+
+ char const* fileName;
+ DB2FileLoadInfo const* _loadInfo;
+
+ DB2Header const* _header;
+
+ unsigned char* data;
+ unsigned char* stringTable;
+ unsigned char* idTable;
+ uint32 idTableSize;
+ DB2RecordCopy* copyTable;
+ FieldEntry* fields;
+};
+
+class DB2FileLoaderSparseImpl final : public DB2FileLoaderImpl
+{
+public:
+ DB2FileLoaderSparseImpl();
+ ~DB2FileLoaderSparseImpl();
+
+ bool Load(DB2FileSource* source, DB2FileLoadInfo const* loadInfo, DB2Header const* header) override;
+ char* AutoProduceData(uint32& records, char**& indexTable, std::vector<char*>& stringPool) override;
+ char* AutoProduceStrings(char* dataTable, uint32 locale) override;
+ void AutoProduceRecordCopies(uint32 /*records*/, char** /*indexTable*/, char* /*dataTable*/) override { }
+ DB2Record GetRecord(uint32 recordNumber) const override;
+ DB2RecordCopy GetRecordCopy(uint32 copyNumber) const override;
+ uint32 GetRecordCount() const override;
+ uint32 GetRecordCopyCount() const override;
+ uint32 GetMaxId() const override;
+
+private:
+ unsigned char const* GetRawRecordData(uint32 recordNumber) const override;
+ uint32 RecordGetId(unsigned char const* record, uint32 recordIndex) const override;
+ uint8 RecordGetUInt8(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ uint16 RecordGetUInt16(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ uint32 RecordGetUInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ int32 RecordGetInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ float RecordGetFloat(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ char const* RecordGetString(unsigned char const* record, uint32 field, uint32 arrayIndex) const override;
+ uint32 RecordGetVarInt(unsigned char const* record, uint32 field, uint32 arrayIndex, bool isSigned) const;
+ uint16 GetFieldOffset(uint32 field, uint32 arrayIndex) const;
+ uint16 GetFieldSize(uint32 field) const;
+ std::size_t* RecordCreateDetachedFieldOffsets(std::size_t* oldOffsets) const override;
+ void RecordDestroyFieldOffsets(std::size_t*& fieldOffsets) const override;
+ void CalculateAndStoreFieldOffsets(unsigned char const* rawRecord) const;
+
+#pragma pack(push, 1)
+ struct OffsetTableEntry
+ {
+ uint32 FileOffset;
+ uint16 RecordSize;
+ };
+ struct FieldEntry
+ {
+ uint16 UnusedBits;
+ uint16 Offset;
+ };
+#pragma pack(pop)
+
+ char const* fileName;
+ DB2FileLoadInfo const* _loadInfo;
+ std::size_t* _fieldAndArrayOffsets;
+
+ DB2Header const* _header;
+ FieldEntry* fields;
+
+ std::size_t dataStart;
+ unsigned char* data;
+ OffsetTableEntry* offsets;
+};
+
+DB2FileLoaderRegularImpl::DB2FileLoaderRegularImpl()
+{
+ fileName = nullptr;
+ _loadInfo = nullptr;
+ _header = nullptr;
+ data = nullptr;
+ stringTable = nullptr;
+ idTable = nullptr;
+ idTableSize = 0;
+ copyTable = nullptr;
+ fields = nullptr;
+}
+
+bool DB2FileLoaderRegularImpl::Load(DB2FileSource* source, DB2FileLoadInfo const* loadInfo, DB2Header const* header)
+{
+ _loadInfo = loadInfo;
+ _header = header;
+ ASSERT(_loadInfo->Meta->IndexField == -1 || _loadInfo->Meta->IndexField == int32(header->IndexField));
+
+ fileName = source->GetFileName();
+ fields = new FieldEntry[header->FieldCount];
+ if (!source->Read(fields, header->FieldCount * sizeof(FieldEntry)))
+ return false;
+
+ if (!_loadInfo->Meta->HasIndexFieldInData())
+ idTableSize = header->RecordCount * sizeof(uint32);
+
+ data = new unsigned char[header->RecordSize * header->RecordCount + header->StringTableSize];
+ stringTable = data + header->RecordSize * header->RecordCount;
+
+ if (!source->Read(data, header->RecordSize * header->RecordCount + header->StringTableSize))
+ return false;
+
+ if (idTableSize)
+ {
+ idTable = new unsigned char[idTableSize];
+ if (!source->Read(idTable, idTableSize))
+ return false;
+ }
+
+ if (header->CopyTableSize)
+ {
+ copyTable = new DB2RecordCopy[header->CopyTableSize / sizeof(DB2RecordCopy)];
+ if (!source->Read(copyTable, header->CopyTableSize))
+ return false;
+ }
+
+ return true;
+}
+
+DB2FileLoaderRegularImpl::~DB2FileLoaderRegularImpl()
+{
+ delete[] data;
+ delete[] idTable;
+ delete[] copyTable;
+ delete[] fields;
+}
+
+static char const* const nullStr = "";
+
+char* DB2FileLoaderRegularImpl::AutoProduceData(uint32& records, char**& indexTable, std::vector<char*>& stringPool)
+{
+ if (_loadInfo->Meta->FieldCount != _header->FieldCount)
+ return nullptr;
+
+ //get struct size and index pos
+ uint32 recordsize = _loadInfo->Meta->GetRecordSize();
+
+ uint32 maxi = GetMaxId() + 1;
+
+ using index_entry_t = char*;
+
+ records = maxi;
+ indexTable = new index_entry_t[maxi];
+ memset(indexTable, 0, maxi * sizeof(index_entry_t));
+
+ char* dataTable = new char[(_header->RecordCount + (_header->CopyTableSize / 8)) * recordsize];
+
+ // we store flat holders pool as single memory block
+ std::size_t stringFields = _loadInfo->GetStringFieldCount(false);
+ std::size_t localizedStringFields = _loadInfo->GetStringFieldCount(true);
+
+ // each string field at load have array of string for each locale
+ std::size_t stringHoldersRecordPoolSize = localizedStringFields * sizeof(LocalizedString) + (stringFields - localizedStringFields) * sizeof(char*);
+ char* stringHoldersPool = nullptr;
+ if (stringFields)
+ {
+ std::size_t stringHoldersPoolSize = stringHoldersRecordPoolSize * _header->RecordCount;
+
+ stringHoldersPool = new char[stringHoldersPoolSize];
+ stringPool.push_back(stringHoldersPool);
+
+ // DB2 strings expected to have at least empty string
+ for (std::size_t i = 0; i < stringHoldersPoolSize / sizeof(char*); ++i)
+ ((char const**)stringHoldersPool)[i] = nullStr;
+ }
+
+ uint32 offset = 0;
+
+ for (uint32 y = 0; y < _header->RecordCount; y++)
+ {
+ unsigned char const* rawRecord = GetRawRecordData(y);
+ if (!rawRecord)
+ continue;
+
+ uint32 indexVal = RecordGetId(rawRecord, y);
+
+ indexTable[indexVal] = &dataTable[offset];
+
+ uint32 fieldIndex = 0;
+ if (!_loadInfo->Meta->HasIndexFieldInData())
+ {
+ *((uint32*)(&dataTable[offset])) = indexVal;
+ offset += 4;
+ ++fieldIndex;
+ }
+
+ uint32 stringFieldOffset = 0;
+
+ for (uint32 x = 0; x < _header->FieldCount; ++x)
+ {
+ for (uint32 z = 0; z < _loadInfo->Meta->ArraySizes[x]; ++z)
+ {
+ switch (_loadInfo->TypesString[fieldIndex])
+ {
+ case FT_FLOAT:
+ *((float*)(&dataTable[offset])) = RecordGetFloat(rawRecord, x, z);
+ offset += 4;
+ break;
+ case FT_INT:
+ *((uint32*)(&dataTable[offset])) = RecordGetVarInt(rawRecord, x, z, _loadInfo->Fields[fieldIndex].IsSigned);
+ offset += 4;
+ break;
+ case FT_BYTE:
+ *((uint8*)(&dataTable[offset])) = RecordGetUInt8(rawRecord, x, z);
+ offset += 1;
+ break;
+ case FT_SHORT:
+ *((uint16*)(&dataTable[offset])) = RecordGetUInt16(rawRecord, x, z);
+ offset += 2;
+ break;
+ case FT_STRING:
+ case FT_STRING_NOT_LOCALIZED:
+ {
+ // init db2 string field slots by pointers to string holders
+ char const*** slot = (char const***)(&dataTable[offset]);
+ *slot = (char const**)(&stringHoldersPool[stringHoldersRecordPoolSize * y + stringFieldOffset]);
+ if (_loadInfo->TypesString[fieldIndex] == FT_STRING)
+ stringFieldOffset += sizeof(LocalizedString);
+ else
+ stringFieldOffset += sizeof(char*);
+
+ offset += sizeof(char*);
+ break;
+ }
+ default:
+ ASSERT(false, "Unknown format character '%c' found in %s meta", _loadInfo->TypesString[x], fileName);
+ break;
+ }
+ ++fieldIndex;
+ }
+ }
+ }
+
+ return dataTable;
+}
+
+char* DB2FileLoaderRegularImpl::AutoProduceStrings(char* dataTable, uint32 locale)
+{
+ if (_loadInfo->Meta->FieldCount != _header->FieldCount)
+ return nullptr;
+
+ if (!(_header->Locale & (1 << locale)))
+ {
+ char const* sep = "";
+ std::ostringstream str;
+ for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
+ {
+ if (_header->Locale & (1 << i))
+ {
+ str << sep << localeNames[i];
+ sep = ", ";
+ }
+ }
+
+ TC_LOG_ERROR("", "Attempted to load %s which has locales %s as %s. Check if you placed your localized db2 files in correct directory.", fileName, str.str().c_str(), localeNames[locale]);
+ return nullptr;
+ }
+
+ char* stringPool = new char[_header->StringTableSize];
+ memcpy(stringPool, stringTable, _header->StringTableSize);
+
+ uint32 offset = 0;
+
+ for (uint32 y = 0; y < _header->RecordCount; y++)
+ {
+ unsigned char const* rawRecord = GetRawRecordData(y);
+ uint32 fieldIndex = 0;
+ if (!_loadInfo->Meta->HasIndexFieldInData())
+ {
+ offset += 4;
+ ++fieldIndex;
+ }
+
+ for (uint32 x = 0; x < _header->FieldCount; ++x)
+ {
+ for (uint32 z = 0; z < _loadInfo->Meta->ArraySizes[x]; ++z)
+ {
+ switch (_loadInfo->TypesString[fieldIndex])
+ {
+ case FT_FLOAT:
+ case FT_INT:
+ offset += 4;
+ break;
+ case FT_BYTE:
+ offset += 1;
+ break;
+ case FT_SHORT:
+ offset += 2;
+ break;
+ case FT_STRING:
+ {
+ // fill only not filled entries
+ LocalizedString* db2str = *(LocalizedString**)(&dataTable[offset]);
+ if (db2str->Str[locale] == nullStr)
+ {
+ char const* st = RecordGetString(rawRecord, x, z);
+ db2str->Str[locale] = stringPool + (st - (char const*)stringTable);
+ }
+
+ offset += sizeof(char*);
+ break;
+ }
+ case FT_STRING_NOT_LOCALIZED:
+ {
+ char** db2str = (char**)(&dataTable[offset]);
+ char const* st = RecordGetString(rawRecord, x, z);
+ *db2str = stringPool + (st - (char const*)stringTable);
+ offset += sizeof(char*);
+ break;
+ }
+ default:
+ ASSERT(false, "Unknown format character '%c' found in %s meta", _loadInfo->TypesString[x], fileName);
+ break;
+ }
+ ++fieldIndex;
+ }
+ }
+ }
+
+ return stringPool;
+}
+
+void DB2FileLoaderRegularImpl::AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable)
+{
+ uint32 recordsize = _loadInfo->Meta->GetRecordSize();
+ uint32 offset = _header->RecordCount * recordsize;
+ for (uint32 c = 0; c < GetRecordCopyCount(); ++c)
+ {
+ DB2RecordCopy copy = GetRecordCopy(c);
+ if (copy.SourceRowId && copy.SourceRowId < records && copy.NewRowId < records && indexTable[copy.SourceRowId])
+ {
+ indexTable[copy.NewRowId] = &dataTable[offset];
+ memcpy(indexTable[copy.NewRowId], indexTable[copy.SourceRowId], recordsize);
+
+ if (_loadInfo->Meta->HasIndexFieldInData())
+ *((uint32*)(&dataTable[offset + GetFieldOffset(_loadInfo->Meta->GetIndexField())])) = copy.NewRowId;
+ else
+ *((uint32*)(&dataTable[offset])) = copy.NewRowId;
+
+ offset += recordsize;
+ }
+ }
+}
+
+DB2Record DB2FileLoaderRegularImpl::GetRecord(uint32 recordNumber) const
+{
+ return DB2Record(*this, recordNumber, nullptr);
+}
+
+DB2RecordCopy DB2FileLoaderRegularImpl::GetRecordCopy(uint32 copyNumber) const
+{
+ if (copyNumber >= GetRecordCopyCount())
+ return DB2RecordCopy{};
+
+ return copyTable[copyNumber];
+}
+
+uint32 DB2FileLoaderRegularImpl::GetRecordCount() const
+{
+ return _header->RecordCount;
+}
+
+uint32 DB2FileLoaderRegularImpl::GetRecordCopyCount() const
+{
+ return _header->CopyTableSize / sizeof(DB2RecordCopy);
+}
+
+unsigned char const* DB2FileLoaderRegularImpl::GetRawRecordData(uint32 recordNumber) const
+{
+ if (recordNumber >= _header->RecordCount)
+ return nullptr;
+
+ return data + recordNumber * _header->RecordSize;
+}
+
+uint32 DB2FileLoaderRegularImpl::RecordGetId(unsigned char const* record, uint32 recordIndex) const
+{
+ if (_loadInfo->Meta->HasIndexFieldInData())
+ return RecordGetVarInt(record, _loadInfo->Meta->GetIndexField(), 0, false);
+
+ return (reinterpret_cast<uint32 const*>(idTable))[recordIndex];
+}
+
+uint8 DB2FileLoaderRegularImpl::RecordGetUInt8(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ ASSERT(field < _header->FieldCount);
+ ASSERT(GetFieldSize(field) == 1);
+ return *reinterpret_cast<uint8 const*>(record + GetFieldOffset(field) + arrayIndex * sizeof(uint8));
+}
+
+uint16 DB2FileLoaderRegularImpl::RecordGetUInt16(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ ASSERT(field < _header->FieldCount);
+ ASSERT(GetFieldSize(field) == 2);
+ uint16 val = *reinterpret_cast<uint16 const*>(record + GetFieldOffset(field) + arrayIndex * sizeof(uint16));
+ EndianConvert(val);
+ return val;
+}
+
+uint32 DB2FileLoaderRegularImpl::RecordGetUInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ return RecordGetVarInt(record, field, arrayIndex, false);
+}
+
+int32 DB2FileLoaderRegularImpl::RecordGetInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ return int32(RecordGetVarInt(record, field, arrayIndex, true));
+}
+
+float DB2FileLoaderRegularImpl::RecordGetFloat(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ ASSERT(field < _header->FieldCount);
+ float val = *reinterpret_cast<float const*>(record + GetFieldOffset(field) + arrayIndex * sizeof(float));
+ EndianConvert(val);
+ return val;
+}
+
+char const* DB2FileLoaderRegularImpl::RecordGetString(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ ASSERT(field < _header->FieldCount);
+ uint32 stringOffset = *reinterpret_cast<uint32 const*>(record + GetFieldOffset(field) + arrayIndex * sizeof(uint32));
+ EndianConvert(stringOffset);
+ ASSERT(stringOffset < _header->StringTableSize);
+ return reinterpret_cast<char*>(stringTable + stringOffset);
+}
+
+uint32 DB2FileLoaderRegularImpl::RecordGetVarInt(unsigned char const* record, uint32 field, uint32 arrayIndex, bool isSigned) const
+{
+ ASSERT(field < _header->FieldCount);
+ uint32 val = *reinterpret_cast<uint32 const*>(record + GetFieldOffset(field) + arrayIndex * (4 - fields[field].UnusedBits / 8));
+ EndianConvert(val);
+ if (isSigned)
+ return int32(val) << fields[field].UnusedBits >> fields[field].UnusedBits;
+
+ return val << fields[field].UnusedBits >> fields[field].UnusedBits;
+}
+
+uint16 DB2FileLoaderRegularImpl::GetFieldOffset(uint32 field) const
+{
+ ASSERT(field < _header->FieldCount);
+ return fields[field].Offset;
+}
+
+uint16 DB2FileLoaderRegularImpl::GetFieldSize(uint32 field) const
+{
+ ASSERT(field < _header->FieldCount);
+ return 4 - fields[field].UnusedBits / 8;
+}
+
+std::size_t* DB2FileLoaderRegularImpl::RecordCreateDetachedFieldOffsets(std::size_t* /*oldOffsets*/) const
+{
+ return nullptr;
+}
+
+void DB2FileLoaderRegularImpl::RecordDestroyFieldOffsets(std::size_t*& /*fieldOffsets*/) const
+{
+}
+
+uint32 DB2FileLoaderRegularImpl::GetMaxId() const
+{
+ uint32 maxId = 0;
+ for (uint32 row = 0; row < _header->RecordCount; ++row)
+ {
+ uint32 id = RecordGetId(GetRawRecordData(row), row);
+ if (id > maxId)
+ maxId = id;
+ }
+
+ for (uint32 copy = 0; copy < GetRecordCopyCount(); ++copy)
+ {
+ uint32 id = GetRecordCopy(copy).NewRowId;
+ if (id > maxId)
+ maxId = id;
+ }
+
+ return maxId;
+}
+
+DB2FileLoaderSparseImpl::DB2FileLoaderSparseImpl()
+{
+ fileName = nullptr;
+ _loadInfo = nullptr;
+ _fieldAndArrayOffsets = nullptr;
+ _header = nullptr;
+ fields = nullptr;
+ dataStart = 0;
+ data = nullptr;
+ offsets = nullptr;
+}
+
+DB2FileLoaderSparseImpl::~DB2FileLoaderSparseImpl()
+{
+ delete[] _fieldAndArrayOffsets;
+ delete[] data;
+ delete[] offsets;
+ delete[] fields;
+}
+
+bool DB2FileLoaderSparseImpl::Load(DB2FileSource* source, DB2FileLoadInfo const* loadInfo, DB2Header const* header)
+{
+ _loadInfo = loadInfo;
+ _header = header;
+ fileName = source->GetFileName();
+
+ fields = new FieldEntry[header->FieldCount];
+ if (!source->Read(fields, header->FieldCount * sizeof(FieldEntry)))
+ return false;
+
+ dataStart = source->GetPosition();
+
+ data = new unsigned char[header->StringTableSize - dataStart];
+
+ if (!source->Read(data, header->StringTableSize - dataStart))
+ return false;
+
+ offsets = new OffsetTableEntry[header->MaxId - header->MinId + 1];
+ if (!source->Read(offsets, (header->MaxId - header->MinId + 1) * 6))
+ return false;
+
+ _fieldAndArrayOffsets = new std::size_t[_loadInfo->Meta->FieldCount + _loadInfo->FieldCount - (!_loadInfo->Meta->HasIndexFieldInData() ? 1 : 0)];
+ return true;
+}
+
+char* DB2FileLoaderSparseImpl::AutoProduceData(uint32& maxId, char**& indexTable, std::vector<char*>& stringPool)
+{
+ if (_loadInfo->Meta->FieldCount != _header->FieldCount)
+ return NULL;
+
+ //get struct size and index pos
+ uint32 recordsize = _loadInfo->Meta->GetRecordSize();
+
+ uint32 offsetCount = _header->MaxId - _header->MinId + 1;
+ uint32 records = 0;
+ uint32 expandedDataSize = 0;
+ for (uint32 i = 0; i < offsetCount; ++i)
+ {
+ if (offsets[i].FileOffset && offsets[i].RecordSize)
+ {
+ ++records;
+ expandedDataSize += offsets[i].RecordSize;
+ }
+ }
+
+ using index_entry_t = char*;
+
+ maxId = _header->MaxId + 1;
+ indexTable = new index_entry_t[maxId];
+ memset(indexTable, 0, maxId * sizeof(index_entry_t));
+
+ char* dataTable = new char[records * recordsize];
+
+ // we store flat holders pool as single memory block
+ std::size_t stringFields = _loadInfo->GetStringFieldCount(false);
+ std::size_t localizedStringFields = _loadInfo->GetStringFieldCount(true);
+
+ // each string field at load have array of string for each locale
+ std::size_t stringHoldersRecordPoolSize = localizedStringFields * sizeof(LocalizedString) + (stringFields - localizedStringFields) * sizeof(char*);
+ std::size_t stringHoldersPoolSize = stringHoldersRecordPoolSize * records;
+
+ char* stringHoldersPool = new char[stringHoldersPoolSize];
+ stringPool.push_back(stringHoldersPool);
+
+ // DB2 strings expected to have at least empty string
+ for (std::size_t i = 0; i < stringHoldersPoolSize / sizeof(char*); ++i)
+ ((char const**)stringHoldersPool)[i] = nullStr;
+
+ char* stringTable = new char[expandedDataSize - records * ((recordsize - (!_loadInfo->Meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*))];
+ memset(stringTable, 0, expandedDataSize - records * ((recordsize - (!_loadInfo->Meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*)));
+ stringPool.push_back(stringTable);
+ char* stringPtr = stringTable;
+
+ uint32 offset = 0;
+ uint32 recordNum = 0;
+ for (uint32 y = 0; y < offsetCount; ++y)
+ {
+ unsigned char const* rawRecord = GetRawRecordData(y);
+ if (!rawRecord)
+ continue;
+
+ uint32 indexVal = RecordGetId(rawRecord, y);
+ indexTable[indexVal] = &dataTable[offset];
+
+ uint32 fieldIndex = 0;
+ if (!_loadInfo->Meta->HasIndexFieldInData())
+ {
+ *((uint32*)(&dataTable[offset])) = indexVal;
+ offset += 4;
+ ++fieldIndex;
+ }
+
+ uint32 stringFieldOffset = 0;
+ for (uint32 x = 0; x < _header->FieldCount; ++x)
+ {
+ for (uint32 z = 0; z < _loadInfo->Meta->ArraySizes[x]; ++z)
+ {
+ switch (_loadInfo->TypesString[fieldIndex])
+ {
+ case FT_FLOAT:
+ *((float*)(&dataTable[offset])) = RecordGetFloat(rawRecord, x, z);
+ offset += 4;
+ break;
+ case FT_INT:
+ *((uint32*)(&dataTable[offset])) = RecordGetVarInt(rawRecord, x, z, _loadInfo->Fields[fieldIndex].IsSigned);
+ offset += 4;
+ break;
+ case FT_BYTE:
+ *((uint8*)(&dataTable[offset])) = RecordGetUInt8(rawRecord, x, z);
+ offset += 1;
+ break;
+ case FT_SHORT:
+ *((uint16*)(&dataTable[offset])) = RecordGetUInt16(rawRecord, x, z);
+ offset += 2;
+ break;
+ case FT_STRING:
+ {
+ LocalizedString** slot = (LocalizedString**)(&dataTable[offset]);
+ *slot = (LocalizedString*)(&stringHoldersPool[stringHoldersRecordPoolSize * recordNum + stringFieldOffset]);
+ for (uint32 locale = 0; locale < TOTAL_LOCALES; ++locale)
+ if (_header->Locale & (1 << locale))
+ (*slot)->Str[locale] = stringPtr;
+ strcpy(stringPtr, RecordGetString(rawRecord, x, z));
+ stringPtr += strlen(stringPtr) + 1;
+ stringFieldOffset += sizeof(LocalizedString);
+ offset += sizeof(LocalizedString*);
+ break;
+ }
+ case FT_STRING_NOT_LOCALIZED:
+ {
+ char const*** slot = (char const***)(&dataTable[offset]);
+ *slot = (char const**)(&stringHoldersPool[stringHoldersRecordPoolSize * recordNum + stringFieldOffset]);
+ **slot = stringPtr;
+ strcpy(stringPtr, RecordGetString(rawRecord, x, z));
+ stringPtr += strlen(stringPtr) + 1;
+ stringFieldOffset += sizeof(char*);
+ offset += sizeof(char*);
+ break;
+ }
+ default:
+ ASSERT(false, "Unknown format character '%c' found in %s meta", _loadInfo->TypesString[x], fileName);
+ break;
+ }
+ ++fieldIndex;
+ }
+ }
+
+ ++recordNum;
+ }
+
+ return dataTable;
+}
+
+char* DB2FileLoaderSparseImpl::AutoProduceStrings(char* dataTable, uint32 locale)
+{
+ if (_loadInfo->Meta->FieldCount != _header->FieldCount)
+ return nullptr;
+
+ if (!(_header->Locale & (1 << locale)))
+ {
+ char const* sep = "";
+ std::ostringstream str;
+ for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
+ {
+ if (_header->Locale & (1 << i))
+ {
+ str << sep << localeNames[i];
+ sep = ", ";
+ }
+ }
+
+ TC_LOG_ERROR("", "Attempted to load %s which has locales %s as %s. Check if you placed your localized db2 files in correct directory.", fileName, str.str().c_str(), localeNames[locale]);
+ return nullptr;
+ }
+
+ uint32 offsetCount = _header->MaxId - _header->MinId + 1;
+ uint32 records = 0;
+ for (uint32 i = 0; i < offsetCount; ++i)
+ if (offsets[i].FileOffset && offsets[i].RecordSize)
+ ++records;
+
+ uint32 recordsize = _loadInfo->Meta->GetRecordSize();
+ std::size_t stringFields = _loadInfo->GetStringFieldCount(true);
+ char* stringTable = new char[_header->StringTableSize - dataStart - records * ((recordsize - (!_loadInfo->Meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*))];
+ memset(stringTable, 0, _header->StringTableSize - dataStart - records * ((recordsize - (!_loadInfo->Meta->HasIndexFieldInData() ? 4 : 0)) - stringFields * sizeof(char*)));
+ char* stringPtr = stringTable;
+
+ uint32 offset = 0;
+
+ for (uint32 y = 0; y < offsetCount; y++)
+ {
+ unsigned char const* rawRecord = GetRawRecordData(y);
+ if (!rawRecord)
+ continue;
+
+ uint32 fieldIndex = 0;
+ if (!_loadInfo->Meta->HasIndexFieldInData())
+ {
+ offset += 4;
+ ++fieldIndex;
+ }
+
+ for (uint32 x = 0; x < _header->FieldCount; ++x)
+ {
+ for (uint32 z = 0; z < _loadInfo->Meta->ArraySizes[x]; ++z)
+ {
+ switch (_loadInfo->TypesString[fieldIndex])
+ {
+ case FT_FLOAT:
+ offset += 4;
+ break;
+ case FT_INT:
+ offset += 4;
+ break;
+ case FT_BYTE:
+ offset += 1;
+ break;
+ case FT_SHORT:
+ offset += 2;
+ break;
+ case FT_STRING:
+ {
+ LocalizedString* db2str = *(LocalizedString**)(&dataTable[offset]);
+ db2str->Str[locale] = stringPtr;
+ strcpy(stringPtr, RecordGetString(rawRecord, x, z));
+ stringPtr += strlen(stringPtr) + 1;
+ offset += sizeof(char*);
+ break;
+ }
+ case FT_STRING_NOT_LOCALIZED:
+ offset += sizeof(char*);
+ break;
+ default:
+ ASSERT(false, "Unknown format character '%c' found in %s meta", _loadInfo->TypesString[x], fileName);
+ break;
+ }
+ ++fieldIndex;
+ }
+ }
+ }
+
+ return stringTable;
+}
+
+DB2Record DB2FileLoaderSparseImpl::GetRecord(uint32 recordNumber) const
+{
+ return DB2Record(*this, recordNumber, _fieldAndArrayOffsets);
+}
+
+DB2RecordCopy DB2FileLoaderSparseImpl::GetRecordCopy(uint32 /*recordId*/) const
+{
+ return DB2RecordCopy{};
+}
+
+uint32 DB2FileLoaderSparseImpl::GetRecordCount() const
+{
+ return _header->MaxId - _header->MinId + 1;
+}
+
+uint32 DB2FileLoaderSparseImpl::GetRecordCopyCount() const
+{
+ return 0;
+}
+
+unsigned char const* DB2FileLoaderSparseImpl::GetRawRecordData(uint32 recordNumber) const
+{
+ if (recordNumber > (_header->MaxId - _header->MinId) || !offsets[recordNumber].FileOffset || !offsets[recordNumber].RecordSize)
+ return nullptr;
+
+ unsigned char const* rawRecord = &data[offsets[recordNumber].FileOffset - dataStart];
+ CalculateAndStoreFieldOffsets(rawRecord);
+ return rawRecord;
+}
+
+uint32 DB2FileLoaderSparseImpl::RecordGetId(unsigned char const* record, uint32 recordIndex) const
+{
+ if (_loadInfo->Meta->HasIndexFieldInData())
+ return RecordGetVarInt(record, _loadInfo->Meta->GetIndexField(), 0, false);
+
+ return _header->MinId + recordIndex;
+}
+
+uint8 DB2FileLoaderSparseImpl::RecordGetUInt8(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ ASSERT(field < _header->FieldCount);
+ ASSERT(GetFieldSize(field) == 1);
+ return *reinterpret_cast<uint8 const*>(record + GetFieldOffset(field, arrayIndex));
+}
+
+uint16 DB2FileLoaderSparseImpl::RecordGetUInt16(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ ASSERT(field < _header->FieldCount);
+ ASSERT(GetFieldSize(field) == 2);
+ uint16 val = *reinterpret_cast<uint16 const*>(record + GetFieldOffset(field, arrayIndex));
+ EndianConvert(val);
+ return val;
+}
+
+uint32 DB2FileLoaderSparseImpl::RecordGetUInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ return RecordGetVarInt(record, field, arrayIndex, false);
+}
+
+int32 DB2FileLoaderSparseImpl::RecordGetInt32(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ return int32(RecordGetVarInt(record, field, arrayIndex, true));
+}
+
+float DB2FileLoaderSparseImpl::RecordGetFloat(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ ASSERT(field < _header->FieldCount);
+ float val = *reinterpret_cast<float const*>(record + GetFieldOffset(field, arrayIndex));
+ EndianConvert(val);
+ return val;
+}
+
+char const* DB2FileLoaderSparseImpl::RecordGetString(unsigned char const* record, uint32 field, uint32 arrayIndex) const
+{
+ ASSERT(field < _header->FieldCount);
+ return reinterpret_cast<char const*>(record + GetFieldOffset(field, arrayIndex));
+}
+
+uint32 DB2FileLoaderSparseImpl::RecordGetVarInt(unsigned char const* record, uint32 field, uint32 arrayIndex, bool isSigned) const
+{
+ ASSERT(field < _header->FieldCount);
+ uint32 val = *reinterpret_cast<uint32 const*>(record + GetFieldOffset(field, arrayIndex));
+ EndianConvert(val);
+ if (isSigned)
+ return int32(val) << fields[field].UnusedBits >> fields[field].UnusedBits;
+
+ return val << fields[field].UnusedBits >> fields[field].UnusedBits;
+}
+
+uint16 DB2FileLoaderSparseImpl::GetFieldOffset(uint32 field, uint32 arrayIndex) const
+{
+ return _fieldAndArrayOffsets[_fieldAndArrayOffsets[field] + arrayIndex];
+}
+
+uint16 DB2FileLoaderSparseImpl::GetFieldSize(uint32 field) const
+{
+ ASSERT(field < _header->FieldCount);
+ return 4 - fields[field].UnusedBits / 8;
+}
+
+std::size_t* DB2FileLoaderSparseImpl::RecordCreateDetachedFieldOffsets(std::size_t* oldOffsets) const
+{
+ if (oldOffsets != _fieldAndArrayOffsets)
+ return oldOffsets;
+
+ uint32 size = _loadInfo->Meta->FieldCount + _loadInfo->FieldCount - (!_loadInfo->Meta->HasIndexFieldInData() ? 1 : 0);
+ std::size_t* newOffsets = new std::size_t[size];
+ memcpy(newOffsets, _fieldAndArrayOffsets, size * sizeof(std::size_t));
+ return newOffsets;
+}
+
+void DB2FileLoaderSparseImpl::RecordDestroyFieldOffsets(std::size_t*& fieldOffsets) const
+{
+ if (fieldOffsets == _fieldAndArrayOffsets)
+ return;
+
+ delete[] fieldOffsets;
+ fieldOffsets = nullptr;
+}
+
+void DB2FileLoaderSparseImpl::CalculateAndStoreFieldOffsets(unsigned char const* rawRecord) const
+{
+ std::size_t offset = 0;
+ uint32 combinedField = _loadInfo->Meta->FieldCount;
+ for (uint32 field = 0; field < _loadInfo->Meta->FieldCount; ++field)
+ {
+ _fieldAndArrayOffsets[field] = combinedField;
+ for (uint32 arr = 0; arr < _loadInfo->Meta->ArraySizes[field]; ++arr)
+ {
+ _fieldAndArrayOffsets[combinedField] = offset;
+ switch (_loadInfo->Meta->Types[field])
+ {
+ case FT_BYTE:
+ case FT_SHORT:
+ case FT_INT:
+ offset += GetFieldSize(field);
+ break;
+ case FT_FLOAT:
+ offset += sizeof(float);
+ break;
+ case FT_STRING:
+ offset += strlen(reinterpret_cast<char const*>(rawRecord) + offset) + 1;
+ break;
+ default:
+ ASSERT(false, "Unknown format character '%c' found in %s meta", _loadInfo->Meta->Types[field], fileName);
+ break;
+ }
+ ++combinedField;
+ }
+ }
+}
+
+uint32 DB2FileLoaderSparseImpl::GetMaxId() const
+{
+ return _header->MaxId;
+}
+
+DB2Record::DB2Record(DB2FileLoaderImpl const& db2, uint32 recordIndex, std::size_t* fieldOffsets)
+ : _db2(db2), _recordIndex(recordIndex), _recordData(db2.GetRawRecordData(recordIndex)), _fieldOffsets(fieldOffsets)
+{
+}
+
+DB2Record::~DB2Record()
+{
+ _db2.RecordDestroyFieldOffsets(_fieldOffsets);
+}
+
+DB2Record::operator bool()
+{
+ return _recordData != nullptr;
+}
+
+uint32 DB2Record::GetId() const
+{
+ return _db2.RecordGetId(_recordData, _recordIndex);
+}
+
+uint8 DB2Record::GetUInt8(uint32 field, uint32 arrayIndex) const
+{
+ return _db2.RecordGetUInt8(_recordData, field, arrayIndex);
+}
+
+uint16 DB2Record::GetUInt16(uint32 field, uint32 arrayIndex) const
+{
+ return _db2.RecordGetUInt16(_recordData, field, arrayIndex);
+}
+
+uint32 DB2Record::GetUInt32(uint32 field, uint32 arrayIndex) const
+{
+ return _db2.RecordGetUInt32(_recordData, field, arrayIndex);
+}
+
+int32 DB2Record::GetInt32(uint32 field, uint32 arrayIndex) const
+{
+ return _db2.RecordGetInt32(_recordData, field, arrayIndex);
+}
+
+float DB2Record::GetFloat(uint32 field, uint32 arrayIndex) const
+{
+ return _db2.RecordGetFloat(_recordData, field, arrayIndex);
+}
+
+char const* DB2Record::GetString(uint32 field, uint32 arrayIndex) const
+{
+ return _db2.RecordGetString(_recordData, field, arrayIndex);
+}
+
+void DB2Record::MakePersistent()
+{
+ _fieldOffsets = _db2.RecordCreateDetachedFieldOffsets(_fieldOffsets);
+}
+
+DB2FileLoader::DB2FileLoader() : _impl(nullptr)
+{
+}
+
+DB2FileLoader::~DB2FileLoader()
+{
+ delete _impl;
+}
+
+bool DB2FileLoader::Load(DB2FileSource* source, DB2FileLoadInfo const* loadInfo)
+{
+ if (!source->IsOpen())
+ return false;
+
+ if (!source->Read(&_header, sizeof(DB2Header)))
+ return false;
+
+ EndianConvert(_header.Signature);
+ EndianConvert(_header.RecordCount);
+ EndianConvert(_header.FieldCount);
+ EndianConvert(_header.RecordSize);
+ EndianConvert(_header.StringTableSize);
+ EndianConvert(_header.TableHash);
+ EndianConvert(_header.LayoutHash);
+ EndianConvert(_header.MinId);
+ EndianConvert(_header.MaxId);
+ EndianConvert(_header.Locale);
+ EndianConvert(_header.CopyTableSize);
+ EndianConvert(_header.Flags);
+ EndianConvert(_header.IndexField);
+
+ if (_header.Signature != 0x35424457) //'WDB5'
+ return false;
+
+ if (_header.LayoutHash != loadInfo->Meta->LayoutHash)
+ return false;
+
+ if (!(_header.Flags & 0x1))
+ _impl = new DB2FileLoaderRegularImpl();
+ else
+ _impl = new DB2FileLoaderSparseImpl();
+
+ return _impl->Load(source, loadInfo, &_header);
+}
+
+char* DB2FileLoader::AutoProduceData(uint32& count, char**& indexTable, std::vector<char*>& stringPool)
+{
+ return _impl->AutoProduceData(count, indexTable, stringPool);
+}
+
+char* DB2FileLoader::AutoProduceStrings(char* dataTable, uint32 locale)
+{
+ return _impl->AutoProduceStrings(dataTable, locale);
+}
+
+void DB2FileLoader::AutoProduceRecordCopies(uint32 records, char** indexTable, char* dataTable)
+{
+ _impl->AutoProduceRecordCopies(records, indexTable, dataTable);
+}
+
+uint32 DB2FileLoader::GetRecordCount() const
+{
+ return _impl->GetRecordCount();
+}
+
+uint32 DB2FileLoader::GetRecordCopyCount() const
+{
+ return _impl->GetRecordCopyCount();
+}
+
+uint32 DB2FileLoader::GetMaxId() const
+{
+ return _impl->GetMaxId();
+}
+
+DB2Record DB2FileLoader::GetRecord(uint32 recordNumber) const
+{
+ return _impl->GetRecord(recordNumber);
+}
+
+DB2RecordCopy DB2FileLoader::GetRecordCopy(uint32 copyNumber) const
+{
+ return _impl->GetRecordCopy(copyNumber);
+}