| // Copyright 2023 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_LINK_MAP_LIST_H_ |
| #define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_LINK_MAP_LIST_H_ |
| |
| #include <iterator> |
| |
| #include "layout.h" |
| #include "svr4-abi.h" |
| |
| namespace elfldltl { |
| |
| // Forward declaration; see below. |
| struct LinkMapListDefaultTraits; |
| |
| // This provides a container API with bidirectional iterators for a |
| // doubly-linked list that uses the traditional `struct link_map` (see |
| // elfldltl::Elf<...>::LinkMap<...> in <lib/elfldltl/svr4-abi.h>) as part |
| // of its element type layout. The first template parameter gives the type |
| // of elements, what will be `value_type` in the container and its |
| // iterators. The second template parameter defines how the LinkMap member |
| // is found therein via a "traits" type (see below). |
| template <typename ElementType = Elf<>::LinkMap<>, class Traits = LinkMapListDefaultTraits> |
| class LinkMapList { |
| template <bool Reverse, bool Const> |
| class IteratorImpl; |
| |
| public: |
| using value_type = ElementType; |
| using pointer = value_type*; |
| using reference = value_type&; |
| using const_reference = const value_type&; |
| using difference_type = ptrdiff_t; |
| using size_type = size_t; |
| |
| using iterator = IteratorImpl<false, false>; |
| using reverse_iterator = IteratorImpl<true, false>; |
| using const_iterator = IteratorImpl<false, true>; |
| using const_reverse_iterator = IteratorImpl<true, true>; |
| |
| constexpr LinkMapList() = default; |
| |
| constexpr LinkMapList(const LinkMapList&) = default; |
| |
| constexpr explicit LinkMapList(const value_type* head) : head_(head) {} |
| |
| constexpr LinkMapList& operator=(const LinkMapList&) = default; |
| |
| constexpr iterator begin() { return iterator(this, head_); } |
| |
| constexpr iterator end() { return iterator(this, nullptr); } |
| |
| constexpr const_iterator begin() const { return const_iterator(this, head_); } |
| |
| constexpr const_iterator end() const { return const_iterator(this, nullptr); } |
| |
| constexpr reverse_iterator rbegin() { |
| reverse_iterator it(this, nullptr); |
| if (head_) { |
| ++it; |
| } |
| return it; |
| } |
| |
| constexpr reverse_iterator rend() { |
| reverse_iterator it(this, head_); |
| if (head_) { |
| ++it; |
| } |
| return it; |
| } |
| |
| constexpr const_reverse_iterator rbegin() const { return const_reverse_iterator(this, nullptr); } |
| |
| constexpr const_reverse_iterator rend() const { return const_reverse_iterator(this, head_); } |
| |
| constexpr bool empty() const { return !head_; } |
| |
| private: |
| constexpr const value_type* Next(const value_type* pos) const { |
| if (!pos) { |
| // Moving "forwards" from rend() gets the head. |
| assert(head_); |
| return head_; |
| } |
| if (auto* next = Traits::ElementToLinkMap(*pos).next.get()) { |
| return &(Traits::LinkMapToElement(*next)); |
| } |
| // We've reached the end, so cache the tail if not already known. |
| if (!tail_) { |
| tail_ = pos; |
| } |
| return nullptr; |
| } |
| |
| constexpr const value_type* Prev(const value_type* pos) const { |
| if (!pos) { |
| // Moving backwards from the end gets the tail. |
| if (!tail_) { |
| // It's not already known, so find it forward from the head and |
| // cache it. Note this takes O(n) time. |
| assert(head_); |
| const value_type* pos = head_; |
| do { |
| pos = Next(pos); |
| } while (pos); |
| assert(tail_); |
| } |
| return tail_; |
| } |
| if (auto* prev = Traits::ElementToLinkMap(*pos).prev.get()) { |
| return &(Traits::LinkMapToElement(*prev)); |
| } |
| return nullptr; |
| } |
| |
| const value_type* head_ = nullptr; |
| mutable const value_type* tail_ = nullptr; |
| }; |
| |
| // This is the default "traits" type for LinkMapList, which only works when |
| // the element type is LinkMap itself, or really is any type with members |
| // named `next` and `prev` that are pointers to that same type. |
| struct LinkMapListDefaultTraits { |
| // This must translate a `const LinkMap&` to a `const value_type&`. |
| template <class Element> |
| static constexpr Element& LinkMapToElement(Element& map) { |
| return map; |
| } |
| |
| // This must translate a `const value_type&` to a `const LinkMap&`. |
| template <class Element> |
| static constexpr Element& ElementToLinkMap(Element& map) { |
| return map; |
| } |
| }; |
| |
| // This defines the "traits" type for a struct with a first member named |
| // `link_map` that is of the LinkMap type. |
| template <class ElementType> |
| struct LinkMapListInFirstMemberTraits { |
| using LinkMapType = decltype(std::declval<ElementType>().link_map); |
| |
| static const ElementType& LinkMapToElement(const LinkMapType& map) { |
| if constexpr (std::is_standard_layout_v<ElementType>) { |
| static_assert(offsetof(ElementType, link_map) == 0); |
| } else { |
| // It's not kosher to use offsetof here, but this should get compiled |
| // away so there's no runtime test at all (but maybe a runtime failure). |
| assert(&(reinterpret_cast<const ElementType&>(map).link_map) == &map); |
| } |
| return reinterpret_cast<const ElementType&>(map); |
| } |
| |
| static const LinkMapType& ElementToLinkMap(const ElementType& element) { |
| return element.link_map; |
| } |
| }; |
| |
| template <typename ElementType, class Traits> |
| template <bool Reverse, bool Const> |
| class LinkMapList<ElementType, Traits>::IteratorImpl { |
| public: |
| using value_type = LinkMapList::value_type; |
| using pointer = LinkMapList::pointer; |
| using reference = LinkMapList::reference; |
| using difference_type = LinkMapList::difference_type; |
| using iterator_category = std::bidirectional_iterator_tag; |
| |
| constexpr IteratorImpl() = default; |
| |
| constexpr IteratorImpl(const IteratorImpl&) = default; |
| |
| // const_iterator is constructible from iterator. |
| template <bool C = Const, typename = std::enable_if_t<C>> |
| constexpr IteratorImpl(const IteratorImpl<Reverse, false>& other) |
| : list_(other.list_), pos_(other.pos_) {} |
| |
| constexpr IteratorImpl& operator=(const IteratorImpl&) = default; |
| |
| constexpr bool operator==(const IteratorImpl& other) const { return pos_ == other.pos_; } |
| |
| constexpr bool operator!=(const IteratorImpl& other) const { return !(*this == other); } |
| |
| constexpr auto& operator*() const { return *operator->(); } |
| |
| constexpr auto* operator->() const { |
| if constexpr (Const) { |
| return pos_; |
| } else { |
| return const_cast<value_type*>(pos_); |
| } |
| } |
| |
| constexpr IteratorImpl& operator++() { // prefix |
| pos_ = (list_->*kNext)(pos_); |
| return *this; |
| } |
| |
| constexpr IteratorImpl operator++(int) { // postfix |
| IteratorImpl old = *this; |
| ++*this; |
| return old; |
| } |
| |
| constexpr IteratorImpl& operator--() { // prefix |
| pos_ = (list_->*kPrev)(pos_); |
| return *this; |
| } |
| |
| constexpr IteratorImpl operator--(int) { // postfix |
| IteratorImpl old = *this; |
| --*this; |
| return old; |
| } |
| |
| private: |
| friend LinkMapList; |
| |
| static constexpr auto kNext = Reverse ? &LinkMapList::Prev : &LinkMapList::Next; |
| static constexpr auto kPrev = !Reverse ? &LinkMapList::Prev : &LinkMapList::Next; |
| |
| constexpr IteratorImpl(const LinkMapList* list, const value_type* pos) : list_{list}, pos_{pos} {} |
| |
| const LinkMapList* list_ = nullptr; |
| const value_type* pos_ = nullptr; |
| }; |
| |
| } // namespace elfldltl |
| |
| #endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_LINK_MAP_LIST_H_ |