diff --git a/include/ebcl/Strings.hh b/include/ebcl/Strings.hh index c85d0cd..cf98593 100644 --- a/include/ebcl/Strings.hh +++ b/include/ebcl/Strings.hh @@ -459,6 +459,19 @@ M_LSHIFT_OP( T_StringBuilder , float ); M_LSHIFT_OP( T_StringBuilder , double ); +/*= STRING LIBRARY INITIALIZER ===============================================*/ +/* A few things in the string library need to be initialised as soon as + * anything that may use it is loaded. This includes the string pool and the + * "empty string" constant. + */ + +static struct T_StringLibInitializer +{ + T_StringLibInitializer( ); + ~T_StringLibInitializer( ); +} StringLibInitializer; + + } // namespace #endif // _H_EBCL_STRINGS #include diff --git a/src/Strings.cc b/src/Strings.cc index 245a102..2573b17 100644 --- a/src/Strings.cc +++ b/src/Strings.cc @@ -38,16 +38,14 @@ M_ABSTRACT_POINTERS( StringDataInternal ); /*----------------------------------------------------------------------------*/ -// T_EmptyString - Fake storage for the empty string -class T_EmptyString final : public A_StringDataInternal +// T_EmptyString_ - Fake storage for the empty string +class T_EmptyString_ final : public A_StringDataInternal { public: - static T_EmptyString EmptyString; + T_EmptyString_( ); - T_EmptyString( ); - - T_EmptyString( T_EmptyString const& ) = delete; - T_EmptyString( T_EmptyString&& other ) = delete; + T_EmptyString_( T_EmptyString_ const& ) = delete; + T_EmptyString_( T_EmptyString_&& other ) = delete; }; @@ -120,18 +118,16 @@ class T_Substring final : public A_RefCountedString /*----------------------------------------------------------------------------*/ -// T_StringPool - Pool of string storage classes -class T_StringPool final +// T_StringPool_ - Pool of string storage classes +class T_StringPool_ final { private: T_HashIndex index_; T_Array< T_OwnPtr< T_StaticString > > strings_; + mutable T_ReadWriteMutex mutex_; public: - static T_StringPool Pool; - static T_ReadWriteMutex Mutex; - - T_StringPool( ); + T_StringPool_( ); RP_StringDataInternal add( char const* data , uint32_t size ); RP_StringDataInternal get( char const* data , uint32_t size ) const; @@ -146,6 +142,38 @@ class T_StringPool final namespace ebcl { M_DECLARE_HASH( A_StringDataInternal ); } +/*= LIBRARY STATE AND INITIALIZATION =========================================*/ + +namespace { + + uint64_t StringLibCounter_; + + T_VarStorage< T_StringPool_ > StringPoolBuffer_; + T_VarStorage< T_EmptyString_ > EmptyStringBuffer_; + + T_StringPool_& StringPool_ = reinterpret_cast< T_StringPool_& >( StringPoolBuffer_ ); + T_EmptyString_& EmptyString_ = reinterpret_cast< T_EmptyString_& >( EmptyStringBuffer_ ); + +} + +T_StringLibInitializer::T_StringLibInitializer( ) +{ + if ( StringLibCounter_ ++ == 0 ) { + new (&StringPool_) T_StringPool_( ); + new (&EmptyString_) T_EmptyString_( ); + } +} + +T_StringLibInitializer::~T_StringLibInitializer( ) +{ + if ( -- StringLibCounter_ == 0 ) { + ( &StringPool_ )->~T_StringPool_( ); + ( &EmptyString_ )->~T_EmptyString_( ); + } +} + + + /*= UTF-8 UTILITY FUNCTIONS ==================================================*/ bool ebcl::UTF8IsValid( char const* string ) @@ -938,9 +966,9 @@ inline M_DEFINE_HASH( A_StringDataInternal ) } -/*= T_EmptyString ============================================================*/ +/*= T_EmptyString_ ============================================================*/ -T_EmptyString::T_EmptyString( ) +T_EmptyString_::T_EmptyString_( ) : A_StringDataInternal( false ) { data_ = nullptr; @@ -949,8 +977,6 @@ T_EmptyString::T_EmptyString( ) length_ = 0; } -T_EmptyString T_EmptyString::EmptyString; - /*= T_StaticString ===========================================================*/ @@ -1076,27 +1102,22 @@ T_Substring::~T_Substring( ) } -/*= T_StringPool =============================================================*/ +/*= T_StringPool_ ============================================================*/ -T_StringPool T_StringPool::Pool; -T_ReadWriteMutex T_StringPool::Mutex; - -/*----------------------------------------------------------------------------*/ - -T_StringPool::T_StringPool( ) +T_StringPool_::T_StringPool_( ) : index_( 16384 , 4096 , 4096 ) , strings_( 4096 ) { } /*----------------------------------------------------------------------------*/ -RP_StringDataInternal T_StringPool::add( char const* data , uint32_t size ) +RP_StringDataInternal T_StringPool_::add( char const* data , uint32_t size ) { - T_ReadLock lock( T_StringPool::Mutex ); - const auto hash( HashData( (uint8_t const*) data , size ) ); - const auto idx( find( data , size , hash ) ); + T_ReadLock lock{ mutex_ }; + const auto hash{ HashData( (uint8_t const*) data , size ) }; + const auto idx{ find( data , size , hash ) }; if ( idx == T_HashIndex::INVALID_INDEX ) { - const T_WriteLock wLock( lock.upgrade( ) ); + const T_WriteLock wLock{ lock.upgrade( ) }; index_.add( hash ); const auto str( strings_.add( NewOwned< T_StaticString >( data , size ) ) ); return strings_[ str ].get( ); @@ -1107,11 +1128,11 @@ RP_StringDataInternal T_StringPool::add( char const* data , uint32_t size ) /*----------------------------------------------------------------------------*/ -RP_StringDataInternal T_StringPool::get( char const* data , uint32_t size ) const +RP_StringDataInternal T_StringPool_::get( char const* data , uint32_t size ) const { - const T_ReadLock lock( T_StringPool::Mutex ); - const auto hash( HashData( reinterpret_cast< uint8_t const* >( data ) , size ) ); - const auto idx( find( data , size , hash ) ); + const T_ReadLock lock{ mutex_ }; + const auto hash{ HashData( reinterpret_cast< uint8_t const* >( data ) , size ) }; + const auto idx{ find( data , size , hash ) }; if ( idx == T_HashIndex::INVALID_INDEX ) { return nullptr; @@ -1122,7 +1143,7 @@ RP_StringDataInternal T_StringPool::get( char const* data , uint32_t size ) cons /*----------------------------------------------------------------------------*/ -uint32_t T_StringPool::find( char const* data , uint32_t sz , uint32_t hash ) const +uint32_t T_StringPool_::find( char const* data , uint32_t sz , uint32_t hash ) const { uint32_t idx = index_.first( hash ); while ( idx != T_HashIndex::INVALID_INDEX ) { @@ -1220,16 +1241,16 @@ bool T_StringIterator::next( ) /*= T_String =================================================================*/ T_String::T_String( ) noexcept - : data_( &T_EmptyString::EmptyString ) + : data_( &EmptyString_ ) { } T_String::T_String( char const* initial ) { if ( initial == nullptr || *initial == 0 ) { - data_ = &T_EmptyString::EmptyString; + data_ = &EmptyString_; } else { const uint32_t len( strlen( initial ) ); - data_ = T_StringPool::Pool.get( initial , len ); + data_ = StringPool_.get( initial , len ); if ( data_ == nullptr ) { data_ = new T_DynamicString( initial , len , false ); } @@ -1250,7 +1271,7 @@ T_String::T_String( T_StringBuilder const& sb ) T_String::T_String( char const* data , uint32_t size , bool nodup ) { if ( data == nullptr || size == 0 ) { - data_ = &T_EmptyString::EmptyString; + data_ = &EmptyString_; } else { data_ = new T_DynamicString( data , size , nodup ); } @@ -1265,7 +1286,7 @@ T_String::T_String( T_String const& source ) } T_String::T_String( T_String&& source ) noexcept - : data_( &T_EmptyString::EmptyString ) + : data_( &EmptyString_ ) { swap( *this , source ); } @@ -1285,7 +1306,7 @@ T_String& T_String::operator= ( T_String&& string ) noexcept assert( data_ != nullptr ); dynamic_cast< RP_StringDataInternal >( data_ )->removeUser( ); data_ = string.data_; - string.data_ = &T_EmptyString::EmptyString; + string.data_ = &EmptyString_; return *this; } @@ -1334,7 +1355,7 @@ T_String T_String::Pooled( char const* data , uint32_t size ) assert( data != nullptr ); T_String s; if ( size ) { - s.data_ = T_StringPool::Pool.add( data , size ); + s.data_ = StringPool_.add( data , size ); } return s; } @@ -1345,7 +1366,7 @@ T_String& T_String::addToPool( ) { const auto d( dynamic_cast< RP_StringDataInternal >( data_ ) ); if ( d->poolable ) { - data_ = T_StringPool::Pool.add( d->data( ) , d->size( ) ); + data_ = StringPool_.add( d->data( ) , d->size( ) ); d->removeUser( ); } return *this; @@ -1356,7 +1377,7 @@ T_String& T_String::usePool( ) { const auto d( dynamic_cast< RP_StringDataInternal >( data_ ) ); if ( d->poolable ) { - const auto nd( T_StringPool::Pool.get( d->data( ) , d->size( ) ) ); + const auto nd( StringPool_.get( d->data( ) , d->size( ) ) ); if ( nd != nullptr ) { data_ = nd; d->removeUser( );