<commit_before>#ifndef ECL_MEMORY_HPP_
#define ECL_MEMORY_HPP_

namespace ecl
{

template< typename T >
class shared_ptr;

template< typename T, class Alloc, class... Args >
shared_ptr< T > allocate_shared(const Alloc &alloc, Args... args);

template< typename T >
class shared_ptr
{
    template< typename U, class Alloc, class... Args >
    friend shared_ptr< U > allocate_shared(const Alloc& alloc, Args... args);

public:
    shared_ptr();
    ~shared_ptr();

    shared_ptr(shared_ptr &other);
    shared_ptr(shared_ptr &&other);
    shared_ptr& operator=(shared_ptr &other);

    // Returns true if this pointer holds the last remaining node
    bool unique() const;

    // Returs a value itself
    T* get() const;

private:
    struct aux
    {
        // Gets object
        virtual T* get() = 0;
        // Destroys payload and aux itself. _Must_ be called to destroy
        virtual void destroy() = 0;
    };

    // Helper object
    template< class Alloc, class... Args >
    class aux_alloc : public aux
    {
    public:
        aux_alloc(const Alloc &a, Args... args)
            :m_object{args...}
            ,m_alloc{a}
        { }

        virtual void destroy() override
        {
            m_object.~T();
            auto allocator = m_alloc.template rebind< aux_alloc >();
            allocator.deallocate(this, 1);
        }

        T* get() override
        {
            return &m_object;
        }

        // Cannot be deleted by calling dtor
        ~aux_alloc() = delete;

    private:
        // An object itself
        T        m_object;
        Alloc    m_alloc;
    };

    // Pointers are stored in the single-linked circular list
    shared_ptr *m_next;
    // Helper object
    aux        *m_aux;

    // List traverse helpers
    shared_ptr* get_next() const;
    shared_ptr* get_prev() const;

    void set_next(shared_ptr *ptr);
    void set_prev(shared_ptr *ptr);
};


//------------------------------------------------------------------------------

template< typename T >
shared_ptr< T >::shared_ptr()
    :m_next{this}
    ,m_aux{nullptr}
{

}

template< typename T >
shared_ptr< T >::~shared_ptr()
{
    if (unique() && m_aux) {
        m_aux->destroy();
    } else {
        // Unlink me
        //ecl::cout << "unlink in desc" << ecl::endl;
        shared_ptr *prev = get_prev();
        shared_ptr *next = get_next();

        prev->set_next(next);
    }
}

template< typename T >
shared_ptr< T >::shared_ptr(shared_ptr &other)
    :m_next{&other}
    ,m_aux{other.m_aux}
{
    shared_ptr *prev = other.get_prev();
    prev->set_next(this);
}

template< typename T >
shared_ptr< T >::shared_ptr(shared_ptr &&other)
    :m_next{other.m_next}
    ,m_aux{other.m_aux}
{
    other.m_next = &other;
    other.m_aux = nullptr;
}

template< typename T >
shared_ptr< T >& shared_ptr< T >::operator=(shared_ptr &other)
{
    if (&other != this) {
        if (unique()) {
            // Release the resource
            if (m_aux) {
                m_aux->destroy();
                m_aux = nullptr;
            }
        } else {
            // Unlink me
            shared_ptr *prev = get_prev();
            shared_ptr *next = get_next();

            prev->set_next(next);
        }

        // Link me to the new company
        other.set_prev(this);
        this->set_next(&other);
        m_aux = other.m_aux;
    }

    return *this;
}

template< typename T >
shared_ptr< T >* shared_ptr< T >::get_next() const
{
    return this->m_next;
}

template< typename T >
shared_ptr< T >* shared_ptr< T >::get_prev() const
{
    shared_ptr *cur = this->m_next;
    while (cur->m_next != this) {
        cur = cur->m_next;
    }

    return cur;
}

template< typename T >
void shared_ptr< T >::set_next(shared_ptr *ptr)
{
    this->m_next = ptr;
}

template< typename T >
void shared_ptr< T >::set_prev(shared_ptr *ptr)
{
    this->get_prev()->set_next(ptr);
}

template< typename T >
bool shared_ptr< T >::unique() const
{
    return get_next() == this;
}
<commit_msg>Add reference counting to control block<commit_after>#ifndef ECL_MEMORY_HPP_
#define ECL_MEMORY_HPP_

#include <cstddef>

namespace ecl
{

template< typename T >
class shared_ptr;

template< typename T, class Alloc, class... Args >
shared_ptr< T > allocate_shared(const Alloc &alloc, Args... args);

template< typename T >
class shared_ptr
{
    template< typename U, class Alloc, class... Args >
    friend shared_ptr< U > allocate_shared(const Alloc& alloc, Args... args);

public:
    shared_ptr();
    ~shared_ptr();

    shared_ptr(shared_ptr &other);
    shared_ptr(shared_ptr &&other);
    shared_ptr& operator=(shared_ptr &other);

    // Returns true if this pointer holds the last remaining node
    bool unique() const;

    // Returs a value itself
    T* get() const;

private:
    // The base helper class
    class aux
    {
    public:
        aux()
            :m_cnt{0} { }

        // Gets object
        virtual T* get() = 0;
        // Increments/decrements/gets the reference counter
        size_t inc() { return ++m_cnt; }
        size_t dec() { return --m_cnt; }
        size_t ref() { return m_cnt;   }

        // Destroys payload and aux itself.
        // _Must_ be called to destroy whole object
        virtual void destroy() = 0;
    private:
        size_t m_cnt;
    };

    // Type-erased helper class
    template< class Alloc, class... Args >
    class aux_alloc : public aux
    {
    public:
        aux_alloc(const Alloc &a, Args... args)
            :aux{}
            ,m_object{args...}
            ,m_alloc{a}
        { }

        virtual void destroy() override
        {
            m_object.~T();
            auto allocator = m_alloc.template rebind< aux_alloc >();
            allocator.deallocate(this, 1);
        }

        T* get() override
        {
            return &m_object;
        }

        // Cannot be deleted by calling dtor
        ~aux_alloc() = delete;

    private:
        // An object itself
        T        m_object;
        Alloc    m_alloc;
    };

    // Helper object
    aux        *m_aux;
};

//------------------------------------------------------------------------------

template< typename T >
shared_ptr< T >::shared_ptr()
    :m_aux{nullptr}
{

}

template< typename T >
shared_ptr< T >::~shared_ptr()
{
    if (m_aux) {
        if (unique()) {
            m_aux->destroy();
        } else {
            m_aux->dec();
        }
    }
}

template< typename T >
shared_ptr< T >::shared_ptr(shared_ptr &other)
    :m_aux{other.m_aux}
{

    if (m_aux)
        m_aux->inc();
    // Else do nothing. Copy constructing from empty shared pointer is allowed.
}

template< typename T >
shared_ptr< T >::shared_ptr(shared_ptr &&other)
    :m_aux{other.m_aux}
{
    other.m_aux = nullptr;
}

template< typename T >
shared_ptr< T >& shared_ptr< T >::operator=(shared_ptr &other)
{
    if (&other != this) {
        if (unique()) {
            // Release the resource
            if (m_aux) {
                m_aux->destroy();
                m_aux = nullptr;
            }
        } else {
            // If it is not single shrared pointer,
            // then auxilary object is guaranteed to exist.
            m_aux->dec();
        }

        m_aux = other.m_aux;

        // Empty shared pointer assigment can be allowed, too.
        if (m_aux)
            m_aux->inc();
    }

    return *this;
}

template< typename T >
bool shared_ptr< T >::unique() const
{
    return m_aux ? m_aux->ref() == 0 : 0;
}

{'fraction_non_alphanumeric': 0.14144649052575395, 'fraction_numerical': 0.0012009607686148918, 'mean_word_length': 1.9095496894409938, 'pattern_counts': {'":': 0, '<': 60, '<?xml version=': 0, '>': 76, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6117720', 'n_tokens_mistral': 2455, 'n_tokens_neox': 2369, 'n_words': 653}