diff options
Diffstat (limited to 'libbase')
| -rw-r--r-- | libbase/include/core/adt/ilist_node.h | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/libbase/include/core/adt/ilist_node.h b/libbase/include/core/adt/ilist_node.h new file mode 100644 index 0000000..9ca9ed3 --- /dev/null +++ b/libbase/include/core/adt/ilist_node.h @@ -0,0 +1,470 @@ +/* + RCC-Project + + Copyright (C) 2026 Richard Qin + + This file is part of RCC-Project. + + RCC-Project is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your option) any + later version. + + RCC-Project 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 General Public License for more details. + + You should have received a copy of the GNU General Public License along with + RCC-Project; see the file COPYING. If not see + <http://www.gnu.org/licenses/>. +*/ + +//===- core/adt/ilist_node.h - Intrusive Linked List Helper -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the ilist_node class template, which is a convenient +/// base class for creating classes that can be used with ilists. +/// +//===----------------------------------------------------------------------===// + +#ifndef LIBBASE_ADT_ILIST_NODE_H +#define LIBBASE_ADT_ILIST_NODE_H + +#include "core/adt/ilist_node_base.h" +#include "core/adt/ilist_node_options.h" + +#include <type_traits> + +namespace libbase +{ + +namespace ilist_detail +{ + +struct NodeAccess; + +/// Mixin base class that is used to add \a getParent() and +/// \a setParent(ParentTy*) methods to \a ilist_node_impl iff \a ilist_parent +/// has been set in the list options. +template <class NodeTy, class ParentTy> class node_parent_access +{ +public: + inline const ParentTy * + getParent () const + { + return static_cast<const NodeTy *> (this)->getNodeBaseParent (); + } + inline ParentTy * + getParent () + { + return static_cast<NodeTy *> (this)->getNodeBaseParent (); + } + void + setParent (ParentTy *Parent) + { + return static_cast<NodeTy *> (this)->setNodeBaseParent (Parent); + } +}; +template <class NodeTy> class node_parent_access<NodeTy, void> +{ +}; + +} // end namespace ilist_detail + +template <class OptionsT, bool IsReverse, bool IsConst> class ilist_iterator; +template <class OptionsT, bool IsReverse, bool IsConst> +class ilist_iterator_w_bits; +template <class OptionsT> class ilist_sentinel; + +// Selector for which iterator type to pick given the iterator-bits node option. +template <class OptionsT, bool IsReverse, bool IsConst> +using ilist_select_iterator_type = + std::conditional_t<OptionsT::has_iterator_bits, + ilist_iterator_w_bits<OptionsT, IsReverse, IsConst>, + ilist_iterator<OptionsT, IsReverse, IsConst>>; + +/// Implementation for an ilist node. +/// +/// Templated on an appropriate \a ilist_detail::node_options, usually computed +/// by \a ilist_detail::compute_node_options. +/// +/// This is a wrapper around \a ilist_node_base whose main purpose is to +/// provide type safety: you can't insert nodes of \a ilist_node_impl into the +/// wrong \a simple_ilist or \a iplist. +template <class OptionsT> +class ilist_node_impl + : OptionsT::node_base_type, + public ilist_detail::node_parent_access<ilist_node_impl<OptionsT>, + typename OptionsT::parent_ty> +{ + using value_type = typename OptionsT::value_type; + using node_base_type = typename OptionsT::node_base_type; + using list_base_type = typename OptionsT::list_base_type; + + friend typename OptionsT::list_base_type; + friend struct ilist_detail::NodeAccess; + friend class ilist_sentinel<OptionsT>; + + friend class ilist_detail::node_parent_access<ilist_node_impl<OptionsT>, + typename OptionsT::parent_ty>; + friend class ilist_iterator<OptionsT, false, false>; + friend class ilist_iterator<OptionsT, false, true>; + friend class ilist_iterator<OptionsT, true, false>; + friend class ilist_iterator<OptionsT, true, true>; + friend class ilist_iterator_w_bits<OptionsT, false, false>; + friend class ilist_iterator_w_bits<OptionsT, false, true>; + friend class ilist_iterator_w_bits<OptionsT, true, false>; + friend class ilist_iterator_w_bits<OptionsT, true, true>; + +protected: + using self_iterator = ilist_select_iterator_type<OptionsT, false, false>; + using const_self_iterator = ilist_select_iterator_type<OptionsT, false, true>; + using reverse_self_iterator = + ilist_select_iterator_type<OptionsT, true, false>; + using const_reverse_self_iterator = + ilist_select_iterator_type<OptionsT, true, true>; + + ilist_node_impl () = default; + +private: + ilist_node_impl * + getPrev () + { + return static_cast<ilist_node_impl *> (node_base_type::getPrev ()); + } + + ilist_node_impl * + getNext () + { + return static_cast<ilist_node_impl *> (node_base_type::getNext ()); + } + + const ilist_node_impl * + getPrev () const + { + return static_cast<ilist_node_impl *> (node_base_type::getPrev ()); + } + + const ilist_node_impl * + getNext () const + { + return static_cast<ilist_node_impl *> (node_base_type::getNext ()); + } + + void + setPrev (ilist_node_impl *N) + { + node_base_type::setPrev (N); + } + void + setNext (ilist_node_impl *N) + { + node_base_type::setNext (N); + } + +public: + self_iterator + getIterator () + { + return self_iterator (*this); + } + const_self_iterator + getIterator () const + { + return const_self_iterator (*this); + } + + reverse_self_iterator + getReverseIterator () + { + return reverse_self_iterator (*this); + } + + const_reverse_self_iterator + getReverseIterator () const + { + return const_reverse_self_iterator (*this); + } + + // Under-approximation, but always available for assertions. + using node_base_type::isKnownSentinel; + + /// Check whether this is the sentinel node. + /// + /// This requires sentinel tracking to be explicitly enabled. Use the + /// ilist_sentinel_tracking<true> option to get this API. + /// + /// Rather than using static_assert to enforce the API is not called when + /// configured with is_sentinel_tracking_explicit=false, the method is + /// conditionally provided using std::enable_if. This way, clients of + /// ilist_node_impl can be fully instantiated for DLLExport on Windows. + template <typename T = OptionsT> + std::enable_if_t<T::is_sentinel_tracking_explicit, bool> + isSentinel () const + { + return node_base_type::isSentinel (); + } +}; + +/// An intrusive list node. +/// +/// A base class to enable membership in intrusive lists, including \a +/// simple_ilist, \a iplist, and \a ilist. The first template parameter is the +/// \a value_type for the list. +/// +/// An ilist node can be configured with compile-time options to change +/// behaviour and/or add API. +/// +/// By default, an \a ilist_node does not track whether it is the list sentinel +/// (an instance of \a ilist_sentinel). The function \a isKnownSentinel() always +/// returns \c false tracking is off. Sentinel tracking steals a bit from the +/// "prev" link, which adds a mask operation when decrementing an iterator, but +/// enables bug-finding assertions in \a ilist_iterator. +/// +/// To turn sentinel tracking on all the time, pass in the +/// ilist_sentinel_tracking<true> template parameter. This also enables the \a +/// isSentinel() function. The same option must be passed to the intrusive +/// list. (ilist_sentinel_tracking<false> turns sentinel tracking off all the +/// time.) +/// +/// A type can inherit from ilist_node multiple times by passing in different +/// \a ilist_tag options. This allows a single instance to be inserted into +/// multiple lists simultaneously, where each list is given the same tag. +/// +/// \example +/// struct A {}; +/// struct B {}; +/// struct N : ilist_node<N, ilist_tag<A>>, ilist_node<N, ilist_tag<B>> {}; +/// +/// void foo() { +/// simple_ilist<N, ilist_tag<A>> ListA; +/// simple_ilist<N, ilist_tag<B>> ListB; +/// N N1; +/// ListA.push_back(N1); +/// ListB.push_back(N1); +/// } +/// \endexample +/// +/// When the \a ilist_parent<ParentTy> option is passed to an ilist_node and the +/// owning ilist, each node contains a pointer to the ilist's owner. This adds +/// \a getParent() and \a setParent(ParentTy*) methods to the ilist_node, which +/// will be used for node access by the ilist if the node class publicly +/// inherits from \a ilist_node_with_parent. By default, setParent() is not +/// automatically called by the ilist; a SymbolTableList will call setParent() +/// on inserted nodes, but the sentinel must still be manually set after the +/// list is created (e.g. SymTabList.end()->setParent(Parent)). +/// +/// The primary benefit of using ilist_parent is that a parent +/// pointer will be stored in the sentinel, meaning that you can safely use \a +/// ilist_iterator::getNodeParent() to get the node parent from any valid (i.e. +/// non-null) iterator, even one that points to a sentinel value. +/// +/// See \a is_valid_option for steps on adding a new option. +template <class T, class... Options> +class ilist_node + : public ilist_node_impl< + typename ilist_detail::compute_node_options<T, Options...>::type> +{ + static_assert (ilist_detail::check_options<Options...>::value, + "Unrecognized node option!"); +}; + +namespace ilist_detail +{ + +/// An access class for ilist_node private API. +/// +/// This gives access to the private parts of ilist nodes. Nodes for an ilist +/// should friend this class if they inherit privately from ilist_node. +/// +/// Using this class outside of the ilist implementation is unsupported. +struct NodeAccess +{ +protected: + template <class OptionsT> + static ilist_node_impl<OptionsT> * + getNodePtr (typename OptionsT::pointer N) + { + return N; + } + + template <class OptionsT> + static const ilist_node_impl<OptionsT> * + getNodePtr (typename OptionsT::const_pointer N) + { + return N; + } + + template <class OptionsT> + static typename OptionsT::pointer + getValuePtr (ilist_node_impl<OptionsT> *N) + { + return static_cast<typename OptionsT::pointer> (N); + } + + template <class OptionsT> + static typename OptionsT::const_pointer + getValuePtr (const ilist_node_impl<OptionsT> *N) + { + return static_cast<typename OptionsT::const_pointer> (N); + } + + template <class OptionsT> + static ilist_node_impl<OptionsT> * + getPrev (ilist_node_impl<OptionsT> &N) + { + return N.getPrev (); + } + + template <class OptionsT> + static ilist_node_impl<OptionsT> * + getNext (ilist_node_impl<OptionsT> &N) + { + return N.getNext (); + } + + template <class OptionsT> + static const ilist_node_impl<OptionsT> * + getPrev (const ilist_node_impl<OptionsT> &N) + { + return N.getPrev (); + } + + template <class OptionsT> + static const ilist_node_impl<OptionsT> * + getNext (const ilist_node_impl<OptionsT> &N) + { + return N.getNext (); + } +}; + +template <class OptionsT> struct SpecificNodeAccess : NodeAccess +{ +protected: + using pointer = typename OptionsT::pointer; + using const_pointer = typename OptionsT::const_pointer; + using node_type = ilist_node_impl<OptionsT>; + + static node_type * + getNodePtr (pointer N) + { + return NodeAccess::getNodePtr<OptionsT> (N); + } + + static const node_type * + getNodePtr (const_pointer N) + { + return NodeAccess::getNodePtr<OptionsT> (N); + } + + static pointer + getValuePtr (node_type *N) + { + return NodeAccess::getValuePtr<OptionsT> (N); + } + + static const_pointer + getValuePtr (const node_type *N) + { + return NodeAccess::getValuePtr<OptionsT> (N); + } +}; + +} // end namespace ilist_detail + +template <class OptionsT> +class ilist_sentinel : public ilist_node_impl<OptionsT> +{ +public: + ilist_sentinel () + { + this->initializeSentinel (); + reset (); + } + + void + reset () + { + this->setPrev (this); + this->setNext (this); + } + + bool + empty () const + { + return this == this->getPrev (); + } +}; + +/// An ilist node that can access its parent list. +/// +/// Requires \c NodeTy to have \a getParent() to find the parent node, and the +/// \c ParentTy to have \a getSublistAccess() to get a reference to the list. +template <typename NodeTy, typename ParentTy, class... Options> +class ilist_node_with_parent : public ilist_node<NodeTy, Options...> +{ +protected: + ilist_node_with_parent () = default; + +private: + /// Forward to NodeTy::getParent(). + /// + /// Note: do not use the name "getParent()". We want a compile error + /// (instead of recursion) when the subclass fails to implement \a + /// getParent(). + const ParentTy * + getNodeParent () const + { + return static_cast<const NodeTy *> (this)->getParent (); + } + +public: + /// @name Adjacent Node Accessors + /// @{ + /// Get the previous node, or \c nullptr for the list head. + NodeTy * + getPrevNode () + { + // Should be separated to a reused function, but then we couldn't use auto + // (and would need the type of the list). + const auto &List = + getNodeParent ()->*(ParentTy::getSublistAccess ((NodeTy *)nullptr)); + return List.getPrevNode (*static_cast<NodeTy *> (this)); + } + + /// Get the previous node, or \c nullptr for the list head. + const NodeTy * + getPrevNode () const + { + return const_cast<ilist_node_with_parent *> (this)->getPrevNode (); + } + + /// Get the next node, or \c nullptr for the list tail. + NodeTy * + getNextNode () + { + // Should be separated to a reused function, but then we couldn't use auto + // (and would need the type of the list). + const auto &List = + getNodeParent ()->*(ParentTy::getSublistAccess ((NodeTy *)nullptr)); + return List.getNextNode (*static_cast<NodeTy *> (this)); + } + + /// Get the next node, or \c nullptr for the list tail. + const NodeTy * + getNextNode () const + { + return const_cast<ilist_node_with_parent *> (this)->getNextNode (); + } + /// @} +}; + +} // end namespace libbase + +#endif // LIBBASE_ADT_ILIST_NODE_H |
