//===-- ConstString.cpp -----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Core/ConstString.h"
#include "lldb/Core/Stream.h"
#include "lldb/Host/Mutex.h"
#include "llvm/ADT/StringMap.h"

#include <mutex>

using namespace lldb_private;


class Pool
{
public:
    typedef const char * StringPoolValueType;
    typedef llvm::StringMap<StringPoolValueType, llvm::BumpPtrAllocator> StringPool;
    typedef llvm::StringMapEntry<StringPoolValueType> StringPoolEntryType;
    
    //------------------------------------------------------------------
    // Default constructor
    //
    // Initialize the member variables and create the empty string.
    //------------------------------------------------------------------
    Pool () :
        m_mutex (Mutex::eMutexTypeRecursive),
        m_string_map ()
    {
    }

    //------------------------------------------------------------------
    // Destructor
    //------------------------------------------------------------------
    ~Pool ()
    {
    }


    static StringPoolEntryType &
    GetStringMapEntryFromKeyData (const char *keyData)
    {
        char *ptr = const_cast<char*>(keyData) - sizeof (StringPoolEntryType);
        return *reinterpret_cast<StringPoolEntryType*>(ptr);
    }

    size_t
    GetConstCStringLength (const char *ccstr) const
    {
        if (ccstr)
        {
            const StringPoolEntryType&entry = GetStringMapEntryFromKeyData (ccstr);
            return entry.getKey().size();
        }
        return 0;
    }

    StringPoolValueType
    GetMangledCounterpart (const char *ccstr) const
    {
        if (ccstr)
            return GetStringMapEntryFromKeyData (ccstr).getValue();
        return 0;
    }

    bool
    SetMangledCounterparts (const char *key_ccstr, const char *value_ccstr)
    {
        if (key_ccstr && value_ccstr)
        {
            GetStringMapEntryFromKeyData (key_ccstr).setValue(value_ccstr);
            GetStringMapEntryFromKeyData (value_ccstr).setValue(key_ccstr);
            return true;
        }
        return false;
    }

    const char *
    GetConstCString (const char *cstr)
    {
        if (cstr)
            return GetConstCStringWithLength (cstr, strlen (cstr));
        return NULL;
    }

    const char *
    GetConstCStringWithLength (const char *cstr, size_t cstr_len)
    {
        if (cstr)
        {
            Mutex::Locker locker (m_mutex);
            llvm::StringRef string_ref (cstr, cstr_len);
            StringPoolEntryType& entry = *m_string_map.insert (std::make_pair (string_ref, (StringPoolValueType)NULL)).first;
            return entry.getKeyData();
        }
        return NULL;
    }

    const char *
    GetConstCStringWithStringRef (const llvm::StringRef &string_ref)
    {
        if (string_ref.data())
        {
            Mutex::Locker locker (m_mutex);
            StringPoolEntryType& entry = *m_string_map.insert (std::make_pair (string_ref, (StringPoolValueType)NULL)).first;
            return entry.getKeyData();
        }
        return NULL;
    }

    const char *
    GetConstCStringAndSetMangledCounterPart (const char *demangled_cstr, const char *mangled_ccstr)
    {
        if (demangled_cstr)
        {
            Mutex::Locker locker (m_mutex);
            // Make string pool entry with the mangled counterpart already set
            StringPoolEntryType& entry = *m_string_map.insert (std::make_pair (llvm::StringRef (demangled_cstr), mangled_ccstr)).first;

            // Extract the const version of the demangled_cstr
            const char *demangled_ccstr = entry.getKeyData();
            // Now assign the demangled const string as the counterpart of the
            // mangled const string...
            GetStringMapEntryFromKeyData (mangled_ccstr).setValue(demangled_ccstr);
            // Return the constant demangled C string
            return demangled_ccstr;
        }
        return NULL;
    }

    const char *
    GetConstTrimmedCStringWithLength (const char *cstr, size_t cstr_len)
    {
        if (cstr)
        {
            const size_t trimmed_len = std::min<size_t> (strlen (cstr), cstr_len);
            return GetConstCStringWithLength (cstr, trimmed_len);
        }
        return NULL;
    }

    //------------------------------------------------------------------
    // Return the size in bytes that this object and any items in its
    // collection of uniqued strings + data count values takes in
    // memory.
    //------------------------------------------------------------------
    size_t
    MemorySize() const
    {
        Mutex::Locker locker (m_mutex);
        size_t mem_size = sizeof(Pool);
        const_iterator end = m_string_map.end();
        for (const_iterator pos = m_string_map.begin(); pos != end; ++pos)
        {
            mem_size += sizeof(StringPoolEntryType) + pos->getKey().size();
        }
        return mem_size;
    }

protected:
    //------------------------------------------------------------------
    // Typedefs
    //------------------------------------------------------------------
    typedef StringPool::iterator iterator;
    typedef StringPool::const_iterator const_iterator;

    //------------------------------------------------------------------
    // Member variables
    //------------------------------------------------------------------
    mutable Mutex m_mutex;
    StringPool m_string_map;
};

//----------------------------------------------------------------------
// Frameworks and dylibs aren't supposed to have global C++
// initializers so we hide the string pool in a static function so
// that it will get initialized on the first call to this static
// function.
//
// Note, for now we make the string pool a pointer to the pool, because
// we can't guarantee that some objects won't get destroyed after the
// global destructor chain is run, and trying to make sure no destructors
// touch ConstStrings is difficult.  So we leak the pool instead.
//----------------------------------------------------------------------
static Pool &
StringPool()
{
    static std::once_flag g_pool_initialization_flag;
    static Pool *g_string_pool = NULL;

    std::call_once(g_pool_initialization_flag, [] () {
        g_string_pool = new Pool();
    });
    
    return *g_string_pool;
}

ConstString::ConstString (const char *cstr) :
    m_string (StringPool().GetConstCString (cstr))
{
}

ConstString::ConstString (const char *cstr, size_t cstr_len) :
    m_string (StringPool().GetConstCStringWithLength (cstr, cstr_len))
{
}

ConstString::ConstString (const llvm::StringRef &s) :
    m_string (StringPool().GetConstCStringWithLength (s.data(), s.size()))
{
}

bool
ConstString::operator < (const ConstString& rhs) const
{
    if (m_string == rhs.m_string)
        return false;

    llvm::StringRef lhs_string_ref (m_string, StringPool().GetConstCStringLength (m_string));
    llvm::StringRef rhs_string_ref (rhs.m_string, StringPool().GetConstCStringLength (rhs.m_string));

    // If both have valid C strings, then return the comparison
    if (lhs_string_ref.data() && rhs_string_ref.data())
        return lhs_string_ref < rhs_string_ref;

    // Else one of them was NULL, so if LHS is NULL then it is less than
    return lhs_string_ref.data() == NULL;
}

Stream&
lldb_private::operator << (Stream& s, const ConstString& str)
{
    const char *cstr = str.GetCString();
    if (cstr)
        s << cstr;

    return s;
}

size_t
ConstString::GetLength () const
{
    return StringPool().GetConstCStringLength (m_string);
}

int
ConstString::Compare (const ConstString& lhs, const ConstString& rhs)
{
    // If the iterators are the same, this is the same string
    const char *lhs_cstr = lhs.m_string;
    const char *rhs_cstr = rhs.m_string;
    if (lhs_cstr == rhs_cstr)
        return 0;
    if (lhs_cstr && rhs_cstr)
    {
        llvm::StringRef lhs_string_ref (lhs_cstr, StringPool().GetConstCStringLength (lhs_cstr));
        llvm::StringRef rhs_string_ref (rhs_cstr, StringPool().GetConstCStringLength (rhs_cstr));
        return lhs_string_ref.compare(rhs_string_ref);
    }

    if (lhs_cstr)
        return +1;  // LHS isn't NULL but RHS is
    else
        return -1;  // LHS is NULL but RHS isn't
}

void
ConstString::Dump(Stream *s, const char *fail_value) const
{
    if (s)
    {
        const char *cstr = AsCString (fail_value);
        if (cstr)
            s->PutCString (cstr);
    }
}

void
ConstString::DumpDebug(Stream *s) const
{
    const char *cstr = GetCString ();
    size_t cstr_len = GetLength();
    // Only print the parens if we have a non-NULL string
    const char *parens = cstr ? "\"" : "";
    s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64,
              static_cast<int>(sizeof(void*) * 2),
              static_cast<const void*>(this), parens, cstr, parens,
              static_cast<uint64_t>(cstr_len));
}

void
ConstString::SetCString (const char *cstr)
{
    m_string = StringPool().GetConstCString (cstr);
}

void
ConstString::SetString (const llvm::StringRef &s)
{
    m_string = StringPool().GetConstCStringWithLength (s.data(), s.size());
}

void
ConstString::SetCStringWithMangledCounterpart (const char *demangled, const ConstString &mangled)
{
    m_string = StringPool().GetConstCStringAndSetMangledCounterPart (demangled, mangled.m_string);
}

bool
ConstString::GetMangledCounterpart (ConstString &counterpart) const
{
    counterpart.m_string = StringPool().GetMangledCounterpart(m_string);
    return (bool)counterpart;
}

void
ConstString::SetCStringWithLength (const char *cstr, size_t cstr_len)
{
    m_string = StringPool().GetConstCStringWithLength(cstr, cstr_len);
}

void
ConstString::SetTrimmedCStringWithLength (const char *cstr, size_t cstr_len)
{
    m_string = StringPool().GetConstTrimmedCStringWithLength (cstr, cstr_len);
}

size_t
ConstString::StaticMemorySize()
{
    // Get the size of the static string pool
    return StringPool().MemorySize();
}
