/******************************************************************************* The content of this file includes portions of the AUDIOKINETIC Wwise Technology released in source code form as part of the SDK installer package. Commercial License Usage Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology may use this file in accordance with the end user license agreement provided with the software or, alternatively, in accordance with the terms contained in a written agreement between you and Audiokinetic Inc. Apache License Usage Alternatively, this file may be used under the Apache License, Version 2.0 (the "Apache License"); you may not use this file except in compliance with the Apache License. You may obtain a copy of the Apache License at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the Apache License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Apache License for the specific language governing permissions and limitations under the License. Copyright (c) 2023 Audiokinetic Inc. *******************************************************************************/ #pragma once #include #include template class AkStringData : public TAlloc { public: AkStringData() : pStr(NULL), bOwner(false) {} AkStringData(const T_CHAR* in_pStr) : pStr(in_pStr), bOwner(false) {} ~AkStringData() { Term(); } void Term() { if (pStr && bOwner) { TAlloc::Free((void*)pStr); bOwner = false; } pStr = nullptr; } void ClearReference() { if (!bOwner) pStr = nullptr; } protected: const T_CHAR* pStr; bool bOwner; }; template class AkStringImpl : public AkStringData {}; template class AkString: public AkStringImpl { private: typedef AkStringData tData; typedef AkStringImpl tImpl; public: AkString() : AkStringImpl() {} template AkString(const T_CHAR2* in_pStr) { tImpl::Set(in_pStr); } AkString(const AkString& in_other) { tImpl::Set(in_other.Get()); TAlloc::operator=(in_other); } template AkString(const AkString& in_other) { tImpl::Set(in_other.Get()); TAlloc::operator=(in_other); } // if this does not compile it means that both allocation policies are not compatible. // The default assignment behavior is to not create a local copy, unless it is necessary (due to incompatible string types). // Call AllocCopy() if you want to ensure there is a local copy owned by this AkString. AKRESULT AllocCopy() { if (tData::pStr && !tData::bOwner) { const T_CHAR* pRefStr = tData::pStr; AkUInt32 uLen = tImpl::Length(); if (uLen > 0) { tData::pStr = (T_CHAR*)TAlloc::Alloc((uLen + 1) * sizeof(T_CHAR)); if (tData::pStr == NULL) return AK_InsufficientMemory; AKPLATFORM::AkMemCpy((void*)tData::pStr, (void*)pRefStr, ((uLen + 1) * sizeof(T_CHAR))); tData::bOwner = true; } else { tData::pStr = NULL; } } return AK_Success; } // Transfer memory ownership from in_from to this AkString. void Transfer(AkString& in_from) { tData::Term(); TAlloc::TransferMem((void*&) tData::pStr, in_from, (void*)in_from.tData::pStr); in_from.tData::pStr = nullptr; tData::bOwner = in_from.tData::bOwner; in_from.tData::bOwner = false; } const T_CHAR* Get() const { return tData::pStr; } AkString& operator=(const AkString& in_rhs) { tImpl::Set(in_rhs.Get()); TAlloc::operator=(in_rhs); return *this; } template AkString& operator=(const AkString& in_rhs) { tImpl::Set(in_rhs.Get()); TAlloc::operator=(in_rhs); // if this does not compile it means that both allocation policies are not compatible. return *this; } template AkString& operator=(const T_CHAR2* in_pStr) { tImpl::Set(in_pStr); return *this; } }; #ifdef AK_SUPPORT_WCHAR template class AkStringImpl : public AkStringData { private: typedef AkStringData tData; public: AkStringImpl() : AkStringData() {} protected: AKRESULT Set(const char* in_pStr) { tData::Term(); if (in_pStr != NULL) { size_t uLen = strlen(in_pStr); if (uLen > 0) { tData::pStr = (wchar_t*)TAlloc::Alloc((uLen + 1) * sizeof(wchar_t)); if (tData::pStr == NULL) return AK_InsufficientMemory; AKPLATFORM::AkCharToWideChar(in_pStr, (AkUInt32)(uLen + 1), const_cast(tData::pStr)); tData::bOwner = true; } else { tData::pStr = NULL; } } return AK_Success; } AKRESULT Set(const wchar_t* in_pStr) { tData::Term(); tData::pStr = in_pStr; return AK_Success; } public: AkUInt32 Length() const { if (tData::pStr == nullptr) return 0; return (AkUInt32)wcslen(tData::pStr); } }; #endif template class AkStringImpl : public AkStringData { private: typedef AkStringData tData; public: AkStringImpl() : AkStringData() {} protected: AKRESULT Set(const wchar_t* in_pStr) { tData::Term(); if (in_pStr != NULL) { size_t uLen = wcslen(in_pStr); if (uLen > 0) { tData::pStr = (char*)TAlloc::Alloc((uLen + 1) * sizeof(char)); if (tData::pStr == NULL) return AK_InsufficientMemory; AKPLATFORM::AkWideCharToChar(in_pStr, (AkUInt32)(uLen + 1), const_cast(tData::pStr)); tData::bOwner = true; } else { tData::pStr = NULL; } } return AK_Success; } AKRESULT Set(const char* in_pStr) { tData::Term(); tData::pStr = in_pStr; return AK_Success; } public: AkUInt32 Length() const { if (tData::pStr == nullptr) return 0; return (AkUInt32)strlen(tData::pStr); } }; struct AkNonThreaded { AkForceInline void Lock() {} AkForceInline void Unlock() {} }; template static AkForceInline AkUInt32 AkHash(const AkString& in_str) { AkUInt32 uLen = in_str.Length(); if (uLen > 0) { AK::FNVHash32 hash; return hash.Compute(in_str.Get(), uLen * sizeof(T_CHAR)); } return 0; } // // AkDbString - A string reference class that stores a hash to a string in a database. If an identical string is found, the reference count in the database is incremented, // so that we do not store duplicate strings. Database can be made multi thread safe by passing in CAkLock for tLock, or AkNonThreaded if concurrent access is not needed. // template class AkDbString : public TAlloc { public: typedef AkDbString tThis; typedef AkString tString; struct Entry { Entry() : refCount(0) {} tString str; AkInt32 refCount; }; typedef AkHashList tStringTable; struct Instance : public TAlloc { tStringTable table; tLock lock; }; public: // Must be called to initialize the database. static AKRESULT InitDB() { if (pInstance == NULL) { pInstance = (Instance*)pInstance->TAlloc::Alloc(sizeof(Instance)); AkPlacementNew(pInstance) Instance(); return AK_Success; } else return AK_Fail; } // Term the DB. static void TermDB() { if (pInstance != NULL) { pInstance->~Instance(); pInstance->TAlloc::Free(pInstance); pInstance = NULL; } } static void UnlockDB() { pInstance->lock.Unlock();} static void LockDB() { pInstance->lock.Lock(); } private: static Instance* pInstance; public: AkDbString() : m_uHash(0) {} AkDbString(const tThis& in_fromDbStr) : m_uHash(0) { Aquire(in_fromDbStr.m_uHash); } // Construct from AkString template AkDbString(const AkString& in_fromStr) : m_uHash(0) { Aquire(in_fromStr); } tThis& operator=(const tThis& in_rhs) { Aquire(in_rhs.m_uHash); return *this; } // Assign from AkString template tThis& operator=(const AkString& in_rhs) { Aquire(in_rhs); return *this; } // Assign from char string template tThis& operator=(const T_CHAR2* in_rhs) { const AkString& convert = in_rhs; Aquire(convert); return *this; } ~AkDbString() { Release(); } const T_CHAR* Get() const { if (m_uHash != 0) { Entry* pEntry = pInstance->table.Exists(m_uHash); AKASSERT(pEntry != NULL); return pEntry->str.Get(); } return NULL; } protected: template AKRESULT Aquire(const AkString& in_str) { AKRESULT res = AK_Success; Release(); if (in_str.Get() != NULL) { m_uHash = AkHash(in_str); LockDB(); { Entry* pEntry = pInstance->table.Set(m_uHash); if (pEntry != NULL) { pEntry->refCount++; if (pEntry->str.Get() == NULL) { pEntry->str = in_str; pEntry->str.AllocCopy(); if (pEntry->str.Get() == NULL) // Allocation failure { pInstance->table.Unset(m_uHash); m_uHash = 0; res = AK_Fail; } } } else { m_uHash = 0; res = AK_Fail; } } UnlockDB(); } return res; } // in_uHash must have come from another AkDbString, and therefore already exist in the DB. AKRESULT Aquire(AkUInt32 in_uHash) { AKRESULT res = AK_Success; Release(); if (in_uHash != 0) { m_uHash = in_uHash; LockDB(); { Entry* pEntry = pInstance->table.Exists(m_uHash); AKASSERT(pEntry != NULL); pEntry->refCount++; AKASSERT(pEntry->str.Get() != NULL); } UnlockDB(); } return res; } void Release() { if (m_uHash != 0) { LockDB(); { tStringTable& table = pInstance->table; typename tStringTable::IteratorEx it = table.FindEx(m_uHash); AKASSERT(it != table.End());//<- Check that DbString was properly constructed. Entry& entry = (*it).item; AKASSERT(entry.refCount > 0); entry.refCount--; if (entry.refCount == 0) { table.Erase(it); } } UnlockDB(); m_uHash = 0; } } AkUInt32 m_uHash; }; template typename AkDbString::Instance* AkDbString::pInstance = NULL;