summaryrefslogtreecommitdiffstats
path: root/src/foundation/StringTable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/foundation/StringTable.cpp')
-rw-r--r--src/foundation/StringTable.cpp700
1 files changed, 700 insertions, 0 deletions
diff --git a/src/foundation/StringTable.cpp b/src/foundation/StringTable.cpp
new file mode 100644
index 0000000..47130dc
--- /dev/null
+++ b/src/foundation/StringTable.cpp
@@ -0,0 +1,700 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "foundation/StringTable.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Utils.h"
+#include "EASTL/string.h"
+#include "EASTL/sort.h"
+#include "foundation/StrConvertUTF.h"
+#include "foundation/PreAllocatedAllocator.h"
+#include "foundation/SerializationTypes.h"
+#include "foundation/Qt3DSMutex.h"
+
+using namespace qt3ds;
+using namespace qt3ds::foundation;
+using namespace eastl;
+
+namespace eastl {
+struct SCharAndHash
+{
+ const char8_t *m_Data;
+ size_t m_Hash;
+ // Cache the generated hash code.
+ SCharAndHash(const char8_t *inData)
+ : m_Data(inData)
+ , m_Hash(eastl::hash<const char8_t *>()(inData))
+ {
+ }
+ SCharAndHash()
+ : m_Data(nullptr)
+ , m_Hash(0)
+ {
+ }
+ operator const char *() const { return m_Data; }
+};
+template <>
+struct hash<SCharAndHash>
+{
+ size_t operator()(const SCharAndHash &inItem) const { return inItem.m_Hash; }
+};
+
+template <>
+struct equal_to<SCharAndHash>
+{
+ bool operator()(const SCharAndHash &inLhs, const SCharAndHash &inRhs) const
+ {
+ return char_equal_to()(inLhs.m_Data, inRhs.m_Data);
+ }
+};
+}
+
+void CRegisteredString::Remap(const IStringTable &inTable)
+{
+ Remap(const_cast<IStringTable &>(inTable).GetRemapMap());
+}
+
+void CRegisteredString::Remap(const SStrRemapMap &inMap)
+{
+ if (IsValid()) {
+ SStrRemapMap::const_iterator theIter = inMap.find((char8_t *)m_String);
+ if (theIter != inMap.end())
+ m_String = reinterpret_cast<const char8_t *>(theIter->second);
+ else {
+ QT3DS_ASSERT(false);
+ // Ensure a failure here doesn't *guarantee* a crash somewhere else.
+ m_String = nullptr;
+ }
+ } else {
+ // Indicates an invalid string.
+ m_String = reinterpret_cast<const char8_t *>(QT3DS_MAX_U32);
+ }
+}
+
+void CRegisteredString::Remap(NVDataRef<QT3DSU8> inDataPtr)
+{
+ size_t theOffset = reinterpret_cast<size_t>(m_String);
+ if (theOffset >= QT3DS_MAX_U32)
+ m_String = "";
+ else {
+ if (theOffset < inDataPtr.size())
+ m_String = reinterpret_cast<char8_t *>(inDataPtr.begin() + theOffset);
+ else {
+ QT3DS_ASSERT(false);
+ m_String = "";
+ }
+ }
+}
+
+namespace {
+
+const char16_t g_char16EmptyStr[] = { 0, 0, 0, 0 };
+const char32_t g_char32EmptyStr[] = { 0, 0, 0, 0 };
+
+typedef eastl::basic_string<char8_t, ForwardingAllocator> TNarrowStr;
+typedef eastl::basic_string<TWCharEASTLConverter::TCharType, ForwardingAllocator> TWideStr;
+
+inline bool isTrivialWide(const wchar_t *str)
+{
+ return str == nullptr || *str == 0;
+}
+
+QT3DSU8 *AlignPointer(QT3DSU8 *inStart, QT3DSU8 *inPtr)
+{
+ QT3DSU32 alignment = sizeof(void *);
+ size_t numBytes = (size_t)(inPtr - inStart);
+ QT3DS_ASSERT(inStart < inPtr);
+ if (numBytes % alignment)
+ inPtr += alignment - (numBytes % alignment);
+ return inPtr;
+}
+
+QT3DSU32 Align(QT3DSU32 inValue)
+{
+ QT3DSU32 alignment = sizeof(void *);
+ QT3DSU32 leftover = inValue % alignment;
+ if (leftover)
+ inValue += alignment - leftover;
+ return inValue;
+}
+
+// Structure that is written out to the file.
+// Directly after this is the string character data.
+// TWideStr& inConvertStr
+struct SStringFileData
+{
+ QT3DSU32 m_StartOffset;
+ QT3DSU32 m_StrLen;
+ QT3DSU32 m_Handle;
+ char8_t *m_Str;
+ SStringFileData(QT3DSU32 len, QT3DSU32 off, QT3DSU32 handle, char8_t *data)
+ : m_StartOffset(off)
+ , m_StrLen(len)
+ , m_Handle(handle)
+ , m_Str(data)
+ {
+ }
+ SStringFileData()
+ : m_StartOffset(0)
+ , m_StrLen(0)
+ , m_Handle(0)
+ , m_Str(nullptr)
+ {
+ }
+ void Deallocate(NVAllocatorCallback &inAlloc)
+ {
+ inAlloc.deallocate(m_Str);
+ m_Str = nullptr;
+ }
+ const char8_t *GetNarrow() { return m_Str; }
+ operator CRegisteredString() const
+ {
+ return CRegisteredString::ISwearThisHasBeenRegistered(m_Str);
+ }
+ QT3DSU32 EndOffset() const { return m_StartOffset + m_StrLen; }
+
+ // Used only for the lower bound operation to find a given object by offset.
+ bool operator<(const SStringFileData &inOther) const
+ {
+ return m_StartOffset < inOther.m_StartOffset;
+ }
+};
+
+struct SCharAndHandle
+{
+ const char8_t *m_Data;
+ QT3DSU32 m_Handle;
+ SCharAndHandle()
+ : m_Data(nullptr)
+ , m_Handle(0)
+ {
+ }
+ SCharAndHandle(const char8_t *data, QT3DSU32 hdl)
+ : m_Data(data)
+ , m_Handle(hdl)
+ {
+ }
+ operator const char *() const { return m_Data; }
+};
+
+struct SStringFileHeader
+{
+ QT3DSU32 m_NumStrings;
+ QT3DSU32 m_NextHandleValue;
+ // offset to the items of fixed size. Variable sized data is stored
+ // up front.
+ QT3DSU32 m_FixedBufferOffset;
+ SStringFileHeader()
+ : m_NumStrings(0)
+ , m_NextHandleValue(0)
+ , m_FixedBufferOffset(0)
+ {
+ }
+
+ SStringFileHeader(QT3DSU32 ns, QT3DSU32 hv, QT3DSU32 fbo)
+ : m_NumStrings(ns)
+ , m_NextHandleValue(hv)
+ , m_FixedBufferOffset(fbo)
+ {
+ }
+};
+
+// This is the core of the string table.
+struct SStringFileDataList
+{
+ typedef nvhash_map<SCharAndHash, SCharAndHandle> TMapType;
+ typedef nvhash_map<QT3DSU32, const char8_t *> THandleMapType;
+
+ mutable SPreAllocatedAllocator m_Allocator;
+
+ // When we load from a file, we get a these items
+ NVConstDataRef<SStringFileData> m_DataBlock;
+ NVConstDataRef<eastl::pair<size_t, QT3DSU32>> m_HashDataBlockOffset;
+ NVConstDataRef<eastl::pair<QT3DSU32, QT3DSU32>> m_HandleDataBlockOffset;
+
+ // When we are running normally, this items get mangled
+ nvvector<SStringFileData> m_AppendedStrings;
+
+protected:
+ // Built roughly on demand
+ SStrRemapMap m_StrRemapMap;
+ TMapType m_HashToStrMap;
+ THandleMapType m_HandleToStrMap;
+ QT3DSU32 m_NextHandleValue;
+
+public:
+ SStringFileDataList(NVAllocatorCallback &inAlloc)
+ : m_Allocator(inAlloc)
+ , m_AppendedStrings(inAlloc, "StringTable::m_AppendedStrings")
+ , m_StrRemapMap(inAlloc, "StringTable::m_StrRemapMap")
+ , m_HashToStrMap(inAlloc, "StringTable::m_HashToStrMap")
+ , m_HandleToStrMap(inAlloc, "StringTable::m_HashToStrMap")
+ , m_NextHandleValue(1)
+ {
+ }
+ ~SStringFileDataList()
+ {
+ for (QT3DSU32 idx = 0, end = m_AppendedStrings.size(); idx < end; ++idx)
+ m_AppendedStrings[idx].Deallocate(m_Allocator);
+ }
+
+ SStrRemapMap &GetRemapMap()
+ {
+ if (m_StrRemapMap.empty()) {
+ for (QT3DSU32 idx = 0, end = m_DataBlock.size(); idx < end; ++idx)
+ m_StrRemapMap.insert(
+ eastl::make_pair(m_DataBlock[idx].m_Str, m_DataBlock[idx].m_StartOffset));
+ for (QT3DSU32 idx = 0, end = m_AppendedStrings.size(); idx < end; ++idx)
+ m_StrRemapMap.insert(eastl::make_pair(m_AppendedStrings[idx].m_Str,
+ m_AppendedStrings[idx].m_StartOffset));
+ }
+ return m_StrRemapMap;
+ }
+
+ struct SHashPairComparator
+ {
+ bool operator()(const eastl::pair<size_t, QT3DSU32> &lhs,
+ const eastl::pair<size_t, QT3DSU32> &rhs) const
+ {
+ return lhs.first < rhs.first;
+ }
+ };
+
+ struct SHandlePairComparator
+ {
+ bool operator()(const eastl::pair<QT3DSU32, QT3DSU32> &lhs,
+ const eastl::pair<QT3DSU32, QT3DSU32> &rhs) const
+ {
+ return lhs.first < rhs.first;
+ }
+ };
+
+ SCharAndHandle DoFindStr(SCharAndHash hashCode)
+ {
+ if (isTrivial(hashCode.m_Data))
+ return SCharAndHandle();
+ TMapType::iterator theFind = m_HashToStrMap.find(hashCode);
+ if (theFind == m_HashToStrMap.end()) {
+ SCharAndHandle newStr = FindStrByHash(hashCode);
+ if (!newStr.m_Data)
+ newStr = Append(hashCode);
+
+ // It may not be obvious why we have to reset the data member.
+ // we have to do this so the hashtable keys don't change if the user isn't
+ // passing in a persistent string.
+ hashCode.m_Data = newStr;
+
+ theFind = m_HashToStrMap.insert(eastl::make_pair(hashCode, newStr)).first;
+ m_HandleToStrMap.insert(eastl::make_pair(theFind->second.m_Handle, newStr));
+ }
+ return theFind->second;
+ }
+
+ const CRegisteredString FindStr(SCharAndHash hashCode)
+ {
+ SCharAndHandle result = DoFindStr(hashCode);
+ if (result.m_Data)
+ return CRegisteredString::ISwearThisHasBeenRegistered(result.m_Data);
+ return CRegisteredString();
+ }
+
+ const CStringHandle FindStrHandle(SCharAndHash hashCode)
+ {
+ SCharAndHandle result = DoFindStr(hashCode);
+ return CStringHandle::ISwearThisHasBeenRegistered(result.m_Handle);
+ }
+
+ const CRegisteredString FindStrByHandle(QT3DSU32 handle)
+ {
+ if (handle == 0)
+ return CRegisteredString();
+
+ THandleMapType::iterator theFind = m_HandleToStrMap.find(handle);
+ if (theFind == m_HandleToStrMap.end()) {
+ const char8_t *newStr = FindStrByHandleData(handle);
+ if (!newStr)
+ return CRegisteredString();
+ theFind = m_HandleToStrMap.insert(eastl::make_pair(handle, newStr)).first;
+ }
+ return CRegisteredString::ISwearThisHasBeenRegistered(theFind->second);
+ }
+
+ void Save(SWriteBuffer &ioBuffer) const
+ {
+ // Buffer should be aligned before we get to it.
+ QT3DS_ASSERT(ioBuffer.size() % 4 == 0);
+
+ QT3DSU32 numStrs = Size();
+ size_t bufferBegin = ioBuffer.size();
+ SStringFileHeader theHeader;
+ ioBuffer.write(theHeader);
+ QT3DSU32 startOffset = ioBuffer.size();
+
+ eastl::vector<eastl::pair<size_t, QT3DSU32>> hashToStringIndex;
+ eastl::vector<eastl::pair<QT3DSU32, QT3DSU32>> handleToStringIndex;
+ hashToStringIndex.reserve(numStrs);
+ handleToStringIndex.reserve(numStrs);
+ WriteStringBlockData(m_DataBlock.begin(), m_DataBlock.end(), ioBuffer, hashToStringIndex,
+ handleToStringIndex);
+ WriteStringBlockData(m_AppendedStrings.data(),
+ m_AppendedStrings.data() + m_AppendedStrings.size(), ioBuffer,
+ hashToStringIndex, handleToStringIndex);
+ ioBuffer.align(4);
+
+ QT3DSU32 fixedOffset = ioBuffer.size() - startOffset;
+ WriteStringBlock(m_DataBlock.begin(), m_DataBlock.end(), ioBuffer);
+ WriteStringBlock(m_AppendedStrings.data(),
+ m_AppendedStrings.data() + m_AppendedStrings.size(), ioBuffer);
+
+ // sort by hash code so we can do binary lookups later.
+ eastl::sort(hashToStringIndex.begin(), hashToStringIndex.end(), SHashPairComparator());
+ ioBuffer.write(hashToStringIndex.data(), (QT3DSU32)hashToStringIndex.size());
+
+ eastl::sort(handleToStringIndex.begin(), handleToStringIndex.end(),
+ SHandlePairComparator());
+ ioBuffer.write(handleToStringIndex.data(), (QT3DSU32)handleToStringIndex.size());
+
+ SStringFileHeader *fixedOffsetPtr =
+ reinterpret_cast<SStringFileHeader *>(ioBuffer.begin() + bufferBegin);
+ *fixedOffsetPtr = SStringFileHeader(numStrs, m_NextHandleValue, fixedOffset);
+ }
+
+ void Load(NVDataRef<QT3DSU8> inMemory)
+ {
+ QT3DS_ASSERT(m_AppendedStrings.empty());
+ m_Allocator.m_PreAllocatedBlock = inMemory;
+ m_Allocator.m_OwnsMemory = false;
+ SDataReader theReader(inMemory.begin(), inMemory.end());
+ SStringFileHeader theHeader = theReader.LoadRef<SStringFileHeader>();
+ QT3DSU32 numStrs = theHeader.m_NumStrings;
+ m_NextHandleValue = theHeader.m_NextHandleValue;
+ QT3DSU32 fixedOffset = theHeader.m_FixedBufferOffset;
+ theReader.m_CurrentPtr += fixedOffset;
+ m_DataBlock = NVConstDataRef<SStringFileData>(
+ reinterpret_cast<const SStringFileData *>(theReader.m_CurrentPtr), numStrs);
+ theReader.m_CurrentPtr += sizeof(SStringFileData) * numStrs;
+
+ m_HashDataBlockOffset = NVConstDataRef<eastl::pair<size_t, QT3DSU32>>(
+ reinterpret_cast<const eastl::pair<size_t, QT3DSU32> *>(theReader.m_CurrentPtr), numStrs);
+ theReader.m_CurrentPtr += sizeof(eastl::pair<size_t, QT3DSU32>) * numStrs;
+
+ m_HandleDataBlockOffset = NVConstDataRef<eastl::pair<QT3DSU32, QT3DSU32>>(
+ reinterpret_cast<const eastl::pair<QT3DSU32, QT3DSU32> *>(theReader.m_CurrentPtr), numStrs);
+
+ for (QT3DSU32 idx = 0, end = m_DataBlock.size(); idx < end; ++idx) {
+ SStringFileData &theData = const_cast<SStringFileData &>(m_DataBlock[idx]);
+ theData.m_Str = reinterpret_cast<char8_t *>(inMemory.mData + theData.m_StartOffset);
+ }
+ }
+
+protected:
+ // When we load from a file, we do not put every string into the hash because building
+ // hashtables takes
+ // time. We only put them into the hash on demand; this avoids building a giant hashtable upon
+ // load.
+ // and since in general we do not need to lookup all hash values again it avoids building the
+ // hashtable
+ // in general.
+ SCharAndHandle FindStrByHash(SCharAndHash hashCode) const
+ {
+ const eastl::pair<size_t, QT3DSU32> *iter =
+ eastl::lower_bound(m_HashDataBlockOffset.begin(), m_HashDataBlockOffset.end(),
+ eastl::make_pair(hashCode.m_Hash, (QT3DSU32)0), SHashPairComparator());
+
+ for (; iter != m_HashDataBlockOffset.end() && iter->first == hashCode.m_Hash; ++iter) {
+ if (AreEqual(m_DataBlock[iter->second].m_Str, hashCode.m_Data))
+ return SCharAndHandle(m_DataBlock[iter->second].m_Str,
+ m_DataBlock[iter->second].m_Handle);
+ }
+ return SCharAndHandle();
+ }
+
+ const char8_t *FindStrByHandleData(QT3DSU32 handle) const
+ {
+ const eastl::pair<QT3DSU32, QT3DSU32> *iter =
+ eastl::lower_bound(m_HandleDataBlockOffset.begin(), m_HandleDataBlockOffset.end(),
+ eastl::make_pair(handle, (QT3DSU32)0), SHandlePairComparator());
+
+ if (iter != m_HandleDataBlockOffset.end() && iter->first == handle
+ && m_DataBlock[iter->second].m_Handle == handle)
+ return m_DataBlock[iter->second].m_Str;
+ return nullptr;
+ }
+
+ void WriteStringBlockData(const SStringFileData *begin, const SStringFileData *end,
+ SWriteBuffer &ioBuffer,
+ eastl::vector<eastl::pair<size_t, QT3DSU32>> &ioHashToStringIndex,
+ eastl::vector<eastl::pair<QT3DSU32, QT3DSU32>> &ioHandleToStringIndex) const
+ {
+ for (const SStringFileData *iter = begin; iter != end; ++iter) {
+ size_t idx = ioHashToStringIndex.size();
+ SCharAndHash theHash(iter->m_Str);
+ ioHashToStringIndex.push_back(eastl::make_pair(theHash.m_Hash, (QT3DSU32)idx));
+ ioHandleToStringIndex.push_back(eastl::make_pair(iter->m_Handle, (QT3DSU32)idx));
+ ioBuffer.write(iter->m_Str, iter->m_StrLen);
+ }
+ }
+
+ void WriteStringBlock(const SStringFileData *begin, const SStringFileData *end,
+ SWriteBuffer &ioBuffer) const
+ {
+ for (const SStringFileData *iter = begin; iter != end; ++iter)
+ ioBuffer.write(*iter);
+ }
+
+ SCharAndHandle Append(SCharAndHash inStrHash)
+ {
+ if (isTrivial(inStrHash.m_Data))
+ return SCharAndHandle();
+
+ const char8_t *inStr(inStrHash.m_Data);
+ QT3DSU32 len = (QT3DSU32)strlen(inStr) + 1;
+ char8_t *newStr = (char8_t *)m_Allocator.allocate(len, "StringData", __FILE__, __LINE__);
+ memCopy(newStr, inStr, len);
+
+ // We write the number of strings to the file just after the header.
+ QT3DSU32 theOffset = sizeof(SStringFileHeader);
+ if (Size())
+ theOffset = Back().EndOffset();
+
+ QT3DSU32 handleValue = m_NextHandleValue;
+ ++m_NextHandleValue;
+ m_AppendedStrings.push_back(SStringFileData(len, theOffset, handleValue, newStr));
+
+ // Only add to the str map if necessary because building it could be expensive.
+ if (m_StrRemapMap.empty() == false)
+ m_StrRemapMap.insert(eastl::make_pair(newStr, theOffset));
+
+ return SCharAndHandle(newStr, handleValue);
+ }
+ // precondition is that size != 0
+ const SStringFileData &Back() const
+ {
+ QT3DS_ASSERT(Size());
+
+ if (m_AppendedStrings.size()) {
+ return m_AppendedStrings.back();
+ } else {
+ return m_DataBlock[m_DataBlock.size() - 1];
+ }
+ }
+
+ QT3DSU32 Size() const { return static_cast<QT3DSU32>(m_AppendedStrings.size() + m_DataBlock.size()); }
+};
+
+struct SStringTableMutexScope
+{
+ Mutex *m_Mutex;
+ SStringTableMutexScope(Mutex *inM)
+ : m_Mutex(inM)
+ {
+ if (m_Mutex)
+ m_Mutex->lock();
+ }
+ ~SStringTableMutexScope()
+ {
+ if (m_Mutex)
+ m_Mutex->unlock();
+ }
+};
+
+#define STRING_TABLE_MULTITHREADED_METHOD SStringTableMutexScope __locker(m_MultithreadMutex)
+
+class StringTable : public IStringTable
+{
+ typedef nvhash_map<SCharAndHash, QT3DSU32> TMapType;
+ typedef nvhash_map<CRegisteredString, TWideStr> TNarrowToWideMapType;
+ SStringFileDataList m_FileData;
+ NVAllocatorCallback &m_Allocator;
+ TNarrowToWideMapType m_StrWideMap;
+ volatile QT3DSI32 mRefCount; // fnd's naming convention
+ eastl::basic_string<char8_t, ForwardingAllocator> m_ConvertBuffer;
+ TWideStr m_WideConvertBuffer;
+ Mutex m_MultithreadMutexBacker;
+ Mutex *m_MultithreadMutex;
+ // Data that will be written out to the file.
+
+public:
+ StringTable(NVAllocatorCallback &alloc)
+ : m_FileData(alloc)
+ , m_Allocator(m_FileData.m_Allocator)
+ , m_StrWideMap(alloc, "StringTable::m_StrWideMap")
+ , mRefCount(0)
+ , m_ConvertBuffer(ForwardingAllocator(alloc, "StringTable::m_ConvertBuffer"))
+ , m_WideConvertBuffer(ForwardingAllocator(alloc, "StringTable::m_WideConvertBuffer"))
+ , m_MultithreadMutexBacker(alloc)
+ , m_MultithreadMutex(nullptr)
+ {
+ }
+
+ virtual ~StringTable() override {}
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_FileData.m_Allocator.m_Allocator)
+
+ void EnableMultithreadedAccess() override
+ {
+ m_MultithreadMutex = &m_MultithreadMutexBacker;
+ }
+
+ void DisableMultithreadedAccess() override
+ {
+ m_MultithreadMutex = nullptr;
+ }
+
+ CRegisteredString RegisterStr(const QString &str) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ if (str.isEmpty())
+ return CRegisteredString();
+ return RegisterStr(str.toUtf8());
+ }
+
+ CRegisteredString RegisterStr(const QByteArray &str) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ if (str.isEmpty())
+ return CRegisteredString();
+ return RegisterStr(str.constData());
+ }
+
+ CRegisteredString RegisterStr(const eastl::string &string) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ return RegisterStr(string.c_str());
+ }
+
+ CRegisteredString RegisterStr(Qt3DSBCharPtr str) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ if (isTrivial(str))
+ return CRegisteredString();
+ return m_FileData.FindStr(str);
+ }
+
+ // utf-16->utf-8
+ CRegisteredString RegisterStr(const char16_t *str) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ qt3ds::foundation::ConvertUTF(str, 0, m_ConvertBuffer);
+ return RegisterStr(m_ConvertBuffer.c_str());
+ }
+ // utf-32->utf-8
+ CRegisteredString RegisterStr(const char32_t *str) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ qt3ds::foundation::ConvertUTF(str, 0, m_ConvertBuffer);
+ return RegisterStr(m_ConvertBuffer.c_str());
+ }
+
+ CRegisteredString RegisterStr(const wchar_t *str) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ if (isTrivialWide(str))
+ return CRegisteredString();
+ qt3ds::foundation::ConvertUTF(
+ reinterpret_cast<const TWCharEASTLConverter::TCharType *>(str), 0,
+ m_ConvertBuffer);
+ return RegisterStr(m_ConvertBuffer.c_str());
+ }
+
+ CStringHandle GetHandle(Qt3DSBCharPtr str) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ return m_FileData.FindStrHandle(str);
+ }
+
+ CRegisteredString HandleToStr(QT3DSU32 strHandle) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ return m_FileData.FindStrByHandle(strHandle);
+ }
+
+ const wchar_t *GetWideStr(CRegisteredString theStr)
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ eastl::pair<TNarrowToWideMapType::iterator, bool> pair_iter = m_StrWideMap.insert(
+ eastl::make_pair(theStr, TWideStr(ForwardingAllocator(m_Allocator, "WideString"))));
+ if (pair_iter.second)
+ qt3ds::foundation::ConvertUTF(theStr.c_str(), 0, pair_iter.first->second);
+ return reinterpret_cast<const wchar_t *>(pair_iter.first->second.c_str());
+ }
+
+ const wchar_t *GetWideStr(Qt3DSBCharPtr src) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ if (isTrivial(src))
+ return L"";
+ return GetWideStr(RegisterStr(src));
+ }
+
+ const wchar_t *GetWideStr(const wchar_t *str) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ if (isTrivialWide(str))
+ return L"";
+ qt3ds::foundation::ConvertUTF(
+ reinterpret_cast<const TWCharEASTLConverter::TCharType *>(str), 0,
+ m_ConvertBuffer);
+ return GetWideStr(RegisterStr(m_ConvertBuffer.c_str()));
+ }
+
+ const SStrRemapMap &GetRemapMap() override { return m_FileData.GetRemapMap(); }
+
+ NVAllocatorCallback &GetAllocator() override { return m_FileData.m_Allocator.m_Allocator; }
+
+ // Save to a block of memory. It is up the callers to deallocate using allocator
+ // from GetAllocator(). Returns a remap map that takes existing strings and changes their
+ // address such that they are offsets into the block of memory where this object
+ // started saving. Returns a map that converts registered strings into offsets of the
+ // written buffer.
+ void Save(SWriteBuffer &ioBuffer) const override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ m_FileData.Save(ioBuffer);
+ }
+
+ // Load all the strings from inMemory. inMemory is not freed by this object
+ // Finally, you will need to take inMemory and call remap on all strings
+ // from offsets back into their correct address into inMemory.
+ void Load(qt3ds::foundation::NVDataRef<QT3DSU8> inMemory) override
+ {
+ STRING_TABLE_MULTITHREADED_METHOD;
+ m_FileData.Load(inMemory);
+ }
+};
+}
+
+const char16_t *char16EmptyStr = g_char16EmptyStr;
+const char32_t *char32EmptyStr = g_char32EmptyStr;
+
+IStringTable &IStringTable::CreateStringTable(NVAllocatorCallback &alloc)
+{
+ return *QT3DS_NEW(alloc, StringTable)(alloc);
+}