blob: b6054ed5ee41a32e158297a7add9f752531ce228 [file] [log] [blame]
// 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_