/*
  Copyright (c) 2006 Gábor Lehel <illissius@gmail.com>

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public License
  along with this library; see the file COPYING.LIB.  If not, write to
  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  Boston, MA 02110-1301, USA.
*/

#include <config.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <qdeepcopy.h>
#include <qstring.h>

#include "atomicstring.h"

#if __GNUC__ >= 3

    // Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
    // or anything like that.
    const unsigned PHI = 0x9e3779b9U;
    // Copyright (c) Paul Hsieh
    // http://www.azillionmonkeys.com/qed/hash.html
    struct AtomicString::SuperFastHash
    {
        uint32_t operator()( const QString *string ) const
        {
            unsigned l = string->length();
            const QChar *s = string->unicode();
            uint32_t hash = PHI;
            uint32_t tmp;

            int rem = l & 1;
            l >>= 1;

            // Main loop
            for (; l > 0; l--) {
                hash += s[0].unicode();
                tmp = (s[1].unicode() << 11) ^ hash;
                hash = (hash << 16) ^ tmp;
                s += 2;
                hash += hash >> 11;
            }

            // Handle end case
            if (rem) {
                hash += s[0].unicode();
                hash ^= hash << 11;
                hash += hash >> 17;
            }

            // Force "avalanching" of final 127 bits
            hash ^= hash << 3;
            hash += hash >> 5;
            hash ^= hash << 2;
            hash += hash >> 15;
            hash ^= hash << 10;

            // this avoids ever returning a hash code of 0, since that is used to
            // signal "hash not computed yet", using a value that is likely to be
            // effectively the same as 0 when the low bits are masked
            if (hash == 0)
                hash = 0x80000000;

            return hash;
        }
    };

    struct AtomicString::equal
    {
        bool operator()( const QString *a, const QString *b ) const
        {
            return *a == *b;
        }
    };

#endif

class AtomicString::Data: public QString
{
public:
    uint refcount;
    Data(): refcount( 0 ) { }
    Data( const QString &s ): QString( s ), refcount( 0 ) { }
};

AtomicString::AtomicString(): m_string( 0 ) { }

AtomicString::AtomicString( const AtomicString &other )
{
    storeMutex.lock();
    m_string = other.m_string;
    ref( m_string );
    storeMutex.unlock();
}

AtomicString::AtomicString( const QString &string ): m_string( 0 )
{
    if( string.isEmpty() )
        return;

    Data *s = new Data( string );  // note: s is a shallow copy
    storeMutex.lock();
    m_string = static_cast<Data*>( *( s_store.insert( s ).first ) );
    ref( m_string );
    uint rc = s->refcount;
    if( rc ) {
	// Inserted -- we need to make s a deep copy, as this copy
	// may be refcounted by the caller thread outside our locks
	// Hand hold C++ as to which operator= to use
	(QString &) (*s) = QDeepCopy<QString>(string);
    }
    storeMutex.unlock();
    if ( !rc ) delete( s );	// already present
}

AtomicString::~AtomicString()
{
    storeMutex.lock();
    deref( m_string );
    storeMutex.unlock();
}

QString AtomicString::deepCopy() const
{
    if (m_string) {
	storeMutex.lock();
	QDeepCopy<QString> copy( *m_string );
	storeMutex.unlock();
	return copy;
    }
    return QString::null;
}

bool AtomicString::isEmpty() const
{
    return !m_string;
}

const QString *AtomicString::ptr() const
{
    if( m_string )
        return m_string;
    return &QString::null;
}

uint AtomicString::refcount() const
{
    if ( m_string ) {
	storeMutex.lock();
	uint rc = m_string->refcount;
	storeMutex.unlock();
	return rc;
    } 
    return 0;
}

AtomicString &AtomicString::operator=( const AtomicString &other )
{
    if( m_string == other.m_string )
        return *this;
    storeMutex.lock();
    deref( m_string );
    m_string = other.m_string;
    ref( m_string );
    storeMutex.unlock();
    return *this;
}

bool AtomicString::operator==( const AtomicString &other ) const
{
    return m_string == other.m_string;
}

// needs to be called holding the lock
inline void AtomicString::deref( Data *s )
{
    if( !s )
        return;
    if( !( --s->refcount ) )
    {
        s_store.erase( s );
        delete s;
    }
}

// needs to be called holding the lock
inline void AtomicString::ref( Data *s )
{
    if( s )
        s->refcount++;
}

AtomicString::set_type AtomicString::s_store;
QMutex AtomicString::storeMutex;
