/** * \file imperative/python/src/intrusive_list.h * MegEngine is Licensed under the Apache License, Version 2.0 (the "License") * * Copyright (c) 2014-2021 Megvii Inc. All rights reserved. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ #include "megbrain/utils/metahelper.h" namespace mgb::imperative::python::intrusive_list { // copy policy struct after_t {}; struct before_t {}; struct disable_t {}; template struct Tail; // invariant: next->prev == this template struct Head { Tail* next; Head(Tail* node = nullptr) : next(node) {} Head(const Head&) = delete; Head& operator=(const Head&) = delete; Head(Head&& rhs) : next(rhs.next) { rhs.next = nullptr; if (next) { next->prev = this; } } Head& operator=(Head&& rhs) { mgb_assert(!next); next = rhs.next; rhs.next = nullptr; if (next) { next->prev = this; } return *this; } ~Head() { if (next) { next->prev = nullptr; } } }; // invariant: prev->next == this template struct Tail { Head* prev; Tail(Head* node = nullptr) : prev(node) {} Tail(const Tail&) = delete; Tail& operator=(const Tail&) = delete; Tail(Tail&& rhs) : prev(rhs.prev) { rhs.prev = nullptr; if (prev) { prev->next = this; } } Tail& operator=(Tail&& rhs) { mgb_assert(!prev); prev = rhs.prev; rhs.prev = nullptr; if (prev) { prev->next = this; } return *this; } ~Tail() { if (prev) { prev->next = nullptr; } } }; template struct Node; template class Iterator { T* ptr; void inc() {ptr = static_cast(ptr->Head::next);} void dec() {ptr = static_cast(ptr->Head::prev);} public: Iterator(Head& head) : ptr(static_cast(head.next)) {} Iterator(Tail& tail) : ptr(static_cast(tail.prev)) {} template Iterator(Node& node) : ptr(static_cast(&node)) {} T& operator*() {return *static_cast(ptr);} T* operator->() {return static_cast(ptr);} operator bool() {return ptr;} bool operator==(const Iterator& rhs) {return ptr == rhs.ptr;} Iterator& operator++() {inc(); return *this;} Iterator& operator--() {dec(); return *this;} Iterator operator++(int) {auto ret = *this; inc(); return ret;} Iterator operator--(int) {auto ret = *this; dec(); return ret;} }; // Node in a doubly linked list. Unlike std::list, nodes are not owned by a container. // Instead, nodes may join or leave a list freely. // NOTE: Derived classes have to explicitly declare copy / assignment as default, // otherwise the compiler generated version would use the const T& signature, // which is deleted. template struct Node : Tail, Node, T>>, Head, Node, T>> { private: using this_t = Node; using U = std::conditional_t, this_t, T>; public: using head_t = Head; using tail_t = Tail; using head_t::next; using tail_t::prev; Node() = default; Node(const this_t&) = delete; this_t& operator=(const this_t&) = delete; //! constructed node is inserted after the input node Node(after_t, head_t& node) : tail_t(&node), head_t(node.next) { node.next = this; if (next) { next->prev = this; } } //! constructed node is inserted before the input node Node(before_t, tail_t& node) : head_t(&node), tail_t(node.prev) { node.prev = this; if (prev) { prev->next = this; } } Node(this_t&& rhs) : tail_t(rhs.prev), head_t(rhs.next) { rhs.prev = nullptr; rhs.next = nullptr; if (prev) { prev->next = this; } if (next) { next->prev = this; } } Node& operator=(this_t&& rhs) { unlink(); prev = rhs.prev; next = rhs.next; rhs.prev = nullptr; rhs.next = nullptr; if (prev) { prev->next = this; } if (next) { next->prev = this; } return *this; } template || std::is_same_v, void>> Node(this_t& rhs) : Node(policy{}, rhs) {} template || std::is_same_v, void>> this_t& operator=(this_t& rhs) { insert(policy{}, rhs); return *this; } void unlink() { if (prev) { prev->next = next; } if (next) { next->prev = prev; } prev = nullptr; next = nullptr; } //! this node is unlinked from its list and inserted after the input node void insert(after_t, head_t& node) { unlink(); prev = &node; next = node.next; node.next = this; if (next) { next->prev = this; } } //! this node is unlinked from its list and inserted before the input node void insert(before_t, tail_t& node) { unlink(); next = &node; prev = node.prev; node.prev = this; if (prev) { prev->next = this; } } void insert_before(tail_t& node) {insert(before_t{}, node);} void insert_after(head_t& node) {insert(after_t{}, node);} ~Node() { unlink(); } }; } // namespace mgb::imperative::python::intrusive_list