NodeHandle.h#

Fully qualified name: carb/container/detail/NodeHandle.h

File members: carb/container/detail/NodeHandle.h

// Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "../../Defines.h"
#include "AllocatorTraits.h"

namespace carb
{
namespace container
{

namespace detail
{

struct node_handle_accessor
{
    template <class NodeHandleType>
    static typename NodeHandleType::node* getNodePtr(NodeHandleType& handle)
    {
        return handle.getNodePtr();
    }

    template <class NodeHandleType>
    static NodeHandleType construct(typename NodeHandleType::node* ptr)
    {
        return NodeHandleType(ptr);
    }

    template <class NodeHandleType>
    static void deactivate(NodeHandleType& handle)
    {
        handle.deactivate();
    }
};

template <class Value, class Node, class Allocator>
class node_handle_base
{
public:
    using allocator_type = Allocator;

    node_handle_base() : m_members(ValueInitFirst{}, nullptr)
    {
    }
    node_handle_base(node_handle_base&& other)
        : m_members(InitBoth{}, std::move(other.m_members.first()), std::exchange(other.m_members.second, nullptr))
    {
    }
    ~node_handle_base()
    {
        _destroy();
    }

    node_handle_base& operator=(node_handle_base&& other)
    {
        _destroy();
        m_members.second = other.m_members.second;
        move_assign_allocators(m_members.first(), other.m_members.first());
        other.deactivate();
        return *this;
    }

    [[nodiscard]] bool empty() const noexcept
    {
        return m_members.second == nullptr;
    }

    explicit operator bool() const noexcept
    {
        return !empty();
    }

    void swap(node_handle_base& other)
    {
        using std::swap;
        swap_allocators(m_members.first(), other.m_members.first());
        swap(m_members.second, other.m_members.second);
    }

    allocator_type get_allocator() const
    {
        return m_members.first();
    }

protected:
    using node = Node;
    using AllocatorTraitsType = std::allocator_traits<allocator_type>;

    node_handle_base(node* p) : m_members(ValueInitFirst{}, p)
    {
    }

    void _destroy()
    {
        if (m_members.second)
        {
            AllocatorTraitsType::destroy(m_members.first(), m_members.second->storage());
            typename AllocatorTraitsType::template rebind_alloc<node> node_allocator(m_members.first());
            node_allocator.deallocate(m_members.second, 1);
        }
    }

    node* getNodePtr() const noexcept
    {
        return m_members.second;
    }

    void deactivate() noexcept
    {
        m_members.second = nullptr;
    }

    EmptyMemberPair<allocator_type, node*> m_members;
};

template <class Key, class Value, class Node, class Allocator>
class node_handle : public node_handle_base<Value, Node, Allocator>
{
    using BaseType = node_handle_base<Value, Node, Allocator>;

public:
    using key_type = Key;
    using mapped_type = typename Value::second_type;
    using allocator_type = typename BaseType::allocator_type;

    node_handle() = default;

    key_type& key() const noexcept
    {
        CARB_ASSERT(!this->empty());
        return *const_cast<key_type*>(&(this->m_members.second->value().first));
    }

    mapped_type& mapped() const noexcept
    {
        CARB_ASSERT(!this->empty());
        return this->m_members.second->value().second;
    }

private:
    friend struct node_handle_accessor;

    node_handle(typename BaseType::node* p) : BaseType(p)
    {
    }
};

template <class Key, class Node, class Allocator>
class node_handle<Key, Key, Node, Allocator> : public node_handle_base<Key, Node, Allocator>
{
    using BaseType = node_handle_base<Key, Node, Allocator>;

public:
    using value_type = Key;
    using allocator_type = typename BaseType::allocator_type;

    node_handle() = default;

    value_type& value() const
    {
        CARB_ASSERT(!this->empty());
        return *const_cast<value_type*>(&(this->m_members.second->value()));
    }

private:
    friend struct node_handle_accessor;

    node_handle(typename BaseType::node* p) : BaseType(p)
    {
    }
};

template <class Key, class Value, class Node, class Allocator>
void swap(node_handle<Key, Value, Node, Allocator>& lhs, node_handle<Key, Value, Node, Allocator>& rhs)
{
    lhs.swap(rhs);
}

} // namespace detail

} // namespace container
} // namespace carb