mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
429 lines
13 KiB
C++
429 lines
13 KiB
C++
//===--- DiverseStack.h - Stack of variably-sized objects -------*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
///
|
|
/// This file defines a data structure for representing a stack of
|
|
/// variably-sized objects. It is a requirement that the object type
|
|
/// be trivially movable, meaning that it has a trivial move
|
|
/// constructor and a trivial destructor.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_BASIC_DIVERSESTACK_H
|
|
#define SWIFT_BASIC_DIVERSESTACK_H
|
|
|
|
#include "swift/Basic/Malloc.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
#include "llvm/Support/PointerLikeTypeTraits.h"
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <utility>
|
|
|
|
namespace swift {
|
|
|
|
template <class T> class DiverseStackImpl;
|
|
|
|
/// DiverseStack - A stack of heterogeneously-typed objects.
|
|
///
|
|
/// \tparam T - A common base class of the objects on the stack; must
|
|
/// provide an allocated_size() const method.
|
|
/// \tparam InlineCapacity - the amount of inline storage to provide, in bytes.
|
|
template <class T, unsigned InlineCapacity>
|
|
class DiverseStack : public DiverseStackImpl<T> {
|
|
char InlineStorage[InlineCapacity];
|
|
|
|
public:
|
|
DiverseStack() : DiverseStackImpl<T>(InlineStorage + InlineCapacity) {}
|
|
DiverseStack(const DiverseStack &other)
|
|
: DiverseStackImpl<T>(other, InlineStorage + InlineCapacity) {}
|
|
DiverseStack(const DiverseStackImpl<T> &other)
|
|
: DiverseStackImpl<T>(other, InlineStorage + InlineCapacity) {}
|
|
DiverseStack(DiverseStack<T, InlineCapacity> &&other)
|
|
: DiverseStackImpl<T>(std::move(other), InlineStorage + InlineCapacity) {}
|
|
DiverseStack(DiverseStackImpl<T> &&other)
|
|
: DiverseStackImpl<T>(std::move(other), InlineStorage + InlineCapacity) {}
|
|
};
|
|
|
|
/// A base class for DiverseStackImpl.
|
|
class DiverseStackBase {
|
|
public:
|
|
/// The top of the stack.
|
|
char *Begin;
|
|
|
|
/// The bottom of the stack, i.e. the end of the allocation.
|
|
char *End;
|
|
|
|
/// The beginning of the allocation.
|
|
char *Allocated;
|
|
|
|
bool isAllocatedInline() const {
|
|
return (Allocated == reinterpret_cast<const char *>(this + 1));
|
|
}
|
|
|
|
void checkValid() const {
|
|
assert(Allocated <= Begin);
|
|
assert(Begin <= End);
|
|
}
|
|
|
|
void initialize(char *end) {
|
|
Begin = End = end;
|
|
Allocated = reinterpret_cast<char*>(this + 1);
|
|
}
|
|
void copyFrom(const DiverseStackBase &other) {
|
|
// Ensure that we're large enough to store all the data.
|
|
std::size_t size = static_cast<std::size_t>(other.End - other.Begin);
|
|
pushNewStorage(size);
|
|
std::memcpy(Begin, other.Begin, size);
|
|
}
|
|
void pushNewStorage(std::size_t needed) {
|
|
checkValid();
|
|
if (std::size_t(Begin - Allocated) >= needed) {
|
|
Begin -= needed;
|
|
} else {
|
|
pushNewStorageSlow(needed);
|
|
}
|
|
}
|
|
void pushNewStorageSlow(std::size_t needed);
|
|
|
|
/// A stable iterator is the equivalent of an index into the stack.
|
|
/// It's an iterator that stays stable across modification of the
|
|
/// stack.
|
|
class stable_iterator {
|
|
std::size_t Depth;
|
|
friend class DiverseStackBase;
|
|
template <class T> friend class DiverseStackImpl;
|
|
stable_iterator(std::size_t depth) : Depth(depth) {}
|
|
public:
|
|
stable_iterator() = default;
|
|
friend bool operator==(stable_iterator a, stable_iterator b) {
|
|
return a.Depth == b.Depth;
|
|
}
|
|
friend bool operator!=(stable_iterator a, stable_iterator b) {
|
|
return !operator==(a, b);
|
|
}
|
|
|
|
static stable_iterator invalid() {
|
|
return stable_iterator((std::size_t) -1);
|
|
}
|
|
bool isValid() const {
|
|
return Depth != (std::size_t) -1;
|
|
}
|
|
|
|
std::size_t getDepth() const { return Depth; }
|
|
|
|
/// A helper class that wraps a stable_iterator as something that
|
|
/// pretends to be a non-null pointer.
|
|
///
|
|
/// This allows stable_iterators to be placed in TinyPtrVector.
|
|
///
|
|
/// A wrapper is needed because we don't want to give stable_iterator
|
|
/// a null inhabitant, an operator bool, conversions from nullptr_t, or
|
|
/// any similar features that TinyPtrVector reasonably requires of its
|
|
/// element types.
|
|
class AsPointer {
|
|
void *EncodedValue;
|
|
explicit AsPointer(void *encodedValue) : EncodedValue(encodedValue) {}
|
|
public:
|
|
enum { NumLowBitsAvailable = 3 };
|
|
|
|
/// Allow a null AsPointer to be created with either 'nullptr' or
|
|
/// 'AsPointer()'.
|
|
/*implicit*/ AsPointer(std::nullptr_t _ = nullptr)
|
|
: EncodedValue(nullptr) {}
|
|
|
|
/// Allow an AsPointer to be tested as a boolean value.
|
|
explicit operator bool() const { return EncodedValue != nullptr; }
|
|
|
|
/// Allow an AsPointer to be compared for equality with a void*.
|
|
friend bool operator==(AsPointer lhs, void *rhs) {
|
|
return lhs.EncodedValue == rhs;
|
|
}
|
|
|
|
/// Allow an implicit conversion from stable_iterator.
|
|
/*implicit*/ AsPointer(stable_iterator it) {
|
|
assert(it.isValid() && "can't encode invalid stable_iterator");
|
|
auto encodedDepth = (it.Depth + 1) << NumLowBitsAvailable;
|
|
EncodedValue = reinterpret_cast<void*>(encodedDepth);
|
|
assert(EncodedValue && "encoded pointer was null");
|
|
}
|
|
|
|
/// Allow an implicit conversion to stable_iterator.
|
|
operator stable_iterator() const {
|
|
assert(EncodedValue && "can't decode null pointer");
|
|
auto encodedDepth = reinterpret_cast<std::size_t>(EncodedValue);
|
|
auto depth = (encodedDepth >> NumLowBitsAvailable) - 1;
|
|
auto it = stable_iterator(depth);
|
|
assert(it.isValid() && "decoded stable_iterator was invalid");
|
|
return it;
|
|
}
|
|
|
|
void *getAsVoidPointer() const { return EncodedValue; }
|
|
static AsPointer getFromVoidPointer(void *ptr) { return AsPointer(ptr); }
|
|
};
|
|
AsPointer asPointer() const { return *this; }
|
|
};
|
|
stable_iterator stable_begin() const {
|
|
return stable_iterator(End - Begin);
|
|
}
|
|
static stable_iterator stable_end() {
|
|
return stable_iterator(0);
|
|
}
|
|
|
|
void checkIterator(stable_iterator it) const {
|
|
assert(it.isValid() && "checking an invalid iterator");
|
|
checkValid();
|
|
assert(it.Depth <= size_t(End - Begin));
|
|
}
|
|
};
|
|
|
|
template <class T> class DiverseStackImpl : private DiverseStackBase {
|
|
DiverseStackImpl(const DiverseStackImpl<T> &other) = delete;
|
|
DiverseStackImpl(DiverseStackImpl<T> &&other) = delete;
|
|
|
|
protected:
|
|
DiverseStackImpl(char *end) {
|
|
initialize(end);
|
|
}
|
|
|
|
DiverseStackImpl(const DiverseStackImpl<T> &other, char *end) {
|
|
initialize(end);
|
|
copyFrom(other);
|
|
}
|
|
DiverseStackImpl(DiverseStackImpl<T> &&other, char *end) {
|
|
// If the other is allocated inline, just initialize and copy.
|
|
if (other.isAllocatedInline()) {
|
|
initialize(end);
|
|
copyFrom(other);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, steal its allocations.
|
|
Begin = other.Begin;
|
|
End = other.End;
|
|
Allocated = other.Allocated;
|
|
other.Begin = other.End = other.Allocated = (char*) (&other + 1);
|
|
assert(other.isAllocatedInline());
|
|
}
|
|
|
|
public:
|
|
~DiverseStackImpl() {
|
|
checkValid();
|
|
if (!isAllocatedInline())
|
|
delete[] Allocated;
|
|
}
|
|
|
|
/// Query whether the stack is empty.
|
|
bool empty() const {
|
|
checkValid();
|
|
return Begin == End;
|
|
}
|
|
|
|
/// Return a reference to the top element on the stack.
|
|
T &top() {
|
|
assert(!empty());
|
|
return *reinterpret_cast<T*>(Begin);
|
|
}
|
|
|
|
/// Return a reference to the top element on the stack.
|
|
const T &top() const {
|
|
assert(!empty());
|
|
return *reinterpret_cast<const T*>(Begin);
|
|
}
|
|
|
|
using DiverseStackBase::stable_iterator;
|
|
using DiverseStackBase::stable_begin;
|
|
using DiverseStackBase::stable_end;
|
|
|
|
class const_iterator;
|
|
class iterator {
|
|
char *Ptr;
|
|
friend class DiverseStackImpl;
|
|
friend class const_iterator;
|
|
iterator(char *ptr) : Ptr(ptr) {}
|
|
|
|
public:
|
|
iterator() = default;
|
|
|
|
T &operator*() const { return *reinterpret_cast<T*>(Ptr); }
|
|
T *operator->() const { return reinterpret_cast<T*>(Ptr); }
|
|
iterator &operator++() {
|
|
Ptr += (*this)->allocated_size();
|
|
return *this;
|
|
}
|
|
iterator operator++(int _) {
|
|
auto copy = *this;
|
|
operator++();
|
|
return copy;
|
|
}
|
|
|
|
/// advancePast - Like operator++, but asserting that the current
|
|
/// object has a known type.
|
|
template <class U> void advancePast() {
|
|
assert((*this)->allocated_size() == sizeof(U));
|
|
Ptr += sizeof(U);
|
|
}
|
|
|
|
friend bool operator==(iterator a, iterator b) { return a.Ptr == b.Ptr; }
|
|
friend bool operator!=(iterator a, iterator b) { return !operator==(a, b); }
|
|
};
|
|
|
|
using DiverseStackBase::checkIterator;
|
|
void checkIterator(iterator it) const {
|
|
checkValid();
|
|
assert(Begin <= it.Ptr && it.Ptr <= End);
|
|
}
|
|
|
|
iterator begin() { checkValid(); return iterator(Begin); }
|
|
iterator end() { checkValid(); return iterator(End); }
|
|
iterator find(stable_iterator it) {
|
|
checkIterator(it);
|
|
return iterator(End - it.Depth);
|
|
}
|
|
stable_iterator stabilize(iterator it) const {
|
|
checkIterator(it);
|
|
return stable_iterator(End - it.Ptr);
|
|
}
|
|
|
|
T &findAndAdvance(stable_iterator &i) {
|
|
auto unstable_i = find(i);
|
|
assert(unstable_i != end());
|
|
T &value = *unstable_i;
|
|
++unstable_i;
|
|
i = stabilize(unstable_i);
|
|
return value;
|
|
}
|
|
|
|
class const_iterator {
|
|
const char *Ptr;
|
|
friend class DiverseStackImpl;
|
|
const_iterator(const char *ptr) : Ptr(ptr) {}
|
|
public:
|
|
const_iterator() = default;
|
|
const_iterator(iterator it) : Ptr(it.Ptr) {}
|
|
|
|
const T &operator*() const { return *reinterpret_cast<const T*>(Ptr); }
|
|
const T *operator->() const { return reinterpret_cast<const T*>(Ptr); }
|
|
const_iterator &operator++() {
|
|
Ptr += (*this)->allocated_size();
|
|
return *this;
|
|
}
|
|
const_iterator operator++(int _) {
|
|
auto copy = *this;
|
|
operator++();
|
|
return copy;
|
|
}
|
|
|
|
/// advancePast - Like operator++, but asserting that the current
|
|
/// object has a known type.
|
|
template <class U> void advancePast() {
|
|
assert((*this)->allocated_size() == sizeof(U));
|
|
Ptr += sizeof(U);
|
|
}
|
|
|
|
friend bool operator==(const_iterator a, const_iterator b) {
|
|
return a.Ptr == b.Ptr;
|
|
}
|
|
friend bool operator!=(const_iterator a, const_iterator b) {
|
|
return !operator==(a, b);
|
|
}
|
|
};
|
|
const_iterator begin() const { checkValid(); return const_iterator(Begin); }
|
|
const_iterator end() const { checkValid(); return const_iterator(End); }
|
|
void checkIterator(const_iterator it) const {
|
|
checkValid();
|
|
assert(Begin <= it.Ptr && it.Ptr <= End);
|
|
}
|
|
const_iterator find(stable_iterator it) const {
|
|
checkIterator(it);
|
|
return const_iterator(End - it.Depth);
|
|
}
|
|
stable_iterator stabilize(const_iterator it) const {
|
|
checkIterator(it);
|
|
return stable_iterator(End - it.Ptr);
|
|
}
|
|
|
|
/// Push a new object onto the stack.
|
|
template <class U, class... A> U &push(A && ...args) {
|
|
pushNewStorage(sizeof(U));
|
|
return *::new (Begin) U(::std::forward<A>(args)...);
|
|
}
|
|
|
|
/// Pop an object off the stack.
|
|
void pop() {
|
|
assert(!empty());
|
|
Begin += top().allocated_size();
|
|
}
|
|
|
|
/// Pop an object of known type off the stack.
|
|
template <class U> void pop() {
|
|
assert(!empty());
|
|
assert(sizeof(U) == top().allocated_size());
|
|
Begin += sizeof(U);
|
|
}
|
|
|
|
/// Pop objects off of the stack until \p the object pointed to by stable_iter
|
|
/// is the top element of the stack.
|
|
void pop(stable_iterator stable_iter) {
|
|
iterator iter = find(stable_iter);
|
|
checkIterator(iter);
|
|
#ifndef NDEBUG
|
|
while (Begin != iter.Ptr) {
|
|
pop();
|
|
checkIterator(iter);
|
|
}
|
|
#else
|
|
Begin = iter.Ptr;
|
|
#endif
|
|
}
|
|
};
|
|
|
|
/// A helper class for copying value off a DiverseStack.
|
|
template <class T>
|
|
class DiverseValueBuffer {
|
|
llvm::SmallVector<char, sizeof(T) + 10 * sizeof(void*)> data;
|
|
|
|
public:
|
|
DiverseValueBuffer(const T &value) {
|
|
size_t size = value.allocated_size();
|
|
data.reserve(size);
|
|
data.set_size(size);
|
|
memcpy(data.data(), reinterpret_cast<const void *>(&value), size);
|
|
}
|
|
|
|
T &getCopy() { return *reinterpret_cast<T *>(data.data()); }
|
|
};
|
|
|
|
} // end namespace swift
|
|
|
|
/// Allow stable_iterators to be put in things like TinyPtrVectors.
|
|
namespace llvm {
|
|
template <>
|
|
struct PointerLikeTypeTraits<
|
|
swift::DiverseStackBase::stable_iterator::AsPointer> {
|
|
using AsPointer = swift::DiverseStackBase::stable_iterator::AsPointer;
|
|
public:
|
|
static inline void *getAsVoidPointer(AsPointer ptr) {
|
|
return ptr.getAsVoidPointer();
|
|
}
|
|
static inline AsPointer getFromVoidPointer(void *ptr) {
|
|
return AsPointer::getFromVoidPointer(ptr);
|
|
}
|
|
|
|
enum {
|
|
NumLowBitsAvailable = AsPointer::NumLowBitsAvailable
|
|
};
|
|
};
|
|
}
|
|
|
|
#endif // SWIFT_BASIC_DIVERSESTACK_H
|