mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-12-02 10:35:39 +00:00
916 lines
27 KiB
C++
916 lines
27 KiB
C++
// Copyright 2015, VIXL authors
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without
|
|
// specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#ifndef VIXL_INVALSET_H_
|
|
#define VIXL_INVALSET_H_
|
|
|
|
#include <cstring>
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "globals-vixl.h"
|
|
|
|
namespace vixl {
|
|
|
|
// We define a custom data structure template and its iterator as `std`
|
|
// containers do not fit the performance requirements for some of our use cases.
|
|
//
|
|
// The structure behaves like an iterable unordered set with special properties
|
|
// and restrictions. "InvalSet" stands for "Invalidatable Set".
|
|
//
|
|
// Restrictions and requirements:
|
|
// - Adding an element already present in the set is illegal. In debug mode,
|
|
// this is checked at insertion time.
|
|
// - The templated class `ElementType` must provide comparison operators so that
|
|
// `std::sort()` can be used.
|
|
// - A key must be available to represent invalid elements.
|
|
// - Elements with an invalid key must compare higher or equal to any other
|
|
// element.
|
|
//
|
|
// Use cases and performance considerations:
|
|
// Our use cases present two specificities that allow us to design this
|
|
// structure to provide fast insertion *and* fast search and deletion
|
|
// operations:
|
|
// - Elements are (generally) inserted in order (sorted according to their key).
|
|
// - A key is available to mark elements as invalid (deleted).
|
|
// The backing `std::vector` allows for fast insertions. When
|
|
// searching for an element we ensure the elements are sorted (this is generally
|
|
// the case) and perform a binary search. When deleting an element we do not
|
|
// free the associated memory immediately. Instead, an element to be deleted is
|
|
// marked with the 'invalid' key. Other methods of the container take care of
|
|
// ignoring entries marked as invalid.
|
|
// To avoid the overhead of the `std::vector` container when only few entries
|
|
// are used, a number of elements are preallocated.
|
|
|
|
// 'ElementType' and 'KeyType' are respectively the types of the elements and
|
|
// their key. The structure only reclaims memory when safe to do so, if the
|
|
// number of elements that can be reclaimed is greater than `RECLAIM_FROM` and
|
|
// greater than `<total number of elements> / RECLAIM_FACTOR.
|
|
// clang-format off
|
|
#define TEMPLATE_INVALSET_P_DECL \
|
|
class ElementType, \
|
|
unsigned N_PREALLOCATED_ELEMENTS, \
|
|
class KeyType, \
|
|
KeyType INVALID_KEY, \
|
|
size_t RECLAIM_FROM, \
|
|
unsigned RECLAIM_FACTOR
|
|
// clang-format on
|
|
|
|
#define TEMPLATE_INVALSET_P_DEF \
|
|
ElementType, N_PREALLOCATED_ELEMENTS, KeyType, INVALID_KEY, RECLAIM_FROM, \
|
|
RECLAIM_FACTOR
|
|
|
|
template <class S>
|
|
class InvalSetIterator; // Forward declaration.
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
class InvalSet {
|
|
public:
|
|
InvalSet();
|
|
~InvalSet();
|
|
|
|
static const size_t kNPreallocatedElements = N_PREALLOCATED_ELEMENTS;
|
|
static const KeyType kInvalidKey = INVALID_KEY;
|
|
|
|
// C++ STL iterator interface.
|
|
typedef InvalSetIterator<InvalSet<TEMPLATE_INVALSET_P_DEF> > iterator;
|
|
iterator begin();
|
|
iterator end();
|
|
|
|
// It is illegal to insert an element already present in the set.
|
|
void insert(const ElementType& element);
|
|
|
|
// Looks for the specified element in the set and - if found - deletes it.
|
|
// The return value is the number of elements erased: either 0 or 1.
|
|
size_t erase(const ElementType& element);
|
|
|
|
// This indicates the number of (valid) elements stored in this set.
|
|
size_t size() const;
|
|
|
|
// Returns true if no elements are stored in the set.
|
|
// Note that this does not mean the the backing storage is empty: it can still
|
|
// contain invalid elements.
|
|
bool empty() const;
|
|
|
|
void clear();
|
|
|
|
const ElementType GetMinElement();
|
|
|
|
// This returns the key of the minimum element in the set.
|
|
KeyType GetMinElementKey();
|
|
|
|
static bool IsValid(const ElementType& element);
|
|
static KeyType GetKey(const ElementType& element);
|
|
static void SetKey(ElementType* element, KeyType key);
|
|
|
|
typedef ElementType _ElementType;
|
|
typedef KeyType _KeyType;
|
|
|
|
protected:
|
|
// Returns a pointer to the element in vector_ if it was found, or NULL
|
|
// otherwise.
|
|
ElementType* Search(const ElementType& element);
|
|
|
|
// The argument *must* point to an element stored in *this* set.
|
|
// This function is not allowed to move elements in the backing vector
|
|
// storage.
|
|
void EraseInternal(ElementType* element);
|
|
|
|
// The elements in the range searched must be sorted.
|
|
ElementType* BinarySearch(const ElementType& element,
|
|
ElementType* start,
|
|
ElementType* end) const;
|
|
|
|
// Sort the elements.
|
|
enum SortType {
|
|
// The 'hard' version guarantees that invalid elements are moved to the end
|
|
// of the container.
|
|
kHardSort,
|
|
// The 'soft' version only guarantees that the elements will be sorted.
|
|
// Invalid elements may still be present anywhere in the set.
|
|
kSoftSort
|
|
};
|
|
void Sort(SortType sort_type);
|
|
|
|
// Delete the elements that have an invalid key. The complexity is linear
|
|
// with the size of the vector.
|
|
void Clean();
|
|
|
|
const ElementType Front() const;
|
|
const ElementType Back() const;
|
|
|
|
// Delete invalid trailing elements and return the last valid element in the
|
|
// set.
|
|
const ElementType CleanBack();
|
|
|
|
// Returns a pointer to the start or end of the backing storage.
|
|
const ElementType* StorageBegin() const;
|
|
const ElementType* StorageEnd() const;
|
|
ElementType* StorageBegin();
|
|
ElementType* StorageEnd();
|
|
|
|
// Returns the index of the element within the backing storage. The element
|
|
// must belong to the backing storage.
|
|
size_t GetElementIndex(const ElementType* element) const;
|
|
|
|
// Returns the element at the specified index in the backing storage.
|
|
const ElementType* GetElementAt(size_t index) const;
|
|
ElementType* GetElementAt(size_t index);
|
|
|
|
static const ElementType* GetFirstValidElement(const ElementType* from,
|
|
const ElementType* end);
|
|
|
|
void CacheMinElement();
|
|
const ElementType GetCachedMinElement() const;
|
|
|
|
bool ShouldReclaimMemory() const;
|
|
void ReclaimMemory();
|
|
|
|
bool IsUsingVector() const { return vector_ != NULL; }
|
|
void SetSorted(bool sorted) { sorted_ = sorted; }
|
|
|
|
// We cache some data commonly required by users to improve performance.
|
|
// We cannot cache pointers to elements as we do not control the backing
|
|
// storage.
|
|
bool valid_cached_min_;
|
|
size_t cached_min_index_; // Valid iff `valid_cached_min_` is true.
|
|
KeyType cached_min_key_; // Valid iff `valid_cached_min_` is true.
|
|
|
|
// Indicates whether the elements are sorted.
|
|
bool sorted_;
|
|
|
|
// This represents the number of (valid) elements in this set.
|
|
size_t size_;
|
|
|
|
// The backing storage is either the array of preallocated elements or the
|
|
// vector. The structure starts by using the preallocated elements, and
|
|
// transitions (permanently) to using the vector once more than
|
|
// kNPreallocatedElements are used.
|
|
// Elements are only invalidated when using the vector. The preallocated
|
|
// storage always only contains valid elements.
|
|
ElementType preallocated_[kNPreallocatedElements];
|
|
std::vector<ElementType>* vector_;
|
|
|
|
// Iterators acquire and release this monitor. While a set is acquired,
|
|
// certain operations are illegal to ensure that the iterator will
|
|
// correctly iterate over the elements in the set.
|
|
int monitor_;
|
|
#ifdef VIXL_DEBUG
|
|
int monitor() const { return monitor_; }
|
|
void Acquire() { monitor_++; }
|
|
void Release() {
|
|
monitor_--;
|
|
VIXL_ASSERT(monitor_ >= 0);
|
|
}
|
|
#endif
|
|
|
|
private:
|
|
// The copy constructor and assignment operator are not used and the defaults
|
|
// are unsafe, so disable them (without an implementation).
|
|
#if __cplusplus >= 201103L
|
|
InvalSet(const InvalSet& other) = delete;
|
|
InvalSet operator=(const InvalSet& other) = delete;
|
|
#else
|
|
InvalSet(const InvalSet& other);
|
|
InvalSet operator=(const InvalSet& other);
|
|
#endif
|
|
|
|
friend class InvalSetIterator<InvalSet<TEMPLATE_INVALSET_P_DEF> >;
|
|
};
|
|
|
|
|
|
template <class S>
|
|
class InvalSetIterator/* : public std::iterator<std::forward_iterator_tag,
|
|
typename S::_ElementType> */{
|
|
private:
|
|
// Redefine types to mirror the associated set types.
|
|
typedef typename S::_ElementType ElementType;
|
|
typedef typename S::_KeyType KeyType;
|
|
|
|
public:
|
|
explicit InvalSetIterator(S* inval_set = NULL);
|
|
|
|
// This class implements the standard copy-swap idiom.
|
|
~InvalSetIterator();
|
|
InvalSetIterator(const InvalSetIterator<S>& other);
|
|
InvalSetIterator<S>& operator=(InvalSetIterator<S> other);
|
|
#if __cplusplus >= 201103L
|
|
InvalSetIterator(InvalSetIterator<S>&& other) noexcept;
|
|
#endif
|
|
|
|
friend void swap(InvalSetIterator<S>& a, InvalSetIterator<S>& b) {
|
|
using std::swap;
|
|
swap(a.using_vector_, b.using_vector_);
|
|
swap(a.index_, b.index_);
|
|
swap(a.inval_set_, b.inval_set_);
|
|
}
|
|
|
|
// Return true if the iterator is at the end of the set.
|
|
bool Done() const;
|
|
|
|
// Move this iterator to the end of the set.
|
|
void Finish();
|
|
|
|
// Delete the current element and advance the iterator to point to the next
|
|
// element.
|
|
void DeleteCurrentAndAdvance();
|
|
|
|
static bool IsValid(const ElementType& element);
|
|
static KeyType GetKey(const ElementType& element);
|
|
|
|
// Extra helpers to support the forward-iterator interface.
|
|
InvalSetIterator<S>& operator++(); // Pre-increment.
|
|
InvalSetIterator<S> operator++(int); // Post-increment.
|
|
bool operator==(const InvalSetIterator<S>& rhs) const;
|
|
bool operator!=(const InvalSetIterator<S>& rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
ElementType& operator*() { return *Current(); }
|
|
const ElementType& operator*() const { return *Current(); }
|
|
ElementType* operator->() { return Current(); }
|
|
const ElementType* operator->() const { return Current(); }
|
|
|
|
protected:
|
|
void MoveToValidElement();
|
|
|
|
// Indicates if the iterator is looking at the vector or at the preallocated
|
|
// elements.
|
|
bool using_vector_;
|
|
// Used when looking at the preallocated elements, or in debug mode when using
|
|
// the vector to track how many times the iterator has advanced.
|
|
size_t index_;
|
|
typename std::vector<ElementType>::iterator iterator_;
|
|
S* inval_set_;
|
|
|
|
// TODO: These helpers are deprecated and will be removed in future versions
|
|
// of VIXL.
|
|
ElementType* Current() const;
|
|
void Advance();
|
|
};
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
InvalSet<TEMPLATE_INVALSET_P_DEF>::InvalSet()
|
|
: valid_cached_min_(false), sorted_(true), size_(0), vector_(NULL) {
|
|
#ifdef VIXL_DEBUG
|
|
monitor_ = 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
InvalSet<TEMPLATE_INVALSET_P_DEF>::~InvalSet() {
|
|
VIXL_ASSERT(monitor_ == 0);
|
|
delete vector_;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
typename InvalSet<TEMPLATE_INVALSET_P_DEF>::iterator
|
|
InvalSet<TEMPLATE_INVALSET_P_DEF>::begin() {
|
|
return iterator(this);
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
typename InvalSet<TEMPLATE_INVALSET_P_DEF>::iterator
|
|
InvalSet<TEMPLATE_INVALSET_P_DEF>::end() {
|
|
iterator end(this);
|
|
end.Finish();
|
|
return end;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
void InvalSet<TEMPLATE_INVALSET_P_DEF>::insert(const ElementType& element) {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
VIXL_ASSERT(IsValid(element));
|
|
VIXL_ASSERT(Search(element) == NULL);
|
|
SetSorted(empty() || (sorted_ && (element > CleanBack())));
|
|
if (IsUsingVector()) {
|
|
vector_->push_back(element);
|
|
} else {
|
|
if (size_ < kNPreallocatedElements) {
|
|
preallocated_[size_] = element;
|
|
} else {
|
|
// Transition to using the vector.
|
|
vector_ =
|
|
new std::vector<ElementType>(preallocated_, preallocated_ + size_);
|
|
vector_->push_back(element);
|
|
}
|
|
}
|
|
size_++;
|
|
|
|
if (valid_cached_min_ && (element < GetMinElement())) {
|
|
cached_min_index_ = IsUsingVector() ? vector_->size() - 1 : size_ - 1;
|
|
cached_min_key_ = GetKey(element);
|
|
valid_cached_min_ = true;
|
|
}
|
|
|
|
if (ShouldReclaimMemory()) {
|
|
ReclaimMemory();
|
|
}
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
size_t InvalSet<TEMPLATE_INVALSET_P_DEF>::erase(const ElementType& element) {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
VIXL_ASSERT(IsValid(element));
|
|
ElementType* local_element = Search(element);
|
|
if (local_element != NULL) {
|
|
EraseInternal(local_element);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::Search(
|
|
const ElementType& element) {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
if (empty()) {
|
|
return NULL;
|
|
}
|
|
if (ShouldReclaimMemory()) {
|
|
ReclaimMemory();
|
|
}
|
|
if (!sorted_) {
|
|
Sort(kHardSort);
|
|
}
|
|
if (!valid_cached_min_) {
|
|
CacheMinElement();
|
|
}
|
|
return BinarySearch(element, GetElementAt(cached_min_index_), StorageEnd());
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
size_t InvalSet<TEMPLATE_INVALSET_P_DEF>::size() const {
|
|
return size_;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
bool InvalSet<TEMPLATE_INVALSET_P_DEF>::empty() const {
|
|
return size_ == 0;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
void InvalSet<TEMPLATE_INVALSET_P_DEF>::clear() {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
size_ = 0;
|
|
if (IsUsingVector()) {
|
|
vector_->clear();
|
|
}
|
|
SetSorted(true);
|
|
valid_cached_min_ = false;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
const ElementType InvalSet<TEMPLATE_INVALSET_P_DEF>::GetMinElement() {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
VIXL_ASSERT(!empty());
|
|
CacheMinElement();
|
|
return *GetElementAt(cached_min_index_);
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
KeyType InvalSet<TEMPLATE_INVALSET_P_DEF>::GetMinElementKey() {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
if (valid_cached_min_) {
|
|
return cached_min_key_;
|
|
} else {
|
|
return GetKey(GetMinElement());
|
|
}
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
bool InvalSet<TEMPLATE_INVALSET_P_DEF>::IsValid(const ElementType& element) {
|
|
return GetKey(element) != kInvalidKey;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
void InvalSet<TEMPLATE_INVALSET_P_DEF>::EraseInternal(ElementType* element) {
|
|
// Note that this function must be safe even while an iterator has acquired
|
|
// this set.
|
|
VIXL_ASSERT(element != NULL);
|
|
size_t deleted_index = GetElementIndex(element);
|
|
if (IsUsingVector()) {
|
|
VIXL_ASSERT((&(vector_->front()) <= element) &&
|
|
(element <= &(vector_->back())));
|
|
SetKey(element, kInvalidKey);
|
|
} else {
|
|
VIXL_ASSERT((preallocated_ <= element) &&
|
|
(element < (preallocated_ + kNPreallocatedElements)));
|
|
ElementType* end = preallocated_ + kNPreallocatedElements;
|
|
size_t copy_size = sizeof(*element) * (end - element - 1);
|
|
memmove(element, element + 1, copy_size);
|
|
}
|
|
size_--;
|
|
|
|
if (valid_cached_min_ && (deleted_index == cached_min_index_)) {
|
|
if (sorted_ && !empty()) {
|
|
const ElementType* min = GetFirstValidElement(element, StorageEnd());
|
|
cached_min_index_ = GetElementIndex(min);
|
|
cached_min_key_ = GetKey(*min);
|
|
valid_cached_min_ = true;
|
|
} else {
|
|
valid_cached_min_ = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::BinarySearch(
|
|
const ElementType& element, ElementType* start, ElementType* end) const {
|
|
if (start == end) {
|
|
return NULL;
|
|
}
|
|
VIXL_ASSERT(sorted_);
|
|
VIXL_ASSERT(start < end);
|
|
VIXL_ASSERT(!empty());
|
|
|
|
// Perform a binary search through the elements while ignoring invalid
|
|
// elements.
|
|
ElementType* elements = start;
|
|
size_t low = 0;
|
|
size_t high = (end - start) - 1;
|
|
while (low < high) {
|
|
// Find valid bounds.
|
|
while (!IsValid(elements[low]) && (low < high)) ++low;
|
|
while (!IsValid(elements[high]) && (low < high)) --high;
|
|
VIXL_ASSERT(low <= high);
|
|
// Avoid overflow when computing the middle index.
|
|
size_t middle = low + (high - low) / 2;
|
|
if ((middle == low) || (middle == high)) {
|
|
break;
|
|
}
|
|
while ((middle < high - 1) && !IsValid(elements[middle])) ++middle;
|
|
while ((low + 1 < middle) && !IsValid(elements[middle])) --middle;
|
|
if (!IsValid(elements[middle])) {
|
|
break;
|
|
}
|
|
if (elements[middle] < element) {
|
|
low = middle;
|
|
} else {
|
|
high = middle;
|
|
}
|
|
}
|
|
|
|
if (elements[low] == element) return &elements[low];
|
|
if (elements[high] == element) return &elements[high];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
void InvalSet<TEMPLATE_INVALSET_P_DEF>::Sort(SortType sort_type) {
|
|
if (sort_type == kSoftSort) {
|
|
if (sorted_) {
|
|
return;
|
|
}
|
|
}
|
|
VIXL_ASSERT(monitor() == 0);
|
|
if (empty()) {
|
|
return;
|
|
}
|
|
|
|
Clean();
|
|
std::sort(StorageBegin(), StorageEnd());
|
|
|
|
SetSorted(true);
|
|
cached_min_index_ = 0;
|
|
cached_min_key_ = GetKey(Front());
|
|
valid_cached_min_ = true;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
void InvalSet<TEMPLATE_INVALSET_P_DEF>::Clean() {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
if (empty() || !IsUsingVector()) {
|
|
return;
|
|
}
|
|
// Manually iterate through the vector storage to discard invalid elements.
|
|
ElementType* start = &(vector_->front());
|
|
ElementType* end = start + vector_->size();
|
|
ElementType* c = start;
|
|
ElementType* first_invalid;
|
|
ElementType* first_valid;
|
|
ElementType* next_invalid;
|
|
|
|
while ((c < end) && IsValid(*c)) c++;
|
|
first_invalid = c;
|
|
|
|
while (c < end) {
|
|
while ((c < end) && !IsValid(*c)) c++;
|
|
first_valid = c;
|
|
while ((c < end) && IsValid(*c)) c++;
|
|
next_invalid = c;
|
|
|
|
ptrdiff_t n_moved_elements = (next_invalid - first_valid);
|
|
memmove(first_invalid, first_valid, n_moved_elements * sizeof(*c));
|
|
first_invalid = first_invalid + n_moved_elements;
|
|
c = next_invalid;
|
|
}
|
|
|
|
// Delete the trailing invalid elements.
|
|
vector_->erase(vector_->begin() + (first_invalid - start), vector_->end());
|
|
VIXL_ASSERT(vector_->size() == size_);
|
|
|
|
if (sorted_) {
|
|
valid_cached_min_ = true;
|
|
cached_min_index_ = 0;
|
|
cached_min_key_ = GetKey(*GetElementAt(0));
|
|
} else {
|
|
valid_cached_min_ = false;
|
|
}
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
const ElementType InvalSet<TEMPLATE_INVALSET_P_DEF>::Front() const {
|
|
VIXL_ASSERT(!empty());
|
|
return IsUsingVector() ? vector_->front() : preallocated_[0];
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
const ElementType InvalSet<TEMPLATE_INVALSET_P_DEF>::Back() const {
|
|
VIXL_ASSERT(!empty());
|
|
return IsUsingVector() ? vector_->back() : preallocated_[size_ - 1];
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
const ElementType InvalSet<TEMPLATE_INVALSET_P_DEF>::CleanBack() {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
if (IsUsingVector()) {
|
|
// Delete the invalid trailing elements.
|
|
typename std::vector<ElementType>::reverse_iterator it = vector_->rbegin();
|
|
while (!IsValid(*it)) {
|
|
it++;
|
|
}
|
|
vector_->erase(it.base(), vector_->end());
|
|
}
|
|
return Back();
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
const ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::StorageBegin() const {
|
|
return IsUsingVector() ? &(vector_->front()) : preallocated_;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
const ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::StorageEnd() const {
|
|
return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::StorageBegin() {
|
|
return IsUsingVector() ? &(vector_->front()) : preallocated_;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::StorageEnd() {
|
|
return IsUsingVector() ? &(vector_->back()) + 1 : preallocated_ + size_;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
size_t InvalSet<TEMPLATE_INVALSET_P_DEF>::GetElementIndex(
|
|
const ElementType* element) const {
|
|
VIXL_ASSERT((StorageBegin() <= element) && (element < StorageEnd()));
|
|
return element - StorageBegin();
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
const ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::GetElementAt(
|
|
size_t index) const {
|
|
VIXL_ASSERT((IsUsingVector() && (index < vector_->size())) ||
|
|
(index < size_));
|
|
return StorageBegin() + index;
|
|
}
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::GetElementAt(size_t index) {
|
|
VIXL_ASSERT((IsUsingVector() && (index < vector_->size())) ||
|
|
(index < size_));
|
|
return StorageBegin() + index;
|
|
}
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
const ElementType* InvalSet<TEMPLATE_INVALSET_P_DEF>::GetFirstValidElement(
|
|
const ElementType* from, const ElementType* end) {
|
|
while ((from < end) && !IsValid(*from)) {
|
|
from++;
|
|
}
|
|
return from;
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
void InvalSet<TEMPLATE_INVALSET_P_DEF>::CacheMinElement() {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
VIXL_ASSERT(!empty());
|
|
|
|
if (valid_cached_min_) {
|
|
return;
|
|
}
|
|
|
|
if (sorted_) {
|
|
const ElementType* min = GetFirstValidElement(StorageBegin(), StorageEnd());
|
|
cached_min_index_ = GetElementIndex(min);
|
|
cached_min_key_ = GetKey(*min);
|
|
valid_cached_min_ = true;
|
|
} else {
|
|
Sort(kHardSort);
|
|
}
|
|
VIXL_ASSERT(valid_cached_min_);
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
bool InvalSet<TEMPLATE_INVALSET_P_DEF>::ShouldReclaimMemory() const {
|
|
if (!IsUsingVector()) {
|
|
return false;
|
|
}
|
|
size_t n_invalid_elements = vector_->size() - size_;
|
|
return (n_invalid_elements > RECLAIM_FROM) &&
|
|
(n_invalid_elements > vector_->size() / RECLAIM_FACTOR);
|
|
}
|
|
|
|
|
|
template <TEMPLATE_INVALSET_P_DECL>
|
|
void InvalSet<TEMPLATE_INVALSET_P_DEF>::ReclaimMemory() {
|
|
VIXL_ASSERT(monitor() == 0);
|
|
Clean();
|
|
}
|
|
|
|
|
|
template <class S>
|
|
InvalSetIterator<S>::InvalSetIterator(S* inval_set)
|
|
: using_vector_((inval_set != NULL) && inval_set->IsUsingVector()),
|
|
index_(0),
|
|
inval_set_(inval_set) {
|
|
if (inval_set != NULL) {
|
|
inval_set->Sort(S::kSoftSort);
|
|
#ifdef VIXL_DEBUG
|
|
inval_set->Acquire();
|
|
#endif
|
|
if (using_vector_) {
|
|
iterator_ = typename std::vector<ElementType>::iterator(
|
|
inval_set_->vector_->begin());
|
|
}
|
|
MoveToValidElement();
|
|
}
|
|
}
|
|
|
|
|
|
template <class S>
|
|
InvalSetIterator<S>::~InvalSetIterator() {
|
|
#ifdef VIXL_DEBUG
|
|
if (inval_set_ != NULL) inval_set_->Release();
|
|
#endif
|
|
}
|
|
|
|
|
|
template <class S>
|
|
typename S::_ElementType* InvalSetIterator<S>::Current() const {
|
|
VIXL_ASSERT(!Done());
|
|
if (using_vector_) {
|
|
return &(*iterator_);
|
|
} else {
|
|
return &(inval_set_->preallocated_[index_]);
|
|
}
|
|
}
|
|
|
|
|
|
template <class S>
|
|
void InvalSetIterator<S>::Advance() {
|
|
++(*this);
|
|
}
|
|
|
|
|
|
template <class S>
|
|
bool InvalSetIterator<S>::Done() const {
|
|
if (using_vector_) {
|
|
bool done = (iterator_ == inval_set_->vector_->end());
|
|
VIXL_ASSERT(done == (index_ == inval_set_->size()));
|
|
return done;
|
|
} else {
|
|
return index_ == inval_set_->size();
|
|
}
|
|
}
|
|
|
|
|
|
template <class S>
|
|
void InvalSetIterator<S>::Finish() {
|
|
VIXL_ASSERT(inval_set_->sorted_);
|
|
if (using_vector_) {
|
|
iterator_ = inval_set_->vector_->end();
|
|
}
|
|
index_ = inval_set_->size();
|
|
}
|
|
|
|
|
|
template <class S>
|
|
void InvalSetIterator<S>::DeleteCurrentAndAdvance() {
|
|
if (using_vector_) {
|
|
inval_set_->EraseInternal(&(*iterator_));
|
|
MoveToValidElement();
|
|
} else {
|
|
inval_set_->EraseInternal(inval_set_->preallocated_ + index_);
|
|
}
|
|
}
|
|
|
|
|
|
template <class S>
|
|
bool InvalSetIterator<S>::IsValid(const ElementType& element) {
|
|
return S::IsValid(element);
|
|
}
|
|
|
|
|
|
template <class S>
|
|
typename S::_KeyType InvalSetIterator<S>::GetKey(const ElementType& element) {
|
|
return S::GetKey(element);
|
|
}
|
|
|
|
|
|
template <class S>
|
|
void InvalSetIterator<S>::MoveToValidElement() {
|
|
if (using_vector_) {
|
|
while ((iterator_ != inval_set_->vector_->end()) && !IsValid(*iterator_)) {
|
|
iterator_++;
|
|
}
|
|
} else {
|
|
VIXL_ASSERT(inval_set_->empty() || IsValid(inval_set_->preallocated_[0]));
|
|
// Nothing to do.
|
|
}
|
|
}
|
|
|
|
|
|
template <class S>
|
|
InvalSetIterator<S>::InvalSetIterator(const InvalSetIterator<S>& other)
|
|
: using_vector_(other.using_vector_),
|
|
index_(other.index_),
|
|
inval_set_(other.inval_set_) {
|
|
#ifdef VIXL_DEBUG
|
|
if (inval_set_ != NULL) inval_set_->Acquire();
|
|
#endif
|
|
}
|
|
|
|
|
|
#if __cplusplus >= 201103L
|
|
template <class S>
|
|
InvalSetIterator<S>::InvalSetIterator(InvalSetIterator<S>&& other) noexcept
|
|
: using_vector_(false),
|
|
index_(0),
|
|
inval_set_(NULL) {
|
|
swap(*this, other);
|
|
}
|
|
#endif
|
|
|
|
|
|
template <class S>
|
|
InvalSetIterator<S>& InvalSetIterator<S>::operator=(InvalSetIterator<S> other) {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
|
|
|
|
template <class S>
|
|
bool InvalSetIterator<S>::operator==(const InvalSetIterator<S>& rhs) const {
|
|
bool equal = (inval_set_ == rhs.inval_set_);
|
|
|
|
// If the inval_set_ matches, using_vector_ must also match.
|
|
VIXL_ASSERT(!equal || (using_vector_ == rhs.using_vector_));
|
|
|
|
if (using_vector_) {
|
|
equal = equal && (iterator_ == rhs.iterator_);
|
|
// In debug mode, index_ is maintained even with using_vector_.
|
|
VIXL_ASSERT(!equal || (index_ == rhs.index_));
|
|
} else {
|
|
equal = equal && (index_ == rhs.index_);
|
|
#ifdef DEBUG
|
|
// If not using_vector_, iterator_ should be default-initialised.
|
|
typename std::vector<ElementType>::iterator default_iterator;
|
|
VIXL_ASSERT(iterator_ == default_iterator);
|
|
VIXL_ASSERT(rhs.iterator_ == default_iterator);
|
|
#endif
|
|
}
|
|
return equal;
|
|
}
|
|
|
|
|
|
template <class S>
|
|
InvalSetIterator<S>& InvalSetIterator<S>::operator++() {
|
|
// Pre-increment.
|
|
VIXL_ASSERT(!Done());
|
|
if (using_vector_) {
|
|
iterator_++;
|
|
#ifdef VIXL_DEBUG
|
|
index_++;
|
|
#endif
|
|
MoveToValidElement();
|
|
} else {
|
|
index_++;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
template <class S>
|
|
InvalSetIterator<S> InvalSetIterator<S>::operator++(int /* unused */) {
|
|
// Post-increment.
|
|
VIXL_ASSERT(!Done());
|
|
InvalSetIterator<S> old(*this);
|
|
++(*this);
|
|
return old;
|
|
}
|
|
|
|
|
|
#undef TEMPLATE_INVALSET_P_DECL
|
|
#undef TEMPLATE_INVALSET_P_DEF
|
|
|
|
} // namespace vixl
|
|
|
|
#endif // VIXL_INVALSET_H_
|