AkString.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. /*******************************************************************************
  2. The content of this file includes portions of the AUDIOKINETIC Wwise Technology
  3. released in source code form as part of the SDK installer package.
  4. Commercial License Usage
  5. Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology
  6. may use this file in accordance with the end user license agreement provided
  7. with the software or, alternatively, in accordance with the terms contained in a
  8. written agreement between you and Audiokinetic Inc.
  9. Apache License Usage
  10. Alternatively, this file may be used under the Apache License, Version 2.0 (the
  11. "Apache License"); you may not use this file except in compliance with the
  12. Apache License. You may obtain a copy of the Apache License at
  13. http://www.apache.org/licenses/LICENSE-2.0.
  14. Unless required by applicable law or agreed to in writing, software distributed
  15. under the Apache License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
  16. OR CONDITIONS OF ANY KIND, either express or implied. See the Apache License for
  17. the specific language governing permissions and limitations under the License.
  18. Copyright (c) 2023 Audiokinetic Inc.
  19. *******************************************************************************/
  20. #pragma once
  21. #include <AK/Tools/Common/AkFNVHash.h>
  22. #include <AK/Tools/Common/AkHashList.h>
  23. template<typename TAlloc, typename T_CHAR>
  24. class AkStringData : public TAlloc
  25. {
  26. public:
  27. AkStringData() : pStr(NULL), bOwner(false) {}
  28. AkStringData(const T_CHAR* in_pStr) : pStr(in_pStr), bOwner(false) {}
  29. ~AkStringData() { Term(); }
  30. void Term()
  31. {
  32. if (pStr && bOwner)
  33. {
  34. TAlloc::Free((void*)pStr);
  35. bOwner = false;
  36. }
  37. pStr = nullptr;
  38. }
  39. void ClearReference()
  40. {
  41. if (!bOwner)
  42. pStr = nullptr;
  43. }
  44. protected:
  45. const T_CHAR* pStr;
  46. bool bOwner;
  47. };
  48. template<typename TAlloc, typename T_CHAR>
  49. class AkStringImpl : public AkStringData<TAlloc, T_CHAR>
  50. {};
  51. template<typename TAlloc, typename T_CHAR>
  52. class AkString: public AkStringImpl<TAlloc, T_CHAR>
  53. {
  54. private:
  55. typedef AkStringData<TAlloc, T_CHAR> tData;
  56. typedef AkStringImpl<TAlloc, T_CHAR> tImpl;
  57. public:
  58. AkString() : AkStringImpl<TAlloc, T_CHAR>() {}
  59. template<typename T_CHAR2>
  60. AkString(const T_CHAR2* in_pStr) { tImpl::Set(in_pStr); }
  61. AkString(const AkString<TAlloc, T_CHAR>& in_other) { tImpl::Set(in_other.Get()); TAlloc::operator=(in_other); }
  62. template<typename TAlloc2, typename T_CHAR2>
  63. AkString(const AkString<TAlloc2, T_CHAR2>& 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.
  64. // The default assignment behavior is to not create a local copy, unless it is necessary (due to incompatible string types).
  65. // Call AllocCopy() if you want to ensure there is a local copy owned by this AkString.
  66. AKRESULT AllocCopy()
  67. {
  68. if (tData::pStr && !tData::bOwner)
  69. {
  70. const T_CHAR* pRefStr = tData::pStr;
  71. AkUInt32 uLen = tImpl::Length();
  72. if (uLen > 0)
  73. {
  74. tData::pStr = (T_CHAR*)TAlloc::Alloc((uLen + 1) * sizeof(T_CHAR));
  75. if (tData::pStr == NULL)
  76. return AK_InsufficientMemory;
  77. AKPLATFORM::AkMemCpy((void*)tData::pStr, (void*)pRefStr, ((uLen + 1) * sizeof(T_CHAR)));
  78. tData::bOwner = true;
  79. }
  80. else
  81. {
  82. tData::pStr = NULL;
  83. }
  84. }
  85. return AK_Success;
  86. }
  87. // Transfer memory ownership from in_from to this AkString.
  88. void Transfer(AkString<TAlloc, T_CHAR>& in_from)
  89. {
  90. tData::Term();
  91. TAlloc::TransferMem((void*&) tData::pStr, in_from, (void*)in_from.tData::pStr);
  92. in_from.tData::pStr = nullptr;
  93. tData::bOwner = in_from.tData::bOwner;
  94. in_from.tData::bOwner = false;
  95. }
  96. const T_CHAR* Get() const
  97. {
  98. return tData::pStr;
  99. }
  100. AkString& operator=(const AkString<TAlloc, T_CHAR>& in_rhs)
  101. {
  102. tImpl::Set(in_rhs.Get());
  103. TAlloc::operator=(in_rhs);
  104. return *this;
  105. }
  106. template<typename TAlloc2, typename T_CHAR2>
  107. AkString& operator=(const AkString<TAlloc2, T_CHAR2>& in_rhs)
  108. {
  109. tImpl::Set(in_rhs.Get());
  110. TAlloc::operator=(in_rhs); // if this does not compile it means that both allocation policies are not compatible.
  111. return *this;
  112. }
  113. template<typename T_CHAR2>
  114. AkString& operator=(const T_CHAR2* in_pStr)
  115. {
  116. tImpl::Set(in_pStr);
  117. return *this;
  118. }
  119. };
  120. #ifdef AK_SUPPORT_WCHAR
  121. template<typename TAlloc>
  122. class AkStringImpl <TAlloc, wchar_t> : public AkStringData<TAlloc, wchar_t>
  123. {
  124. private:
  125. typedef AkStringData<TAlloc, wchar_t> tData;
  126. public:
  127. AkStringImpl() : AkStringData<TAlloc, wchar_t>() {}
  128. protected:
  129. AKRESULT Set(const char* in_pStr)
  130. {
  131. tData::Term();
  132. if (in_pStr != NULL)
  133. {
  134. size_t uLen = strlen(in_pStr);
  135. if (uLen > 0)
  136. {
  137. tData::pStr = (wchar_t*)TAlloc::Alloc((uLen + 1) * sizeof(wchar_t));
  138. if (tData::pStr == NULL)
  139. return AK_InsufficientMemory;
  140. AKPLATFORM::AkCharToWideChar(in_pStr, (AkUInt32)(uLen + 1), const_cast<wchar_t*>(tData::pStr));
  141. tData::bOwner = true;
  142. }
  143. else
  144. {
  145. tData::pStr = NULL;
  146. }
  147. }
  148. return AK_Success;
  149. }
  150. AKRESULT Set(const wchar_t* in_pStr)
  151. {
  152. tData::Term();
  153. tData::pStr = in_pStr;
  154. return AK_Success;
  155. }
  156. public:
  157. AkUInt32 Length() const
  158. {
  159. if (tData::pStr == nullptr)
  160. return 0;
  161. return (AkUInt32)wcslen(tData::pStr);
  162. }
  163. };
  164. #endif
  165. template<typename TAlloc>
  166. class AkStringImpl <TAlloc, char> : public AkStringData<TAlloc, char>
  167. {
  168. private:
  169. typedef AkStringData<TAlloc, char> tData;
  170. public:
  171. AkStringImpl() : AkStringData<TAlloc, char>() {}
  172. protected:
  173. AKRESULT Set(const wchar_t* in_pStr)
  174. {
  175. tData::Term();
  176. if (in_pStr != NULL)
  177. {
  178. size_t uLen = wcslen(in_pStr);
  179. if (uLen > 0)
  180. {
  181. tData::pStr = (char*)TAlloc::Alloc((uLen + 1) * sizeof(char));
  182. if (tData::pStr == NULL)
  183. return AK_InsufficientMemory;
  184. AKPLATFORM::AkWideCharToChar(in_pStr, (AkUInt32)(uLen + 1), const_cast<char*>(tData::pStr));
  185. tData::bOwner = true;
  186. }
  187. else
  188. {
  189. tData::pStr = NULL;
  190. }
  191. }
  192. return AK_Success;
  193. }
  194. AKRESULT Set(const char* in_pStr)
  195. {
  196. tData::Term();
  197. tData::pStr = in_pStr;
  198. return AK_Success;
  199. }
  200. public:
  201. AkUInt32 Length() const
  202. {
  203. if (tData::pStr == nullptr)
  204. return 0;
  205. return (AkUInt32)strlen(tData::pStr);
  206. }
  207. };
  208. struct AkNonThreaded
  209. {
  210. AkForceInline void Lock() {}
  211. AkForceInline void Unlock() {}
  212. };
  213. template<typename TAlloc, typename T_CHAR>
  214. static AkForceInline AkUInt32 AkHash(const AkString<TAlloc, T_CHAR>& in_str)
  215. {
  216. AkUInt32 uLen = in_str.Length();
  217. if (uLen > 0)
  218. {
  219. AK::FNVHash32 hash;
  220. return hash.Compute(in_str.Get(), uLen * sizeof(T_CHAR));
  221. }
  222. return 0;
  223. }
  224. //
  225. // 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,
  226. // 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.
  227. //
  228. template<typename TAlloc, typename T_CHAR, typename tLock = AkNonThreaded>
  229. class AkDbString : public TAlloc
  230. {
  231. public:
  232. typedef AkDbString<TAlloc, T_CHAR, tLock> tThis;
  233. typedef AkString<TAlloc, T_CHAR> tString;
  234. struct Entry
  235. {
  236. Entry() : refCount(0) {}
  237. tString str;
  238. AkInt32 refCount;
  239. };
  240. typedef AkHashList<AkUInt32, Entry, TAlloc> tStringTable;
  241. struct Instance : public TAlloc
  242. {
  243. tStringTable table;
  244. tLock lock;
  245. };
  246. public:
  247. // Must be called to initialize the database.
  248. static AKRESULT InitDB()
  249. {
  250. if (pInstance == NULL)
  251. {
  252. pInstance = (Instance*)pInstance->TAlloc::Alloc(sizeof(Instance));
  253. AkPlacementNew(pInstance) Instance();
  254. return AK_Success;
  255. }
  256. else
  257. return AK_Fail;
  258. }
  259. // Term the DB.
  260. static void TermDB()
  261. {
  262. if (pInstance != NULL)
  263. {
  264. pInstance->~Instance();
  265. pInstance->TAlloc::Free(pInstance);
  266. pInstance = NULL;
  267. }
  268. }
  269. static void UnlockDB() { pInstance->lock.Unlock();}
  270. static void LockDB() { pInstance->lock.Lock(); }
  271. private:
  272. static Instance* pInstance;
  273. public:
  274. AkDbString() : m_uHash(0)
  275. {}
  276. AkDbString(const tThis& in_fromDbStr) : m_uHash(0) { Aquire(in_fromDbStr.m_uHash); }
  277. // Construct from AkString
  278. template<typename TAlloc2, typename T_CHAR2>
  279. AkDbString(const AkString<TAlloc2, T_CHAR2>& in_fromStr) : m_uHash(0) { Aquire(in_fromStr); }
  280. tThis& operator=(const tThis& in_rhs)
  281. {
  282. Aquire(in_rhs.m_uHash);
  283. return *this;
  284. }
  285. // Assign from AkString
  286. template<typename TAlloc2, typename T_CHAR2>
  287. tThis& operator=(const AkString<TAlloc2, T_CHAR2>& in_rhs)
  288. {
  289. Aquire(in_rhs);
  290. return *this;
  291. }
  292. // Assign from char string
  293. template<typename T_CHAR2>
  294. tThis& operator=(const T_CHAR2* in_rhs)
  295. {
  296. const AkString<TAlloc, T_CHAR2>& convert = in_rhs;
  297. Aquire(convert);
  298. return *this;
  299. }
  300. ~AkDbString()
  301. {
  302. Release();
  303. }
  304. const T_CHAR* Get() const
  305. {
  306. if (m_uHash != 0)
  307. {
  308. Entry* pEntry = pInstance->table.Exists(m_uHash);
  309. AKASSERT(pEntry != NULL);
  310. return pEntry->str.Get();
  311. }
  312. return NULL;
  313. }
  314. protected:
  315. template<typename TAlloc2, typename T_CHAR2>
  316. AKRESULT Aquire(const AkString<TAlloc2, T_CHAR2>& in_str)
  317. {
  318. AKRESULT res = AK_Success;
  319. Release();
  320. if (in_str.Get() != NULL)
  321. {
  322. m_uHash = AkHash(in_str);
  323. LockDB();
  324. {
  325. Entry* pEntry = pInstance->table.Set(m_uHash);
  326. if (pEntry != NULL)
  327. {
  328. pEntry->refCount++;
  329. if (pEntry->str.Get() == NULL)
  330. {
  331. pEntry->str = in_str;
  332. pEntry->str.AllocCopy();
  333. if (pEntry->str.Get() == NULL) // Allocation failure
  334. {
  335. pInstance->table.Unset(m_uHash);
  336. m_uHash = 0;
  337. res = AK_Fail;
  338. }
  339. }
  340. }
  341. else
  342. {
  343. m_uHash = 0;
  344. res = AK_Fail;
  345. }
  346. }
  347. UnlockDB();
  348. }
  349. return res;
  350. }
  351. // in_uHash must have come from another AkDbString, and therefore already exist in the DB.
  352. AKRESULT Aquire(AkUInt32 in_uHash)
  353. {
  354. AKRESULT res = AK_Success;
  355. Release();
  356. if (in_uHash != 0)
  357. {
  358. m_uHash = in_uHash;
  359. LockDB();
  360. {
  361. Entry* pEntry = pInstance->table.Exists(m_uHash);
  362. AKASSERT(pEntry != NULL);
  363. pEntry->refCount++;
  364. AKASSERT(pEntry->str.Get() != NULL);
  365. }
  366. UnlockDB();
  367. }
  368. return res;
  369. }
  370. void Release()
  371. {
  372. if (m_uHash != 0)
  373. {
  374. LockDB();
  375. {
  376. tStringTable& table = pInstance->table;
  377. typename tStringTable::IteratorEx it = table.FindEx(m_uHash);
  378. AKASSERT(it != table.End());//<- Check that DbString was properly constructed.
  379. Entry& entry = (*it).item;
  380. AKASSERT(entry.refCount > 0);
  381. entry.refCount--;
  382. if (entry.refCount == 0)
  383. {
  384. table.Erase(it);
  385. }
  386. }
  387. UnlockDB();
  388. m_uHash = 0;
  389. }
  390. }
  391. AkUInt32 m_uHash;
  392. };
  393. template<typename TAlloc, typename T_CHAR, typename tLock>
  394. typename AkDbString<TAlloc, T_CHAR, tLock>::Instance* AkDbString<TAlloc, T_CHAR, tLock>::pInstance = NULL;