목차

include/AK/Tools/Common/AkString.h

이 파일의 문서화 페이지로 가기
00001 /*******************************************************************************
00002 The content of this file includes portions of the AUDIOKINETIC Wwise Technology
00003 released in source code form as part of the SDK installer package.
00004 
00005 Commercial License Usage
00006 
00007 Licensees holding valid commercial licenses to the AUDIOKINETIC Wwise Technology
00008 may use this file in accordance with the end user license agreement provided
00009 with the software or, alternatively, in accordance with the terms contained in a
00010 written agreement between you and Audiokinetic Inc.
00011 
00012 Apache License Usage
00013 
00014 Alternatively, this file may be used under the Apache License, Version 2.0 (the
00015 "Apache License"); you may not use this file except in compliance with the
00016 Apache License. You may obtain a copy of the Apache License at
00017 http://www.apache.org/licenses/LICENSE-2.0.
00018 
00019 Unless required by applicable law or agreed to in writing, software distributed
00020 under the Apache License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
00021 OR CONDITIONS OF ANY KIND, either express or implied. See the Apache License for
00022 the specific language governing permissions and limitations under the License.
00023 
00024 Version: <VERSION>  Build: <BUILDNUMBER>
00025 Copyright (c) <COPYRIGHTYEAR> Audiokinetic Inc.
00026 *******************************************************************************/
00027 #pragma once
00028 
00029 #include <AK/Tools/Common/AkFNVHash.h>
00030 #include <AK/Tools/Common/AkHashList.h>
00031 
00032 template<typename TAlloc, typename T_CHAR>
00033 class AkStringData : public TAlloc
00034 {
00035 public:
00036     AkStringData() : pStr(NULL), bOwner(false) {}
00037     AkStringData(const T_CHAR* in_pStr) : pStr(in_pStr), bOwner(false) {}
00038     ~AkStringData() { Term(); }
00039 
00040     void Term()
00041     {
00042         if (pStr && bOwner)
00043         {
00044             TAlloc::Free((void*)pStr);
00045             bOwner = false;
00046         }
00047         pStr = NULL;
00048     }
00049 
00050 protected:
00051     const T_CHAR* pStr;
00052     bool bOwner;
00053 };
00054 
00055 template<typename TAlloc, typename T_CHAR>
00056 class AkStringImpl : public AkStringData<TAlloc, T_CHAR>
00057 {};
00058 
00059 template<typename TAlloc, typename T_CHAR>
00060 class AkString: public AkStringImpl<TAlloc, T_CHAR>
00061 {
00062 private:
00063     typedef AkStringData<TAlloc, T_CHAR> tData;
00064     typedef AkStringImpl<TAlloc, T_CHAR> tImpl;
00065 
00066 public:
00067     AkString() : AkStringImpl<TAlloc, T_CHAR>() {}
00068 
00069     template<typename T_CHAR2>
00070     AkString(const T_CHAR2* in_pStr) { tImpl::Set(in_pStr); }
00071 
00072     AkString(const AkString<TAlloc, T_CHAR>& in_other) { tImpl::Set(in_other.Get()); }
00073 
00074     template<typename TAlloc2, typename T_CHAR2>
00075     AkString(const AkString<TAlloc2, T_CHAR2>& in_other) { tImpl::Set(in_other.Get()); }
00076 
00077     // The default assignment behavior is to not create a local copy, unless it is necessary (due to incompatible string types).
00078     // Call AllocCopy() if you want to ensure there is a local copy owned by this AkString.  
00079     AKRESULT AllocCopy()
00080     {
00081         if (tData::pStr && !tData::bOwner)
00082         {
00083             const T_CHAR* pRefStr = tData::pStr;
00084             AkUInt32 uLen = tImpl::Length();
00085             if (uLen > 0)
00086             {
00087                 tData::pStr = (T_CHAR*)TAlloc::Alloc((uLen + 1) * sizeof(T_CHAR));
00088                 if (tData::pStr == NULL)
00089                     return AK_InsufficientMemory;
00090 
00091                 AKPLATFORM::AkMemCpy((void*)tData::pStr, (void*)pRefStr, ((uLen + 1) * sizeof(T_CHAR)));
00092                 tData::bOwner = true;
00093             }
00094             else
00095             {
00096                 tData::pStr = NULL;
00097             }
00098         }
00099         return AK_Success;
00100     }
00101 
00102     // Transfer memory ownership from in_from to this AkString.
00103     void Transfer(AkString<TAlloc, T_CHAR>& in_from)
00104     {
00105         tData::Term();
00106 
00107         tData::pStr = in_from.tData::pStr;
00108         tData::bOwner = true;
00109         
00110         in_from.tData::pStr = NULL;
00111         in_from.tData::bOwner = false;
00112     }
00113 
00114     const T_CHAR* Get() const
00115     {
00116         return tData::pStr;
00117     }
00118 
00119     AkString& operator=(const AkString<TAlloc, T_CHAR>& in_rhs)
00120     {
00121         tImpl::Set(in_rhs.Get());
00122         return *this;
00123     }
00124 
00125     template<typename TAlloc2, typename T_CHAR2>
00126     AkString& operator=(const AkString<TAlloc2, T_CHAR2>& in_rhs)
00127     {
00128         tImpl::Set(in_rhs.Get());
00129         return *this;
00130     }
00131 
00132     template<typename T_CHAR2>
00133     AkString& operator=(const T_CHAR2* in_pStr)
00134     {
00135         tImpl::Set(in_pStr);
00136         return *this;
00137     }
00138 };
00139 
00140 #ifdef AK_SUPPORT_WCHAR 
00141 template<typename TAlloc>
00142 class AkStringImpl <TAlloc, wchar_t> : public AkStringData<TAlloc, wchar_t>
00143 {
00144 private:
00145     typedef AkStringData<TAlloc, wchar_t> tData;
00146 
00147 public:
00148     AkStringImpl() : AkStringData<TAlloc, wchar_t>() {}
00149 
00150 protected:
00151     AKRESULT Set(const char* in_pStr)
00152     {
00153         tData::Term();
00154 
00155         if (in_pStr != NULL)
00156         {
00157             size_t uLen = strlen(in_pStr);
00158             if (uLen > 0)
00159             {
00160                 tData::pStr = (wchar_t*)TAlloc::Alloc((uLen + 1) * sizeof(wchar_t));
00161                 if (tData::pStr == NULL)
00162                     return AK_InsufficientMemory;
00163 
00164                 AKPLATFORM::AkCharToWideChar(in_pStr, (AkUInt32)(uLen + 1), const_cast<wchar_t*>(tData::pStr));
00165                 tData::bOwner = true;
00166             }
00167             else
00168             {
00169                 tData::pStr = NULL;
00170             }
00171         }
00172 
00173         return AK_Success;
00174     }
00175 
00176     AKRESULT Set(const wchar_t* in_pStr)
00177     {
00178         tData::Term();
00179         tData::pStr = in_pStr;
00180         return AK_Success;
00181     }
00182 
00183 public:
00184     AkUInt32 Length() const
00185     {
00186         return (AkUInt32)wcslen(tData::pStr);
00187     }
00188 };
00189 #endif
00190 
00191 template<typename TAlloc>
00192 class AkStringImpl <TAlloc, char> : public AkStringData<TAlloc, char>
00193 {
00194 private:
00195     typedef AkStringData<TAlloc, char> tData;
00196 
00197 public:
00198     AkStringImpl() : AkStringData<TAlloc, char>() {}
00199 
00200 protected:
00201     AKRESULT Set(const wchar_t* in_pStr)
00202     {
00203         tData::Term();
00204 
00205         if (in_pStr != NULL)
00206         {
00207             size_t uLen = wcslen(in_pStr);
00208             if (uLen > 0)
00209             {
00210                 tData::pStr = (char*)TAlloc::Alloc((uLen + 1) * sizeof(char));
00211                 if (tData::pStr == NULL)
00212                     return AK_InsufficientMemory;
00213 
00214                 AKPLATFORM::AkWideCharToChar(in_pStr, (AkUInt32)(uLen + 1), const_cast<char*>(tData::pStr));
00215                 tData::bOwner = true;
00216             }
00217             else
00218             {
00219                 tData::pStr = NULL;
00220             }
00221         }
00222 
00223         return AK_Success;
00224     }
00225 
00226     AKRESULT Set(const char* in_pStr)
00227     {
00228         tData::Term();
00229         tData::pStr = in_pStr;
00230         return AK_Success;
00231     }
00232 
00233 public:
00234     AkUInt32 Length() const
00235     {
00236         return (AkUInt32)strlen(tData::pStr);
00237     }
00238 };
00239 
00240 struct AkNonThreaded
00241 {
00242     AkForceInline void Lock() {}
00243     AkForceInline void Unlock() {}
00244 };
00245 
00246 template<typename TAlloc, typename T_CHAR>
00247 static AkForceInline AkUInt32 AkHash(const AkString<TAlloc, T_CHAR>& in_str)
00248 {
00249     AkUInt32 uLen = in_str.Length();
00250     if (uLen > 0)
00251     {
00252         AK::FNVHash32 hash;
00253         return hash.Compute(in_str.Get(), uLen * sizeof(T_CHAR));
00254     }
00255     return 0;
00256 }
00257 
00258 //
00259 // 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,
00260 //  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.
00261 //
00262 template<typename TAlloc, typename T_CHAR, typename tLock = AkNonThreaded>
00263 class AkDbString : public TAlloc
00264 {
00265 public: 
00266     typedef AkDbString<TAlloc, T_CHAR, tLock> tThis;
00267     typedef AkString<TAlloc, T_CHAR> tString;
00268 
00269     struct Entry
00270     {
00271         Entry() : refCount(0) {}
00272 
00273         tString str;
00274         AkInt32 refCount;
00275     };
00276 
00277     typedef AkHashList<AkUInt32, Entry, TAlloc> tStringTable;
00278 
00279     struct Instance : public TAlloc
00280     {
00281         tStringTable table;
00282         tLock lock;
00283     };
00284 
00285 public:
00286 
00287     // Must be called to initialize the database.  
00288     static AKRESULT InitDB()
00289     {
00290         if (pInstance == NULL)
00291         {
00292             pInstance = (Instance*)pInstance->TAlloc::Alloc(sizeof(Instance));
00293             AkPlacementNew(pInstance) Instance();
00294             return AK_Success;
00295         }
00296         else
00297             return AK_Fail;
00298     }
00299 
00300     // Term the DB.
00301     static void TermDB()
00302     {
00303         if (pInstance != NULL)
00304         {
00305             pInstance->~Instance();
00306             pInstance->TAlloc::Free(pInstance);
00307             pInstance = NULL;
00308         }
00309     }
00310 
00311     static void UnlockDB() { pInstance->lock.Unlock();}
00312     static void LockDB() { pInstance->lock.Lock(); }
00313 
00314 private:
00315 
00316     static Instance* pInstance;
00317 
00318 public:
00319     AkDbString() : m_uHash(0)
00320     {}
00321 
00322     AkDbString(const tThis& in_fromDbStr) : m_uHash(0) { Aquire(in_fromDbStr.m_uHash); }
00323 
00324     // Construct from AkString
00325     template<typename TAlloc2, typename T_CHAR2>
00326     AkDbString(const AkString<TAlloc2, T_CHAR2>& in_fromStr) : m_uHash(0) { Aquire(in_fromStr); }
00327 
00328     tThis& operator=(const tThis& in_rhs)
00329     {
00330         Aquire(in_rhs.m_uHash);
00331         return *this;
00332     }
00333 
00334     // Assign from AkString
00335     template<typename TAlloc2, typename T_CHAR2>
00336     tThis& operator=(const AkString<TAlloc2, T_CHAR2>& in_rhs)
00337     {
00338         Aquire(in_rhs);
00339         return *this;
00340     }
00341 
00342     // Assign from char string
00343     template<typename T_CHAR2>
00344     tThis& operator=(const T_CHAR2* in_rhs)
00345     {
00346         const AkString<TAlloc, T_CHAR2>& convert = in_rhs;
00347         Aquire(convert);
00348         return *this;
00349     }
00350 
00351     ~AkDbString()
00352     {
00353         Release();
00354     }
00355 
00356     const T_CHAR* Get() const
00357     {
00358         if (m_uHash != 0)
00359         {
00360             Entry* pEntry = pInstance->table.Exists(m_uHash);
00361             AKASSERT(pEntry != NULL);
00362             return pEntry->str.Get();
00363         }
00364         return NULL;
00365     }
00366 
00367 protected:
00368     
00369     template<typename TAlloc2, typename T_CHAR2>
00370     AKRESULT Aquire(const AkString<TAlloc2, T_CHAR2>& in_str)
00371     {
00372         AKRESULT res = AK_Success;
00373 
00374         Release();
00375 
00376         if (in_str.Get() != NULL)
00377         {
00378             m_uHash = AkHash(in_str);
00379 
00380             LockDB();
00381             {
00382                 Entry* pEntry = pInstance->table.Set(m_uHash);
00383                 if (pEntry != NULL)
00384                 {
00385                     pEntry->refCount++;
00386 
00387                     if (pEntry->str.Get() == NULL)
00388                     {
00389                         pEntry->str = in_str;
00390                         pEntry->str.AllocCopy();
00391 
00392                         if (pEntry->str.Get() == NULL) // Allocation failure
00393                         {
00394                             pInstance->table.Unset(m_uHash);
00395                             m_uHash = 0;
00396                             res = AK_Fail;
00397                         }
00398                     }
00399                 }
00400                 else
00401                 {
00402                     m_uHash = 0;
00403                     res = AK_Fail;
00404                 }
00405             }
00406             UnlockDB();
00407         }
00408 
00409         return res;
00410     }
00411 
00412     // in_uHash must have come from another AkDbString, and therefore already exist in the DB.
00413     AKRESULT Aquire(AkUInt32 in_uHash)
00414     {
00415         AKRESULT res = AK_Success;
00416 
00417         Release();
00418 
00419         if (in_uHash != 0)
00420         {
00421             m_uHash = in_uHash;
00422             LockDB();
00423             {
00424                 Entry* pEntry = pInstance->table.Exists(m_uHash);
00425                 AKASSERT(pEntry != NULL);
00426 
00427                 pEntry->refCount++;
00428                 AKASSERT(pEntry->str.Get() != NULL);
00429             }
00430             UnlockDB();
00431         }
00432 
00433         return res;
00434     }
00435 
00436     void Release()
00437     {
00438         if (m_uHash != 0)
00439         {
00440             LockDB();
00441             {
00442                 tStringTable& table = pInstance->table;
00443                 typename tStringTable::IteratorEx it = table.FindEx(m_uHash);
00444                 AKASSERT(it != table.End());//<- Check that DbString was properly constructed.
00445                 Entry& entry = (*it).item;
00446                 AKASSERT(entry.refCount > 0);
00447 
00448                 entry.refCount--;
00449                 if (entry.refCount == 0)
00450                 {
00451                     table.Erase(it);
00452                 }
00453             }
00454             UnlockDB();
00455 
00456             m_uHash = 0;
00457         }
00458     }
00459 
00460     AkUInt32 m_uHash;
00461 
00462 };
00463 
00464 template<typename TAlloc, typename T_CHAR, typename tLock>
00465 typename AkDbString<TAlloc, T_CHAR, tLock>::Instance* AkDbString<TAlloc, T_CHAR, tLock>::pInstance = NULL;