Goose  Documentation

#ifndef EMPATHY_UTIL_STRINGID_H
#define EMPATHY_UTIL_STRINGID_H

namespace empathy::util
{
    class StringId
    {
        public:
            StringId() {}

            explicit constexpr StringId( const char* pString ) :
                m_uniqueId( CalcCRC( pString ) ),
                m_pString( pString )
            {}

            explicit constexpr StringId( const char* pString, size_t size ) :
                m_uniqueId( CalcCRC( pString ) ),
                m_pString( pString )
            {}

            explicit StringId( const string& str );
            explicit StringId( string&& str );

            explicit StringId( uint32_t uniqueId ) :
                m_uniqueId( CalcCRC( uniqueId ) ),
                m_pString( ms_pAnonId )
            {}

            constexpr auto uniqueId() const { return m_uniqueId; }

            bool isAnonymous() const { return m_pString == ms_pAnonId; }

            constexpr auto operator==( const StringId& rhs ) const
            {
                return m_uniqueId == rhs.m_uniqueId;
            }

            constexpr auto operator!=( const StringId& rhs ) const
            {
                return m_uniqueId != rhs.m_uniqueId;
            }

            constexpr auto operator<( const StringId& rhs ) const
            {
                return m_uniqueId < rhs.m_uniqueId;
            }

            constexpr auto operator>=( const StringId& rhs ) const
            {
                return m_uniqueId >= rhs.m_uniqueId;
            }

            constexpr auto operator<=( const StringId& rhs ) const
            {
                return m_uniqueId <= rhs.m_uniqueId;
            }

            constexpr auto operator>( const StringId& rhs ) const
            {
                return m_uniqueId > rhs.m_uniqueId;
            }

            const char* c_str() const { return m_pString; }

            friend ostream& operator<<( ostream& out, const StringId& sid )
            {
                return out << sid.c_str();
            }

        private:
            constexpr static uint32_t CalcCRC( const char* pString )
            {
                uint32_t crc = numeric_limits< uint32_t >::max();
                while( char c = *pString++ )
                    crc = ComputeCRC32( c, crc );

                return ~crc;
            }

            constexpr static uint32_t CalcCRC( uint32_t val )
            {
                uint32_t crc = numeric_limits< uint32_t >::max();

                // To reduce the risk of collisions between string ids
                // and integer ids, instead of using the value directly,
                // compute its crc byte by byte.
                // Inject a byte set to 1 first: otherwise,
                // we could run pretty fast into a value that would be
                // equivalent to a one character string identifier and
                // have a collision before even computing the crc.
                crc = ComputeCRC32( 1, crc );

                crc = ComputeCRC32( val & 0xff, crc );
                val = val >> 8;
                crc = ComputeCRC32( val & 0xff, crc );
                val = val >> 8;
                crc = ComputeCRC32( val & 0xff, crc );
                val = val >> 8;
                crc = ComputeCRC32( val & 0xff, crc );

                return ~crc;
            }


            uint32_t m_uniqueId = ~0;
            const char* m_pString = nullptr;

            static const char* ms_pAnonId;
            static unordered_set< string > ms_DynStrings;
    };
}

namespace std
{
    template<> struct hash< empathy::util::StringId >
    {
        size_t operator()( const empathy::util::StringId& x ) const
        {
            return hash< uint32_t >()( x.uniqueId() );
        }
    };
}

constexpr auto operator "" _sid(const char* pString, std::size_t s )
{
    return empathy::util::StringId( pString, s );
}

#endif