blob: bf57c311c7929ea85dbb01d980825af4e61597fc [file] [log] [blame]
//===--- BlotSetVector.h ----------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_BASIC_BLOTSETVECTOR_H
#define SWIFT_BASIC_BLOTSETVECTOR_H
#include "swift/Basic/LLVM.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/SmallVector.h"
#include <vector>
namespace swift {
/// This is a set container with the following properties:
///
/// 1. Fast insertion-order iteration.
///
/// 2. Stable index offsets for all values after all operations including
/// erase.
///
/// This contrasts with SetVector where index offsets are not stable due to
/// usage of std::vector::erase(). In contrast, BlotSetVector uses the `blot
/// operation' (a) which trades memory for runtime and index offset stability.
///
/// 3. Fast replacement of a value v1 with a second value v2 guaranteeing that
/// v2 is placed into the same array index as v1, just deleting v1 if v2 is
/// already in the array.
///
/// This is important if one has other data structures referring to v1 via v1's
/// index in the vector that one wishes to now refer to v2.
///
/// 4. Fast deletion via the 'blot' operation.
///
/// 5. Fast insertion.
///
/// (a) The `blot operation' is leaving the value in the set vector, but marking
/// the value as being dead.
template <typename ValueT, typename VectorT = std::vector<Optional<ValueT>>,
typename MapT = llvm::DenseMap<ValueT, unsigned>>
class BlotSetVector {
VectorT vector;
MapT map;
public:
/// Construct an empty BlotSetVector.
BlotSetVector() {}
bool empty() const { return vector.empty(); }
unsigned size() const { return vector.size(); }
using iterator = typename VectorT::iterator;
using const_iterator = typename VectorT::const_iterator;
iterator begin() { return vector.begin(); }
iterator end() { return vector.end(); }
const_iterator begin() const { return vector.begin(); }
const_iterator end() const { return vector.end(); }
ArrayRef<Optional<ValueT>> getArray() const { return vector; }
iterator_range<const_iterator> getRange() const {
return {begin(), end()};
}
using const_reverse_iterator = typename VectorT::const_reverse_iterator;
const_reverse_iterator rbegin() const { return vector.rbegin(); }
const_reverse_iterator rend() const { return vector.rend(); }
iterator_range<const_reverse_iterator> getReverseRange() const {
return {rbegin(), rend()};
}
const Optional<ValueT> &operator[](unsigned n) const {
assert(n < vector.size() && "Out of range!");
return vector[n];
}
/// Grow the set vector so that it can contain at least \p capacity items
/// before resizing again.
///
/// \param capacity The minimum size that the set vector will be able to grow
/// to before a resize is required to insert additional items.
void reserve(unsigned capacity) {
vector.reserve(capacity);
map.reserve(capacity);
}
/// Insert \p value into the SetVector if it is not there already.
///
/// \param value The item to insert if not already present.
///
/// \returns Both the index of the item and whether it was inserted in a pair.
/// If the item was already present, its preexisting index along with
/// false will be returned. If the item was newly added, its new
/// index along with true will be returned.
std::pair<unsigned, bool> insert(const ValueT &value) {
auto iterator = map.find(value);
if (iterator != map.end())
return {iterator->second, false};
unsigned index = vector.size();
map[value] = index;
vector.push_back(value);
return {index, true};
}
/// Replace \p value1 with \p value2 placing \p value2 into the position in
/// the array where value1 used to be. If \p value2 is already in the set,
/// this just erases \p value1.
void replace(const ValueT &value1, const ValueT &value2) {
auto iterator1 = map.find(value1);
assert(iterator1 != map.end() && "Cannot replace value that is not in set");
unsigned index1 = iterator1->second;
map.erase(value1);
auto iterator2 = map.find(value2);
if (iterator2 != map.end()) {
vector[index1] = None;
return;
}
map[value2] = index1;
vector[index1] = value2;
}
/// Erase the value \p value if it is in the set. Returns true if value was
/// successfully erased and false otherwise.
bool erase(const ValueT &value) {
auto iterator = map.find(value);
if (iterator == map.end())
return false;
unsigned index = iterator->second;
map.erase(iterator);
vector[index] = None;
return true;
}
/// Return the last element of the blot map vector. Will be None if blotted.
Optional<ValueT> pop_back_val() {
auto result = vector.pop_back_val();
if (!result)
return result;
map.erase(*result);
return result;
}
/// Attempt to lookup the index of \p value. Returns None upon failure and the
/// value on success.
Optional<unsigned> getIndex(const ValueT &value) {
auto iterator = map.find(value);
if (iterator == map.end())
return None;
return iterator->second;
}
/// Clear the backing vector and map.
void clear() {
vector.clear();
map.clear();
}
};
template <typename ValueT, unsigned N>
class SmallBlotSetVector
: public BlotSetVector<ValueT, llvm::SmallVector<Optional<ValueT>, N>,
llvm::SmallDenseMap<ValueT, unsigned, N>> {
public:
SmallBlotSetVector() {}
};
} // namespace swift
#endif // SWIFT_BASIC_BLOTSETVECTOR_H