Home | History | Annotate | Download | only in ADT
      1 //==--- ImmutableList.h - Immutable (functional) list interface --*- C++ -*-==//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file defines the ImmutableList class.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #ifndef LLVM_ADT_IMMUTABLELIST_H
     15 #define LLVM_ADT_IMMUTABLELIST_H
     16 
     17 #include "llvm/ADT/FoldingSet.h"
     18 #include "llvm/Support/Allocator.h"
     19 #include <cassert>
     20 #include <cstdint>
     21 #include <new>
     22 
     23 namespace llvm {
     24 
     25 template <typename T> class ImmutableListFactory;
     26 
     27 template <typename T>
     28 class ImmutableListImpl : public FoldingSetNode {
     29   friend class ImmutableListFactory<T>;
     30 
     31   T Head;
     32   const ImmutableListImpl* Tail;
     33 
     34   ImmutableListImpl(const T& head, const ImmutableListImpl* tail = nullptr)
     35     : Head(head), Tail(tail) {}
     36 
     37 public:
     38   ImmutableListImpl(const ImmutableListImpl &) = delete;
     39   ImmutableListImpl &operator=(const ImmutableListImpl &) = delete;
     40 
     41   const T& getHead() const { return Head; }
     42   const ImmutableListImpl* getTail() const { return Tail; }
     43 
     44   static inline void Profile(FoldingSetNodeID& ID, const T& H,
     45                              const ImmutableListImpl* L){
     46     ID.AddPointer(L);
     47     ID.Add(H);
     48   }
     49 
     50   void Profile(FoldingSetNodeID& ID) {
     51     Profile(ID, Head, Tail);
     52   }
     53 };
     54 
     55 /// ImmutableList - This class represents an immutable (functional) list.
     56 ///  It is implemented as a smart pointer (wraps ImmutableListImpl), so it
     57 ///  it is intended to always be copied by value as if it were a pointer.
     58 ///  This interface matches ImmutableSet and ImmutableMap.  ImmutableList
     59 ///  objects should almost never be created directly, and instead should
     60 ///  be created by ImmutableListFactory objects that manage the lifetime
     61 ///  of a group of lists.  When the factory object is reclaimed, all lists
     62 ///  created by that factory are released as well.
     63 template <typename T>
     64 class ImmutableList {
     65 public:
     66   using value_type = T;
     67   using Factory = ImmutableListFactory<T>;
     68 
     69 private:
     70   const ImmutableListImpl<T>* X;
     71 
     72 public:
     73   // This constructor should normally only be called by ImmutableListFactory<T>.
     74   // There may be cases, however, when one needs to extract the internal pointer
     75   // and reconstruct a list object from that pointer.
     76   ImmutableList(const ImmutableListImpl<T>* x = nullptr) : X(x) {}
     77 
     78   const ImmutableListImpl<T>* getInternalPointer() const {
     79     return X;
     80   }
     81 
     82   class iterator {
     83     const ImmutableListImpl<T>* L = nullptr;
     84 
     85   public:
     86     iterator() = default;
     87     iterator(ImmutableList l) : L(l.getInternalPointer()) {}
     88 
     89     iterator& operator++() { L = L->getTail(); return *this; }
     90     bool operator==(const iterator& I) const { return L == I.L; }
     91     bool operator!=(const iterator& I) const { return L != I.L; }
     92     const value_type& operator*() const { return L->getHead(); }
     93 
     94     ImmutableList getList() const { return L; }
     95   };
     96 
     97   /// begin - Returns an iterator referring to the head of the list, or
     98   ///  an iterator denoting the end of the list if the list is empty.
     99   iterator begin() const { return iterator(X); }
    100 
    101   /// end - Returns an iterator denoting the end of the list.  This iterator
    102   ///  does not refer to a valid list element.
    103   iterator end() const { return iterator(); }
    104 
    105   /// isEmpty - Returns true if the list is empty.
    106   bool isEmpty() const { return !X; }
    107 
    108   bool contains(const T& V) const {
    109     for (iterator I = begin(), E = end(); I != E; ++I) {
    110       if (*I == V)
    111         return true;
    112     }
    113     return false;
    114   }
    115 
    116   /// isEqual - Returns true if two lists are equal.  Because all lists created
    117   ///  from the same ImmutableListFactory are uniqued, this has O(1) complexity
    118   ///  because it the contents of the list do not need to be compared.  Note
    119   ///  that you should only compare two lists created from the same
    120   ///  ImmutableListFactory.
    121   bool isEqual(const ImmutableList& L) const { return X == L.X; }
    122 
    123   bool operator==(const ImmutableList& L) const { return isEqual(L); }
    124 
    125   /// getHead - Returns the head of the list.
    126   const T& getHead() {
    127     assert(!isEmpty() && "Cannot get the head of an empty list.");
    128     return X->getHead();
    129   }
    130 
    131   /// getTail - Returns the tail of the list, which is another (possibly empty)
    132   ///  ImmutableList.
    133   ImmutableList getTail() {
    134     return X ? X->getTail() : nullptr;
    135   }
    136 
    137   void Profile(FoldingSetNodeID& ID) const {
    138     ID.AddPointer(X);
    139   }
    140 };
    141 
    142 template <typename T>
    143 class ImmutableListFactory {
    144   using ListTy = ImmutableListImpl<T>;
    145   using CacheTy = FoldingSet<ListTy>;
    146 
    147   CacheTy Cache;
    148   uintptr_t Allocator;
    149 
    150   bool ownsAllocator() const {
    151     return (Allocator & 0x1) == 0;
    152   }
    153 
    154   BumpPtrAllocator& getAllocator() const {
    155     return *reinterpret_cast<BumpPtrAllocator*>(Allocator & ~0x1);
    156   }
    157 
    158 public:
    159   ImmutableListFactory()
    160     : Allocator(reinterpret_cast<uintptr_t>(new BumpPtrAllocator())) {}
    161 
    162   ImmutableListFactory(BumpPtrAllocator& Alloc)
    163   : Allocator(reinterpret_cast<uintptr_t>(&Alloc) | 0x1) {}
    164 
    165   ~ImmutableListFactory() {
    166     if (ownsAllocator()) delete &getAllocator();
    167   }
    168 
    169   ImmutableList<T> concat(const T& Head, ImmutableList<T> Tail) {
    170     // Profile the new list to see if it already exists in our cache.
    171     FoldingSetNodeID ID;
    172     void* InsertPos;
    173 
    174     const ListTy* TailImpl = Tail.getInternalPointer();
    175     ListTy::Profile(ID, Head, TailImpl);
    176     ListTy* L = Cache.FindNodeOrInsertPos(ID, InsertPos);
    177 
    178     if (!L) {
    179       // The list does not exist in our cache.  Create it.
    180       BumpPtrAllocator& A = getAllocator();
    181       L = (ListTy*) A.Allocate<ListTy>();
    182       new (L) ListTy(Head, TailImpl);
    183 
    184       // Insert the new list into the cache.
    185       Cache.InsertNode(L, InsertPos);
    186     }
    187 
    188     return L;
    189   }
    190 
    191   ImmutableList<T> add(const T& D, ImmutableList<T> L) {
    192     return concat(D, L);
    193   }
    194 
    195   ImmutableList<T> getEmptyList() const {
    196     return ImmutableList<T>(nullptr);
    197   }
    198 
    199   ImmutableList<T> create(const T& X) {
    200     return Concat(X, getEmptyList());
    201   }
    202 };
    203 
    204 //===----------------------------------------------------------------------===//
    205 // Partially-specialized Traits.
    206 //===----------------------------------------------------------------------===//
    207 
    208 template<typename T> struct DenseMapInfo;
    209 template<typename T> struct DenseMapInfo<ImmutableList<T>> {
    210   static inline ImmutableList<T> getEmptyKey() {
    211     return reinterpret_cast<ImmutableListImpl<T>*>(-1);
    212   }
    213 
    214   static inline ImmutableList<T> getTombstoneKey() {
    215     return reinterpret_cast<ImmutableListImpl<T>*>(-2);
    216   }
    217 
    218   static unsigned getHashValue(ImmutableList<T> X) {
    219     uintptr_t PtrVal = reinterpret_cast<uintptr_t>(X.getInternalPointer());
    220     return (unsigned((uintptr_t)PtrVal) >> 4) ^
    221            (unsigned((uintptr_t)PtrVal) >> 9);
    222   }
    223 
    224   static bool isEqual(ImmutableList<T> X1, ImmutableList<T> X2) {
    225     return X1 == X2;
    226   }
    227 };
    228 
    229 template <typename T> struct isPodLike;
    230 template <typename T>
    231 struct isPodLike<ImmutableList<T>> { static const bool value = true; };
    232 
    233 } // end namespace llvm
    234 
    235 #endif // LLVM_ADT_IMMUTABLELIST_H
    236