xref: /openbsd-src/gnu/llvm/compiler-rt/lib/scudo/standalone/list.h (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- list.h --------------------------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick 
93cab2bb3Spatrick #ifndef SCUDO_LIST_H_
103cab2bb3Spatrick #define SCUDO_LIST_H_
113cab2bb3Spatrick 
123cab2bb3Spatrick #include "internal_defs.h"
133cab2bb3Spatrick 
143cab2bb3Spatrick namespace scudo {
153cab2bb3Spatrick 
163cab2bb3Spatrick // Intrusive POD singly and doubly linked list.
173cab2bb3Spatrick // An object with all zero fields should represent a valid empty list. clear()
183cab2bb3Spatrick // should be called on all non-zero-initialized objects before using.
193cab2bb3Spatrick 
203cab2bb3Spatrick template <class T> class IteratorBase {
213cab2bb3Spatrick public:
IteratorBase(T * CurrentT)223cab2bb3Spatrick   explicit IteratorBase(T *CurrentT) : Current(CurrentT) {}
233cab2bb3Spatrick   IteratorBase &operator++() {
243cab2bb3Spatrick     Current = Current->Next;
253cab2bb3Spatrick     return *this;
263cab2bb3Spatrick   }
273cab2bb3Spatrick   bool operator!=(IteratorBase Other) const { return Current != Other.Current; }
283cab2bb3Spatrick   T &operator*() { return *Current; }
293cab2bb3Spatrick 
303cab2bb3Spatrick private:
313cab2bb3Spatrick   T *Current;
323cab2bb3Spatrick };
333cab2bb3Spatrick 
343cab2bb3Spatrick template <class T> struct IntrusiveList {
emptyIntrusiveList353cab2bb3Spatrick   bool empty() const { return Size == 0; }
sizeIntrusiveList363cab2bb3Spatrick   uptr size() const { return Size; }
373cab2bb3Spatrick 
frontIntrusiveList383cab2bb3Spatrick   T *front() { return First; }
frontIntrusiveList393cab2bb3Spatrick   const T *front() const { return First; }
backIntrusiveList403cab2bb3Spatrick   T *back() { return Last; }
backIntrusiveList413cab2bb3Spatrick   const T *back() const { return Last; }
423cab2bb3Spatrick 
clearIntrusiveList433cab2bb3Spatrick   void clear() {
443cab2bb3Spatrick     First = Last = nullptr;
453cab2bb3Spatrick     Size = 0;
463cab2bb3Spatrick   }
473cab2bb3Spatrick 
483cab2bb3Spatrick   typedef IteratorBase<T> Iterator;
493cab2bb3Spatrick   typedef IteratorBase<const T> ConstIterator;
503cab2bb3Spatrick 
beginIntrusiveList513cab2bb3Spatrick   Iterator begin() { return Iterator(First); }
endIntrusiveList523cab2bb3Spatrick   Iterator end() { return Iterator(nullptr); }
533cab2bb3Spatrick 
beginIntrusiveList543cab2bb3Spatrick   ConstIterator begin() const { return ConstIterator(First); }
endIntrusiveList553cab2bb3Spatrick   ConstIterator end() const { return ConstIterator(nullptr); }
563cab2bb3Spatrick 
573cab2bb3Spatrick   void checkConsistency() const;
583cab2bb3Spatrick 
593cab2bb3Spatrick protected:
60d89ec533Spatrick   uptr Size = 0;
61d89ec533Spatrick   T *First = nullptr;
62d89ec533Spatrick   T *Last = nullptr;
633cab2bb3Spatrick };
643cab2bb3Spatrick 
checkConsistency()653cab2bb3Spatrick template <class T> void IntrusiveList<T>::checkConsistency() const {
663cab2bb3Spatrick   if (Size == 0) {
673cab2bb3Spatrick     CHECK_EQ(First, nullptr);
683cab2bb3Spatrick     CHECK_EQ(Last, nullptr);
693cab2bb3Spatrick   } else {
703cab2bb3Spatrick     uptr Count = 0;
713cab2bb3Spatrick     for (T *I = First;; I = I->Next) {
723cab2bb3Spatrick       Count++;
733cab2bb3Spatrick       if (I == Last)
743cab2bb3Spatrick         break;
753cab2bb3Spatrick     }
763cab2bb3Spatrick     CHECK_EQ(this->size(), Count);
773cab2bb3Spatrick     CHECK_EQ(Last->Next, nullptr);
783cab2bb3Spatrick   }
793cab2bb3Spatrick }
803cab2bb3Spatrick 
813cab2bb3Spatrick template <class T> struct SinglyLinkedList : public IntrusiveList<T> {
823cab2bb3Spatrick   using IntrusiveList<T>::First;
833cab2bb3Spatrick   using IntrusiveList<T>::Last;
843cab2bb3Spatrick   using IntrusiveList<T>::Size;
853cab2bb3Spatrick   using IntrusiveList<T>::empty;
863cab2bb3Spatrick 
push_backSinglyLinkedList873cab2bb3Spatrick   void push_back(T *X) {
883cab2bb3Spatrick     X->Next = nullptr;
893cab2bb3Spatrick     if (empty())
903cab2bb3Spatrick       First = X;
913cab2bb3Spatrick     else
923cab2bb3Spatrick       Last->Next = X;
933cab2bb3Spatrick     Last = X;
943cab2bb3Spatrick     Size++;
953cab2bb3Spatrick   }
963cab2bb3Spatrick 
push_frontSinglyLinkedList973cab2bb3Spatrick   void push_front(T *X) {
983cab2bb3Spatrick     if (empty())
993cab2bb3Spatrick       Last = X;
1003cab2bb3Spatrick     X->Next = First;
1013cab2bb3Spatrick     First = X;
1023cab2bb3Spatrick     Size++;
1033cab2bb3Spatrick   }
1043cab2bb3Spatrick 
pop_frontSinglyLinkedList1053cab2bb3Spatrick   void pop_front() {
1063cab2bb3Spatrick     DCHECK(!empty());
1073cab2bb3Spatrick     First = First->Next;
1083cab2bb3Spatrick     if (!First)
1093cab2bb3Spatrick       Last = nullptr;
1103cab2bb3Spatrick     Size--;
1113cab2bb3Spatrick   }
1123cab2bb3Spatrick 
113*810390e3Srobert   // Insert X next to Prev
insertSinglyLinkedList114*810390e3Srobert   void insert(T *Prev, T *X) {
115*810390e3Srobert     DCHECK(!empty());
116*810390e3Srobert     DCHECK_NE(Prev, nullptr);
117*810390e3Srobert     DCHECK_NE(X, nullptr);
118*810390e3Srobert     X->Next = Prev->Next;
119*810390e3Srobert     Prev->Next = X;
120*810390e3Srobert     if (Last == Prev)
121*810390e3Srobert       Last = X;
122*810390e3Srobert     ++Size;
123*810390e3Srobert   }
124*810390e3Srobert 
extractSinglyLinkedList1253cab2bb3Spatrick   void extract(T *Prev, T *X) {
1263cab2bb3Spatrick     DCHECK(!empty());
1273cab2bb3Spatrick     DCHECK_NE(Prev, nullptr);
1283cab2bb3Spatrick     DCHECK_NE(X, nullptr);
1293cab2bb3Spatrick     DCHECK_EQ(Prev->Next, X);
1303cab2bb3Spatrick     Prev->Next = X->Next;
1313cab2bb3Spatrick     if (Last == X)
1323cab2bb3Spatrick       Last = Prev;
1333cab2bb3Spatrick     Size--;
1343cab2bb3Spatrick   }
1353cab2bb3Spatrick 
append_backSinglyLinkedList1363cab2bb3Spatrick   void append_back(SinglyLinkedList<T> *L) {
1373cab2bb3Spatrick     DCHECK_NE(this, L);
1383cab2bb3Spatrick     if (L->empty())
1393cab2bb3Spatrick       return;
1403cab2bb3Spatrick     if (empty()) {
1413cab2bb3Spatrick       *this = *L;
1423cab2bb3Spatrick     } else {
1433cab2bb3Spatrick       Last->Next = L->First;
1443cab2bb3Spatrick       Last = L->Last;
1453cab2bb3Spatrick       Size += L->size();
1463cab2bb3Spatrick     }
1473cab2bb3Spatrick     L->clear();
1483cab2bb3Spatrick   }
1493cab2bb3Spatrick };
1503cab2bb3Spatrick 
1513cab2bb3Spatrick template <class T> struct DoublyLinkedList : IntrusiveList<T> {
1523cab2bb3Spatrick   using IntrusiveList<T>::First;
1533cab2bb3Spatrick   using IntrusiveList<T>::Last;
1543cab2bb3Spatrick   using IntrusiveList<T>::Size;
1553cab2bb3Spatrick   using IntrusiveList<T>::empty;
1563cab2bb3Spatrick 
push_frontDoublyLinkedList1573cab2bb3Spatrick   void push_front(T *X) {
1583cab2bb3Spatrick     X->Prev = nullptr;
1593cab2bb3Spatrick     if (empty()) {
1603cab2bb3Spatrick       Last = X;
1613cab2bb3Spatrick     } else {
1623cab2bb3Spatrick       DCHECK_EQ(First->Prev, nullptr);
1633cab2bb3Spatrick       First->Prev = X;
1643cab2bb3Spatrick     }
1653cab2bb3Spatrick     X->Next = First;
1663cab2bb3Spatrick     First = X;
1673cab2bb3Spatrick     Size++;
1683cab2bb3Spatrick   }
1693cab2bb3Spatrick 
1703cab2bb3Spatrick   // Inserts X before Y.
insertDoublyLinkedList1713cab2bb3Spatrick   void insert(T *X, T *Y) {
1723cab2bb3Spatrick     if (Y == First)
1733cab2bb3Spatrick       return push_front(X);
1743cab2bb3Spatrick     T *Prev = Y->Prev;
1753cab2bb3Spatrick     // This is a hard CHECK to ensure consistency in the event of an intentional
1763cab2bb3Spatrick     // corruption of Y->Prev, to prevent a potential write-{4,8}.
1773cab2bb3Spatrick     CHECK_EQ(Prev->Next, Y);
1783cab2bb3Spatrick     Prev->Next = X;
1793cab2bb3Spatrick     X->Prev = Prev;
1803cab2bb3Spatrick     X->Next = Y;
1813cab2bb3Spatrick     Y->Prev = X;
1823cab2bb3Spatrick     Size++;
1833cab2bb3Spatrick   }
1843cab2bb3Spatrick 
push_backDoublyLinkedList1853cab2bb3Spatrick   void push_back(T *X) {
1863cab2bb3Spatrick     X->Next = nullptr;
1873cab2bb3Spatrick     if (empty()) {
1883cab2bb3Spatrick       First = X;
1893cab2bb3Spatrick     } else {
1903cab2bb3Spatrick       DCHECK_EQ(Last->Next, nullptr);
1913cab2bb3Spatrick       Last->Next = X;
1923cab2bb3Spatrick     }
1933cab2bb3Spatrick     X->Prev = Last;
1943cab2bb3Spatrick     Last = X;
1953cab2bb3Spatrick     Size++;
1963cab2bb3Spatrick   }
1973cab2bb3Spatrick 
pop_frontDoublyLinkedList1983cab2bb3Spatrick   void pop_front() {
1993cab2bb3Spatrick     DCHECK(!empty());
2003cab2bb3Spatrick     First = First->Next;
2013cab2bb3Spatrick     if (!First)
2023cab2bb3Spatrick       Last = nullptr;
2033cab2bb3Spatrick     else
2043cab2bb3Spatrick       First->Prev = nullptr;
2053cab2bb3Spatrick     Size--;
2063cab2bb3Spatrick   }
2073cab2bb3Spatrick 
2083cab2bb3Spatrick   // The consistency of the adjacent links is aggressively checked in order to
2093cab2bb3Spatrick   // catch potential corruption attempts, that could yield a mirrored
2103cab2bb3Spatrick   // write-{4,8} primitive. nullptr checks are deemed less vital.
removeDoublyLinkedList2113cab2bb3Spatrick   void remove(T *X) {
2123cab2bb3Spatrick     T *Prev = X->Prev;
2133cab2bb3Spatrick     T *Next = X->Next;
2143cab2bb3Spatrick     if (Prev) {
2153cab2bb3Spatrick       CHECK_EQ(Prev->Next, X);
2163cab2bb3Spatrick       Prev->Next = Next;
2173cab2bb3Spatrick     }
2183cab2bb3Spatrick     if (Next) {
2193cab2bb3Spatrick       CHECK_EQ(Next->Prev, X);
2203cab2bb3Spatrick       Next->Prev = Prev;
2213cab2bb3Spatrick     }
2223cab2bb3Spatrick     if (First == X) {
2233cab2bb3Spatrick       DCHECK_EQ(Prev, nullptr);
2243cab2bb3Spatrick       First = Next;
2253cab2bb3Spatrick     } else {
2263cab2bb3Spatrick       DCHECK_NE(Prev, nullptr);
2273cab2bb3Spatrick     }
2283cab2bb3Spatrick     if (Last == X) {
2293cab2bb3Spatrick       DCHECK_EQ(Next, nullptr);
2303cab2bb3Spatrick       Last = Prev;
2313cab2bb3Spatrick     } else {
2323cab2bb3Spatrick       DCHECK_NE(Next, nullptr);
2333cab2bb3Spatrick     }
2343cab2bb3Spatrick     Size--;
2353cab2bb3Spatrick   }
2363cab2bb3Spatrick };
2373cab2bb3Spatrick 
2383cab2bb3Spatrick } // namespace scudo
2393cab2bb3Spatrick 
2403cab2bb3Spatrick #endif // SCUDO_LIST_H_
241