1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
|
//===--- Iterator.cpp - Query Symbol Retrieval ------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Iterator.h"
#include <algorithm>
#include <cassert>
#include <numeric>
namespace clang {
namespace clangd {
namespace dex {
namespace {
/// Implements Iterator over a PostingList. DocumentIterator is the most basic
/// iterator: it doesn't have any children (hence it is the leaf of iterator
/// tree) and is simply a wrapper around PostingList::const_iterator.
class DocumentIterator : public Iterator {
public:
DocumentIterator(PostingListRef Documents)
: Documents(Documents), Index(std::begin(Documents)) {}
bool reachedEnd() const override { return Index == std::end(Documents); }
/// Advances cursor to the next item.
void advance() override {
assert(!reachedEnd() && "DocumentIterator can't advance at the end.");
++Index;
}
/// Applies binary search to advance cursor to the next item with DocID equal
/// or higher than the given one.
void advanceTo(DocID ID) override {
assert(!reachedEnd() && "DocumentIterator can't advance at the end.");
Index = std::lower_bound(Index, std::end(Documents), ID);
}
DocID peek() const override {
assert(!reachedEnd() && "DocumentIterator can't call peek() at the end.");
return *Index;
}
llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override {
OS << '[';
auto Separator = "";
for (const auto &ID : Documents) {
OS << Separator << ID;
Separator = ", ";
}
OS << ']';
return OS;
}
private:
PostingListRef Documents;
PostingListRef::const_iterator Index;
};
/// Implements Iterator over the intersection of other iterators.
///
/// AndIterator iterates through common items among all children. It becomes
/// exhausted as soon as any child becomes exhausted. After each mutation, the
/// iterator restores the invariant: all children must point to the same item.
class AndIterator : public Iterator {
public:
AndIterator(std::vector<std::unique_ptr<Iterator>> AllChildren)
: Children(std::move(AllChildren)) {
assert(!Children.empty() && "AndIterator should have at least one child.");
// Establish invariants.
sync();
}
bool reachedEnd() const override { return ReachedEnd; }
/// Advances all children to the next common item.
void advance() override {
assert(!reachedEnd() && "AndIterator can't call advance() at the end.");
Children.front()->advance();
sync();
}
/// Advances all children to the next common item with DocumentID >= ID.
void advanceTo(DocID ID) override {
assert(!reachedEnd() && "AndIterator can't call advanceTo() at the end.");
Children.front()->advanceTo(ID);
sync();
}
DocID peek() const override { return Children.front()->peek(); }
llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override {
OS << "(& ";
auto Separator = "";
for (const auto &Child : Children) {
OS << Separator << *Child;
Separator = " ";
}
OS << ')';
return OS;
}
private:
/// Restores class invariants: each child will point to the same element after
/// sync.
void sync() {
ReachedEnd |= Children.front()->reachedEnd();
if (ReachedEnd)
return;
auto SyncID = Children.front()->peek();
// Indicates whether any child needs to be advanced to new SyncID.
bool NeedsAdvance = false;
do {
NeedsAdvance = false;
for (auto &Child : Children) {
Child->advanceTo(SyncID);
ReachedEnd |= Child->reachedEnd();
// If any child reaches end And iterator can not match any other items.
// In this case, just terminate the process.
if (ReachedEnd)
return;
// If any child goes beyond given ID (i.e. ID is not the common item),
// all children should be advanced to the next common item.
// FIXME(kbobyrev): This is not a very optimized version; after costs
// are introduced, cycle should break whenever ID exceeds current one
// and cheapest children should be advanced over again.
if (Child->peek() > SyncID) {
SyncID = Child->peek();
NeedsAdvance = true;
}
}
} while (NeedsAdvance);
}
/// AndIterator owns its children and ensures that all of them point to the
/// same element. As soon as one child gets exhausted, AndIterator can no
/// longer advance and has reached its end.
std::vector<std::unique_ptr<Iterator>> Children;
/// Indicates whether any child is exhausted. It is cheaper to maintain and
/// update the field, rather than traversing the whole subtree in each
/// reachedEnd() call.
bool ReachedEnd = false;
};
/// Implements Iterator over the union of other iterators.
///
/// OrIterator iterates through all items which can be pointed to by at least
/// one child. To preserve the sorted order, this iterator always advances the
/// child with smallest Child->peek() value. OrIterator becomes exhausted as
/// soon as all of its children are exhausted.
class OrIterator : public Iterator {
public:
OrIterator(std::vector<std::unique_ptr<Iterator>> AllChildren)
: Children(std::move(AllChildren)) {
assert(Children.size() > 0 && "Or Iterator must have at least one child.");
}
/// Returns true if all children are exhausted.
bool reachedEnd() const override {
return std::all_of(begin(Children), end(Children),
[](const std::unique_ptr<Iterator> &Child) {
return Child->reachedEnd();
});
}
/// Moves each child pointing to the smallest DocID to the next item.
void advance() override {
assert(!reachedEnd() &&
"OrIterator must have at least one child to advance().");
const auto SmallestID = peek();
for (const auto &Child : Children)
if (!Child->reachedEnd() && Child->peek() == SmallestID)
Child->advance();
}
/// Advances each child to the next existing element with DocumentID >= ID.
void advanceTo(DocID ID) override {
assert(!reachedEnd() && "Can't advance iterator after it reached the end.");
for (const auto &Child : Children)
if (!Child->reachedEnd())
Child->advanceTo(ID);
}
/// Returns the element under cursor of the child with smallest Child->peek()
/// value.
DocID peek() const override {
assert(!reachedEnd() &&
"OrIterator must have at least one child to peek().");
DocID Result = std::numeric_limits<DocID>::max();
for (const auto &Child : Children)
if (!Child->reachedEnd())
Result = std::min(Result, Child->peek());
return Result;
}
llvm::raw_ostream &dump(llvm::raw_ostream &OS) const override {
OS << "(| ";
auto Separator = "";
for (const auto &Child : Children) {
OS << Separator << *Child;
Separator = " ";
}
OS << ')';
return OS;
}
private:
// FIXME(kbobyrev): Would storing Children in min-heap be faster?
std::vector<std::unique_ptr<Iterator>> Children;
};
} // end namespace
std::vector<DocID> consume(Iterator &It) {
std::vector<DocID> Result;
for (; !It.reachedEnd(); It.advance())
Result.push_back(It.peek());
return Result;
}
std::unique_ptr<Iterator> create(PostingListRef Documents) {
return llvm::make_unique<DocumentIterator>(Documents);
}
std::unique_ptr<Iterator>
createAnd(std::vector<std::unique_ptr<Iterator>> Children) {
return llvm::make_unique<AndIterator>(move(Children));
}
std::unique_ptr<Iterator>
createOr(std::vector<std::unique_ptr<Iterator>> Children) {
return llvm::make_unique<OrIterator>(move(Children));
}
} // namespace dex
} // namespace clangd
} // namespace clang
|