blob: 6b848b91ad1776f65c3e0f886c3f9c693c2d0d38 [file] [log] [blame]
//===--- IndexSubset.h - Fixed-size subset of indices ---------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the `IndexSubset` class and support logic.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_AST_INDEXSUBSET_H
#define SWIFT_AST_INDEXSUBSET_H
#include "swift/Basic/Debug.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/STLExtras.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/Support/raw_ostream.h"
namespace swift {
class ASTContext;
/// An efficient index subset data structure, uniqued in `ASTContext`.
/// Stores a bit vector representing set indices and a total capacity.
class IndexSubset : public llvm::FoldingSetNode {
public:
typedef uint64_t BitWord;
static constexpr unsigned bitWordSize = sizeof(BitWord);
static constexpr unsigned numBitsPerBitWord = bitWordSize * 8;
static std::pair<unsigned, unsigned>
getBitWordIndexAndOffset(unsigned index) {
auto bitWordIndex = index / numBitsPerBitWord;
auto bitWordOffset = index % numBitsPerBitWord;
return {bitWordIndex, bitWordOffset};
}
static unsigned getNumBitWordsNeededForCapacity(unsigned capacity) {
if (capacity == 0) return 0;
return capacity / numBitsPerBitWord + 1;
}
private:
/// The total capacity of the index subset, which is `1` less than the largest
/// index.
unsigned capacity;
/// The number of bit words in the index subset.
unsigned numBitWords;
static unsigned getNumBytesNeededForCapacity(unsigned capacity) {
return getNumBitWordsNeededForCapacity(capacity) * bitWordSize;
}
BitWord *getBitWordsData() {
return reinterpret_cast<BitWord *>(this + 1);
}
const BitWord *getBitWordsData() const {
return reinterpret_cast<const BitWord *>(this + 1);
}
ArrayRef<BitWord> getBitWords() const {
return {getBitWordsData(), getNumBitWords()};
}
BitWord getBitWord(unsigned i) const {
return getBitWordsData()[i];
}
BitWord &getBitWord(unsigned i) {
return getBitWordsData()[i];
}
MutableArrayRef<BitWord> getMutableBitWords() {
return {const_cast<BitWord *>(getBitWordsData()), getNumBitWords()};
}
explicit IndexSubset(const SmallBitVector &indices)
: capacity((unsigned)indices.size()),
numBitWords(getNumBitWordsNeededForCapacity(capacity)) {
std::uninitialized_fill_n(getBitWordsData(), numBitWords, 0);
for (auto i : indices.set_bits()) {
unsigned bitWordIndex, offset;
std::tie(bitWordIndex, offset) = getBitWordIndexAndOffset(i);
getBitWord(bitWordIndex) |= (1ull << offset);
}
}
public:
IndexSubset() = delete;
IndexSubset(const IndexSubset &) = delete;
IndexSubset &operator=(const IndexSubset &) = delete;
// Defined in ASTContext.cpp.
static IndexSubset *get(ASTContext &ctx, const SmallBitVector &indices);
static IndexSubset *get(ASTContext &ctx, unsigned capacity,
ArrayRef<unsigned> indices) {
SmallBitVector indicesBitVec(capacity, false);
for (auto index : indices)
indicesBitVec.set(index);
return IndexSubset::get(ctx, indicesBitVec);
}
static IndexSubset *getDefault(ASTContext &ctx, unsigned capacity,
bool includeAll = false) {
return get(ctx, SmallBitVector(capacity, includeAll));
}
static IndexSubset *getFromRange(ASTContext &ctx, unsigned capacity,
unsigned start, unsigned end) {
assert(start < capacity);
assert(end <= capacity);
SmallBitVector bitVec(capacity);
bitVec.set(start, end);
return get(ctx, bitVec);
}
/// Creates an index subset corresponding to the given string generated by
/// `getString()`. If the string is invalid, returns nullptr.
static IndexSubset *getFromString(ASTContext &ctx, StringRef string);
/// Returns the number of bit words used to store the index subset.
// Note: Use `getCapacity()` to get the total index subset capacity.
// This is public only for unit testing
// (in unittests/AST/IndexSubsetTests.cpp).
unsigned getNumBitWords() const {
return numBitWords;
}
/// Returns the capacity of the index subset.
unsigned getCapacity() const {
return capacity;
}
/// Returns a textual string description of these indices.
///
/// It has the format `[SU]+`, where the total number of characters is equal
/// to the capacity, and where "S" means that the corresponding index is
/// contained and "U" means that the corresponding index is not.
std::string getString() const;
class iterator;
iterator begin() const {
return iterator(this);
}
iterator end() const {
return iterator(this, (int)capacity);
}
/// Returns an iterator range of indices in the index subset.
iterator_range<iterator> getIndices() const {
return make_range(begin(), end());
}
/// Returns the number of indices in the index subset.
unsigned getNumIndices() const {
return (unsigned)std::distance(begin(), end());
}
SmallBitVector getBitVector() const {
SmallBitVector indicesBitVec(capacity, false);
for (auto index : getIndices())
indicesBitVec.set(index);
return indicesBitVec;
}
bool contains(unsigned index) const {
unsigned bitWordIndex, offset;
std::tie(bitWordIndex, offset) = getBitWordIndexAndOffset(index);
return getBitWord(bitWordIndex) & (1ull << offset);
}
bool isEmpty() const {
return llvm::all_of(getBitWords(), [](BitWord bw) { return !(bool)bw; });
}
bool equals(IndexSubset *other) const {
return capacity == other->getCapacity() &&
getBitWords().equals(other->getBitWords());
}
bool isSubsetOf(IndexSubset *other) const;
bool isSupersetOf(IndexSubset *other) const;
IndexSubset *adding(unsigned index, ASTContext &ctx) const;
IndexSubset *extendingCapacity(ASTContext &ctx,
unsigned newCapacity) const;
void Profile(llvm::FoldingSetNodeID &id) const {
id.AddInteger(capacity);
for (auto index : getIndices())
id.AddInteger(index);
}
void print(llvm::raw_ostream &s = llvm::outs()) const;
SWIFT_DEBUG_DUMPER(dump());
int findNext(int startIndex) const;
int findFirst() const { return findNext(-1); }
int findPrevious(int endIndex) const;
int findLast() const { return findPrevious(capacity); }
class iterator {
public:
typedef unsigned value_type;
typedef unsigned difference_type;
typedef unsigned * pointer;
typedef unsigned & reference;
typedef std::forward_iterator_tag iterator_category;
private:
const IndexSubset *parent;
int current = 0;
void advance() {
assert(current != -1 && "Trying to advance past end.");
current = parent->findNext(current);
}
public:
iterator(const IndexSubset *parent, int current)
: parent(parent), current(current) {}
explicit iterator(const IndexSubset *parent)
: iterator(parent, parent->findFirst()) {}
iterator(const iterator &) = default;
iterator operator++(int) {
auto prev = *this;
advance();
return prev;
}
iterator &operator++() {
advance();
return *this;
}
unsigned operator*() const { return current; }
bool operator==(const iterator &other) const {
assert(parent == other.parent &&
"Comparing iterators from different IndexSubsets");
return current == other.current;
}
bool operator!=(const iterator &other) const {
assert(parent == other.parent &&
"Comparing iterators from different IndexSubsets");
return current != other.current;
}
};
};
}
#endif // SWIFT_AST_INDEXSUBSET_H