X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/68482dc584054c65f2dee76def3927d338fc20a7..326b62161d387b4181b797a76652796f730172fb:/include/wx/string.h diff --git a/include/wx/string.h b/include/wx/string.h index 62b362b1ed..b66afd79ae 100644 --- a/include/wx/string.h +++ b/include/wx/string.h @@ -594,8 +594,41 @@ private: unsigned lastUsed; }; +#ifndef wxHAS_COMPILER_TLS + // we must use an accessor function and not a static variable when the TLS + // variables support is implemented in the library (and not by the compiler) + // because the global s_cache variable could be not yet initialized when a + // ctor of another global object is executed and if that ctor uses any + // wxString methods, bad things happen + // + // however notice that this approach does not work when compiler TLS is used, + // at least not with g++ 4.1.2 under amd64 as it apparently compiles code + // using this accessor incorrectly when optimizations are enabled (-O2 is + // enough) -- luckily we don't need it then neither as static __thread + // variables are initialized by 0 anyhow then and so we can use the variable + // directly + WXEXPORT static Cache& GetCache() + { + static wxTLS_TYPE(Cache) s_cache; + + return wxTLS_VALUE(s_cache); + } + + // this helper struct is used to ensure that GetCache() is called during + // static initialization time, i.e. before any threads creation, as otherwise + // the static s_cache construction inside GetCache() wouldn't be MT-safe + friend struct wxStrCacheInitializer; +#else // wxHAS_COMPILER_TLS static wxTLS_TYPE(Cache) ms_cache; + static Cache& GetCache() { return wxTLS_VALUE(ms_cache); } +#endif // !wxHAS_COMPILER_TLS/wxHAS_COMPILER_TLS + + static Cache::Element *GetCacheBegin() { return GetCache().cached; } + static Cache::Element *GetCacheEnd() { return GetCacheBegin() + Cache::SIZE; } + static unsigned& LastUsedCacheElement() { return GetCache().lastUsed; } + // this is used in debug builds only to provide a convenient function, + // callable from a debugger, to show the cache contents friend struct wxStrCacheDumper; // uncomment this to have access to some profiling statistics on program @@ -616,7 +649,7 @@ private: lenhits; // number of cache hits in length() } ms_cacheStats; - friend struct ShowCacheStats; + friend struct wxStrCacheStatsDumper; #define wxCACHE_PROFILE_FIELD_INC(field) ms_cacheStats.field++ #define wxCACHE_PROFILE_FIELD_ADD(field, val) ms_cacheStats.field += (val) @@ -638,9 +671,15 @@ private: // profiling seems to show a small but consistent gain if we use this // simple loop instead of starting from the last used element (there are // a lot of misses in this function...) - for ( Cache::Element *c = ms_cache.cached; - c != ms_cache.cached + Cache::SIZE; - c++ ) + Cache::Element * const cacheBegin = GetCacheBegin(); +#ifndef wxHAS_COMPILER_TLS + // during destruction tls calls may return NULL, in this case return NULL + // immediately without accessing anything else + if ( cacheBegin == NULL ) + return NULL; +#endif + Cache::Element * const cacheEnd = GetCacheEnd(); + for ( Cache::Element *c = cacheBegin; c != cacheEnd; c++ ) { if ( c->str == this ) return c; @@ -654,9 +693,9 @@ private: // its corresponding index in the byte string or not Cache::Element *GetCacheElement() const { - Cache::Element * const cacheBegin = ms_cache.cached; - Cache::Element * const cacheEnd = ms_cache.cached + Cache::SIZE; - Cache::Element * const cacheStart = cacheBegin + ms_cache.lastUsed; + Cache::Element * const cacheBegin = GetCacheBegin(); + Cache::Element * const cacheEnd = GetCacheEnd(); + Cache::Element * const cacheStart = cacheBegin + LastUsedCacheElement(); // check the last used first, this does no (measurable) harm for a miss // but does help for simple loops addressing the same string all the time @@ -679,7 +718,7 @@ private: c->Reset(); // and remember the last used element - ms_cache.lastUsed = c - cacheBegin; + LastUsedCacheElement() = c - cacheBegin; } return c; @@ -702,7 +741,9 @@ private: // used for length caching only so far, i.e. it doesn't count as a hit // from our point of view if ( cache->pos ) + { wxCACHE_PROFILE_FIELD_INC(poshits); + } if ( pos == cache->pos ) return cache->impl; @@ -817,22 +858,26 @@ public: typedef size_t size_type; typedef wxUniChar const_reference; -#if wxUSE_STL +#if wxUSE_STD_STRING #if wxUSE_UNICODE_UTF8 // random access is not O(1), as required by Random Access Iterator #define WX_STR_ITERATOR_TAG std::bidirectional_iterator_tag #else #define WX_STR_ITERATOR_TAG std::random_access_iterator_tag #endif + #define WX_DEFINE_ITERATOR_CATEGORY(cat) typedef cat iterator_category; #else - #define WX_STR_ITERATOR_TAG void /* dummy type */ + // not defining iterator_category at all in this case is better than defining + // it as some dummy type -- at least it results in more intelligible error + // messages + #define WX_DEFINE_ITERATOR_CATEGORY(cat) #endif #define WX_STR_ITERATOR_IMPL(iterator_name, pointer_type, reference_type) \ private: \ typedef wxStringImpl::iterator_name underlying_iterator; \ public: \ - typedef WX_STR_ITERATOR_TAG iterator_category; \ + WX_DEFINE_ITERATOR_CATEGORY(WX_STR_ITERATOR_TAG) \ typedef wxUniChar value_type; \ typedef int difference_type; \ typedef reference_type reference; \ @@ -1067,7 +1112,7 @@ public: public: typedef T iterator_type; - typedef typename T::iterator_category iterator_category; + WX_DEFINE_ITERATOR_CATEGORY(typename T::iterator_category) typedef typename T::value_type value_type; typedef typename T::difference_type difference_type; typedef typename T::reference reference; @@ -1413,12 +1458,7 @@ public: // truncate the string to given length wxString& Truncate(size_t uiLen); // empty string contents - void Empty() - { - Truncate(0); - - wxASSERT_MSG( empty(), _T("string not empty after call to Empty()?") ); - } + void Empty() { clear(); } // empty the string and free memory void Clear() { clear(); } @@ -2899,8 +2939,13 @@ public: // swap two strings void swap(wxString& str) { - wxSTRING_INVALIDATE_CACHE(); - str.wxSTRING_INVALIDATE_CACHE(); +#if wxUSE_STRING_POS_CACHE + // we modify not only this string but also the other one directly so we + // need to invalidate cache for both of them (we could also try to + // exchange their cache entries but it seems unlikely to be worth it) + InvalidateCache(); + str.InvalidateCache(); +#endif // wxUSE_STRING_POS_CACHE m_impl.swap(str.m_impl); }