// https://github.com/vinniefalco/LuaBridge
 
// Copyright 2021, Stefan Frings
 
// Copyright 2019, Dmitry Tarakanov
 
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
 
// Copyright 2007, Nathan Reed
 
// SPDX-License-Identifier: MIT
 
 
 
#pragma once
 
 
 
#include <cassert>
 
#include <unordered_map>
 
#include <utility>
 
 
 
namespace luabridge {
 
 
 
namespace detail {
 
 
 
//==============================================================================
 
/**
 
  Support for our RefCountedPtr.
 
*/
 
struct RefCountedPtrBase
 
{
 
    // Declaration of container for the refcounts
 
    typedef std::unordered_map<const void*, int> RefCountsType;
 
 
 
protected:
 
    RefCountsType& getRefCounts() const
 
    {
 
        static RefCountsType refcounts;
 
        return refcounts;
 
    }
 
};
 
 
 
} // namespace detail
 
 
 
//==============================================================================
 
/**
 
  A reference counted smart pointer.
 
 
 
  The api is compatible with boost::RefCountedPtr and std::RefCountedPtr, in the
 
  sense that it implements a strict subset of the functionality.
 
 
 
  This implementation uses a hash table to look up the reference count
 
  associated with a particular pointer.
 
 
 
  @tparam T The class type.
 
 
 
  @todo Decompose RefCountedPtr using a policy. At a minimum, the underlying
 
        reference count should be policy based (to support atomic operations)
 
        and the delete behavior should be policy based (to support custom
 
        disposal methods).
 
 
 
  @todo Provide an intrusive version of RefCountedPtr.
 
*/
 
template<class T>
 
class RefCountedPtr : private detail::RefCountedPtrBase
 
{
 
public:
 
    template<typename Other>
 
    struct rebind
 
    {
 
        typedef RefCountedPtr<Other> other;
 
    };
 
 
 
    /** Construct as nullptr or from existing pointer to T.
 
 
 
        @param p The optional, existing pointer to assign from.
 
    */
 
    RefCountedPtr(T* const p = nullptr) : m_p(p)
 
    {
 
        if (m_p)
 
        {
 
            ++getRefCounts()[m_p];
 
        }
 
    }
 
 
 
    /** Construct from another RefCountedPtr.
 
 
 
        @param rhs The RefCountedPtr to assign from.
 
    */
 
    RefCountedPtr(RefCountedPtr<T> const& rhs) : RefCountedPtr(rhs.get()) {}
 
 
 
    /** Construct from a RefCountedPtr of a different type.
 
 
 
        @invariant A pointer to U must be convertible to a pointer to T.
 
 
 
        @tparam U   The other object type.
 
        @param  rhs The RefCountedPtr to assign from.
 
    */
 
    template<typename U>
 
    RefCountedPtr(RefCountedPtr<U> const& rhs) : RefCountedPtr(rhs.get())
 
    {
 
    }
 
 
 
    /** Release the object.
 
 
 
        If there are no more references then the object is deleted.
 
    */
 
    ~RefCountedPtr() { reset(); }
 
 
 
    /** Assign from another RefCountedPtr.
 
 
 
        @param  rhs The RefCountedPtr to assign from.
 
        @returns     A reference to the RefCountedPtr.
 
    */
 
    RefCountedPtr<T>& operator=(RefCountedPtr<T> const& rhs)
 
    {
 
        // NOTE Self assignment is handled gracefully
 
        *this = rhs.get();
 
        return *this;
 
    }
 
 
 
    /** Assign from another RefCountedPtr of a different type.
 
 
 
        @note A pointer to U must be convertible to a pointer to T.
 
 
 
        @tparam U   The other object type.
 
        @param  rhs The other RefCountedPtr to assign from.
 
        @returns     A reference to the RefCountedPtr.
 
    */
 
    template<typename U>
 
    RefCountedPtr<T>& operator=(RefCountedPtr<U> const& rhs)
 
    {
 
        // NOTE Self assignment is handled gracefully
 
        *this = rhs.get();
 
        return *this;
 
    }
 
 
 
    RefCountedPtr<T>& operator=(T* const p)
 
    {
 
        if (p != m_p)
 
        {
 
            RefCountedPtr<T> tmp(p);
 
            std::swap(m_p, tmp.m_p);
 
        }
 
 
 
        return *this;
 
    }
 
 
 
    /** Retrieve the raw pointer.
 
 
 
        @returns A pointer to the object.
 
    */
 
    T* get() const { return m_p; }
 
 
 
    /** Retrieve the raw pointer by conversion.
 
 
 
        @returns A pointer to the object.
 
    */
 
    operator T*() const { return m_p; }
 
 
 
    /** Retrieve the raw pointer.
 
 
 
        @returns A pointer to the object.
 
    */
 
    T* operator*() const { return m_p; }
 
 
 
    /** Retrieve the raw pointer.
 
 
 
        @returns A pointer to the object.
 
    */
 
    T* operator->() const { return m_p; }
 
 
 
    /** Determine the number of references.
 
 
 
        @note This is not thread-safe.
 
 
 
        @returns The number of active references.
 
    */
 
    RefCountsType::mapped_type use_count() const
 
    {
 
        if (!m_p)
 
        {
 
            return 0;
 
        }
 
 
 
        const auto itCounter = getRefCounts().find(m_p);
 
        assert(itCounter != getRefCounts().end());
 
        assert(itCounter->second > 0);
 
 
 
        return itCounter->second;
 
    }
 
 
 
    /** Release the pointer.
 
 
 
        The reference count is decremented. If the reference count reaches
 
        zero, the object is deleted.
 
    */
 
    void reset()
 
    {
 
        if (m_p)
 
        {
 
            const auto itCounter = getRefCounts().find(m_p);
 
            assert(itCounter != getRefCounts().end());
 
            assert(itCounter->second > 0);
 
 
 
            if (--itCounter->second == 0)
 
            {
 
                delete m_p;
 
                getRefCounts().erase(itCounter);
 
            }
 
 
 
            m_p = nullptr;
 
        }
 
    }
 
 
 
private:
 
    T* m_p;
 
};
 
 
 
//==============================================================================
 
 
 
// forward declaration
 
template<class T>
 
struct ContainerTraits;
 
 
 
template<class T>
 
struct ContainerTraits<RefCountedPtr<T>>
 
{
 
    typedef T Type;
 
 
 
    static T* get(RefCountedPtr<T> const& c) { return c.get(); }
 
};
 
 
 
} // namespace luabridge