%PDF- %PDF-
| Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/zone/ |
| Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/zone/zone-compact-set.h |
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_ZONE_ZONE_COMPACT_SET_H_
#define V8_ZONE_ZONE_COMPACT_SET_H_
#include <algorithm>
#include <initializer_list>
#include <type_traits>
#include "src/base/compiler-specific.h"
#include "src/base/pointer-with-payload.h"
#include "src/common/assert-scope.h"
#include "src/handles/handles.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
template <typename T, typename Enable = void>
struct ZoneCompactSetTraits;
template <typename T>
struct ZoneCompactSetTraits<Handle<T>> {
using handle_type = Handle<T>;
using data_type = Address;
static data_type* HandleToPointer(handle_type handle) {
// Use address() instead of location() to get around handle access checks
// (we're not actually dereferencing the handle so it's safe to read its
// location)
return base::bit_cast<Address*>(handle.address());
}
static handle_type PointerToHandle(data_type* ptr) {
return handle_type(ptr);
}
};
// A Zone-allocated set which has a compact encoding of zero and one values.
// Two or more values will be stored as a sorted list, which is copied on write
// to keep the ZoneCompactSet copy constructor trivial. Note that this means
// that insertions past the first value will trigger an allocation and copy of
// the existing elements -- ZoneCompactSet should be preferred for cases where
// we mostly have only zero or one values.
//
// T must be a Handle-like type with a specialization of ZoneCompactSetTraits.
// In particular, it must be a trivial wrapper of a pointer to actual data --
// ZoneCompactSet will store this pointer rather than the T type.
template <typename T>
class ZoneCompactSet final {
static_assert(std::is_trivially_copyable_v<T>);
static_assert(std::is_trivially_destructible_v<T>);
using Traits = ZoneCompactSetTraits<T>;
using handle_type = typename Traits::handle_type;
using data_type = typename Traits::data_type;
public:
ZoneCompactSet() : data_(kEmptyTag) {}
explicit ZoneCompactSet(T handle)
: data_(Traits::HandleToPointer(handle), kSingletonTag) {}
explicit ZoneCompactSet(std::initializer_list<T> handles, Zone* zone)
: ZoneCompactSet(handles.begin(), handles.end(), zone) {}
ZoneCompactSet(const ZoneCompactSet& other) V8_NOEXCEPT = default;
ZoneCompactSet& operator=(const ZoneCompactSet& other) V8_NOEXCEPT = default;
ZoneCompactSet(ZoneCompactSet&& other) V8_NOEXCEPT = default;
ZoneCompactSet& operator=(ZoneCompactSet&& other) V8_NOEXCEPT = default;
template <class It,
typename = typename std::iterator_traits<It>::iterator_category>
explicit ZoneCompactSet(It first, It last, Zone* zone) {
auto size = last - first;
if (size == 0) {
data_ = EmptyValue();
} else if (size == 1) {
data_ =
PointerWithPayload(Traits::HandleToPointer(*first), kSingletonTag);
} else {
List* list = NewList(size, zone);
auto list_it = list->begin();
for (auto it = first; it != last; ++it) {
*list_it = Traits::HandleToPointer(*it);
list_it++;
}
std::sort(list->begin(), list->end());
data_ = PointerWithPayload(list, kListTag);
}
}
bool is_empty() const { return data_ == EmptyValue(); }
size_t size() const {
if (is_empty()) return 0;
if (is_singleton()) return 1;
return list()->size();
}
T at(size_t i) const {
DCHECK_NE(kEmptyTag, data_.GetPayload());
if (is_singleton()) {
DCHECK_EQ(0u, i);
return Traits::PointerToHandle(singleton());
}
return Traits::PointerToHandle(list()->at(static_cast<int>(i)));
}
T operator[](size_t i) const { return at(i); }
void insert(T handle, Zone* zone) {
data_type* const value = Traits::HandleToPointer(handle);
if (is_empty()) {
data_ = PointerWithPayload(value, kSingletonTag);
} else if (is_singleton()) {
if (singleton() == value) return;
List* list = NewList(2, zone);
if (singleton() < value) {
(*list)[0] = singleton();
(*list)[1] = value;
} else {
(*list)[0] = value;
(*list)[1] = singleton();
}
data_ = PointerWithPayload(list, kListTag);
} else {
const List* current_list = list();
auto it =
std::lower_bound(current_list->begin(), current_list->end(), value);
if (it != current_list->end() && *it == value) {
// Already in the list.
return;
}
// Otherwise, lower_bound returned the insertion position to keep the list
// sorted.
DCHECK(it == current_list->end() || *it > value);
// We need to copy the list to mutate it, so that trivial copies of the
// data_ pointer don't observe changes to the list.
// TODO(leszeks): Avoid copying on every insertion by introducing some
// concept of mutable/immutable/frozen/CoW sets.
List* new_list = NewList(current_list->size() + 1, zone);
auto new_it = new_list->begin();
new_it = std::copy(current_list->begin(), it, new_it);
*new_it++ = value;
new_it = std::copy(it, current_list->end(), new_it);
DCHECK_EQ(new_it, new_list->end());
DCHECK(std::is_sorted(new_list->begin(), new_list->end()));
data_ = PointerWithPayload(new_list, kListTag);
}
}
void Union(ZoneCompactSet<T> const& other, Zone* zone) {
for (size_t i = 0; i < other.size(); ++i) {
insert(other.at(i), zone);
}
}
bool contains(ZoneCompactSet<T> const& other) const {
if (data_ == other.data_) return true;
if (is_empty()) return false;
if (other.is_empty()) return true;
if (is_singleton()) {
DCHECK_IMPLIES(other.is_singleton(), other.singleton() != singleton());
return false;
}
const List* list = this->list();
DCHECK(std::is_sorted(list->begin(), list->end()));
if (other.is_singleton()) {
return std::binary_search(list->begin(), list->end(), other.singleton());
}
DCHECK(other.is_list());
DCHECK(std::is_sorted(other.list()->begin(), other.list()->end()));
// For each element in the `other` list, find the matching element in this
// list. Since both lists are sorted, each search candidate will be larger
// than the previous, and each found element will be the lower bound for
// the search of the next element.
auto it = list->begin();
for (const data_type* pointer : *other.list()) {
it = std::lower_bound(it, list->end(), pointer);
if (it == list->end() || *it != pointer) return false;
}
return true;
}
bool contains(T handle) const {
if (is_empty()) return false;
data_type* pointer = Traits::HandleToPointer(handle);
if (is_singleton()) {
return singleton() == pointer;
}
const List* list = this->list();
DCHECK(std::is_sorted(list->begin(), list->end()));
return std::binary_search(list->begin(), list->end(), pointer);
}
void remove(T handle, Zone* zone) {
if (is_empty()) return;
data_type* pointer = Traits::HandleToPointer(handle);
if (is_singleton()) {
if (singleton() == pointer) {
data_ = EmptyValue();
}
return;
}
const List* current_list = list();
auto found_it =
std::lower_bound(current_list->begin(), current_list->end(), pointer);
if (found_it == current_list->end() || *found_it != pointer) {
// Not in the list.
return;
}
// Otherwise, lower_bound returned the location of the value.
// Drop back down to singleton mode if the size will drops to 1 -- this is
// needed to ensure that comparisons are correct. We never have to drop down
// from list to zero size.
DCHECK_GE(current_list->size(), 2);
if (current_list->size() == 2) {
data_type* other_value;
if (found_it == current_list->begin()) {
other_value = current_list->at(1);
} else {
other_value = current_list->at(0);
}
data_ = PointerWithPayload(other_value, kSingletonTag);
return;
}
// We need to copy the list to mutate it, so that trivial copies of the
// data_ pointer don't observe changes to the list.
List* new_list = NewList(current_list->size() - 1, zone);
auto new_it = new_list->begin();
new_it = std::copy(current_list->begin(), found_it, new_it);
new_it = std::copy(found_it + 1, current_list->end(), new_it);
DCHECK_EQ(new_it, new_list->end());
DCHECK(std::is_sorted(new_list->begin(), new_list->end()));
data_ = PointerWithPayload(new_list, kListTag);
}
void clear() { data_ = EmptyValue(); }
friend bool operator==(ZoneCompactSet<T> const& lhs,
ZoneCompactSet<T> const& rhs) {
if (lhs.data_ == rhs.data_) return true;
if (lhs.is_list() && rhs.is_list()) {
List const* const lhs_list = lhs.list();
List const* const rhs_list = rhs.list();
return std::equal(lhs_list->begin(), lhs_list->end(), rhs_list->begin(),
rhs_list->end());
}
return false;
}
friend bool operator!=(ZoneCompactSet<T> const& lhs,
ZoneCompactSet<T> const& rhs) {
return !(lhs == rhs);
}
friend uintptr_t hash_value(ZoneCompactSet<T> const& set) {
return set.data_.raw();
}
class const_iterator;
inline const_iterator begin() const;
inline const_iterator end() const;
private:
enum Tag { kSingletonTag = 0, kEmptyTag = 1, kListTag = 2 };
using List = base::Vector<data_type*>;
using PointerWithPayload = base::PointerWithPayload<void, Tag, 2>;
bool is_singleton() const { return data_.GetPayload() == kSingletonTag; }
bool is_list() const { return data_.GetPayload() == kListTag; }
List const* list() const {
DCHECK(is_list());
return static_cast<List const*>(data_.GetPointerWithKnownPayload(kListTag));
}
data_type* singleton() const {
return static_cast<data_type*>(
data_.GetPointerWithKnownPayload(kSingletonTag));
}
List* NewList(size_t size, Zone* zone) {
// We need to allocate both the List, and the backing store of the list, in
// the zone, so that we have a List pointer and not an on-stack List (which
// we can't use in the `data_` pointer).
return zone->New<List>(zone->AllocateArray<data_type*>(size), size);
}
static PointerWithPayload EmptyValue() {
return PointerWithPayload(nullptr, kEmptyTag);
}
PointerWithPayload data_;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, ZoneCompactSet<T> set) {
for (size_t i = 0; i < set.size(); ++i) {
if (i > 0) os << ", ";
os << set.at(i);
}
return os;
}
template <typename T>
class ZoneCompactSet<T>::const_iterator {
public:
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using reference = value_type;
using pointer = value_type*;
const_iterator(const const_iterator& other) = default;
const_iterator& operator=(const const_iterator& other) = default;
reference operator*() const { return (*set_)[current_]; }
bool operator==(const const_iterator& other) const {
return set_ == other.set_ && current_ == other.current_;
}
bool operator!=(const const_iterator& other) const {
return !(*this == other);
}
const_iterator& operator++() {
DCHECK(current_ < set_->size());
current_ += 1;
return *this;
}
const_iterator operator++(int);
private:
friend class ZoneCompactSet<T>;
explicit const_iterator(const ZoneCompactSet<T>* set, size_t current)
: set_(set), current_(current) {}
const ZoneCompactSet<T>* set_;
size_t current_;
};
template <typename T>
typename ZoneCompactSet<T>::const_iterator ZoneCompactSet<T>::begin() const {
return ZoneCompactSet<T>::const_iterator(this, 0);
}
template <typename T>
typename ZoneCompactSet<T>::const_iterator ZoneCompactSet<T>::end() const {
return ZoneCompactSet<T>::const_iterator(this, size());
}
template <typename T>
using ZoneHandleSet = ZoneCompactSet<Handle<T>>;
} // namespace internal
} // namespace v8
#endif // V8_ZONE_ZONE_COMPACT_SET_H_