// 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_HANDLE_SET_H_
#define V8_ZONE_ZONE_HANDLE_SET_H_
#include "src/handles.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
template <typename T>
class ZoneHandleSet final {
public:
ZoneHandleSet() : data_(kEmptyTag) {}
explicit ZoneHandleSet(Handle<T> handle)
: data_(bit_cast<intptr_t>(handle.address()) | kSingletonTag) {
DCHECK(IsAligned(bit_cast<intptr_t>(handle.address()), kPointerAlignment));
}
bool is_empty() const { return data_ == kEmptyTag; }
size_t size() const {
if ((data_ & kTagMask) == kEmptyTag) return 0;
if ((data_ & kTagMask) == kSingletonTag) return 1;
return list()->length();
}
Handle<T> at(size_t i) const {
DCHECK_NE(kEmptyTag, data_ & kTagMask);
if ((data_ & kTagMask) == kSingletonTag) {
DCHECK_EQ(0u, i);
return Handle<T>(singleton());
}
return Handle<T>(list()->at(static_cast<int>(i)));
}
Handle<T> operator[](size_t i) const { return at(i); }
void insert(Handle<T> handle, Zone* zone) {
T** const value = bit_cast<T**>(handle.address());
DCHECK(IsAligned(bit_cast<intptr_t>(value), kPointerAlignment));
if ((data_ & kTagMask) == kEmptyTag) {
data_ = bit_cast<intptr_t>(value) | kSingletonTag;
} else if ((data_ & kTagMask) == kSingletonTag) {
if (singleton() == value) return;
List* list = new (zone) List(2, zone);
if (singleton() < value) {
list->Add(singleton(), zone);
list->Add(value, zone);
} else {
list->Add(value, zone);
list->Add(singleton(), zone);
}
DCHECK(IsAligned(bit_cast<intptr_t>(list), kPointerAlignment));
data_ = bit_cast<intptr_t>(list) | kListTag;
} else {
DCHECK_EQ(kListTag, data_ & kTagMask);
List const* const old_list = list();
for (int i = 0; i < old_list->length(); ++i) {
if (old_list->at(i) == value) return;
if (old_list->at(i) > value) break;
}
List* new_list = new (zone) List(old_list->length() + 1, zone);
int i = 0;
for (; i < old_list->length(); ++i) {
if (old_list->at(i) > value) break;
new_list->Add(old_list->at(i), zone);
}
new_list->Add(value, zone);
for (; i < old_list->length(); ++i) {
new_list->Add(old_list->at(i), zone);
}
DCHECK_EQ(old_list->length() + 1, new_list->length());
DCHECK(IsAligned(bit_cast<intptr_t>(new_list), kPointerAlignment));
data_ = bit_cast<intptr_t>(new_list) | kListTag;
}
}
bool contains(ZoneHandleSet<T> const& other) const {
if (data_ == other.data_) return true;
if (data_ == kEmptyTag) return false;
if (other.data_ == kEmptyTag) return true;
if ((data_ & kTagMask) == kSingletonTag) return false;
DCHECK_EQ(kListTag, data_ & kTagMask);
if ((other.data_ & kTagMask) == kSingletonTag) {
return list()->Contains(other.singleton());
}
DCHECK_EQ(kListTag, other.data_ & kTagMask);
// TODO(bmeurer): Optimize this case.
for (int i = 0; i < other.list()->length(); ++i) {
if (!list()->Contains(other.list()->at(i))) return false;
}
return true;
}
void remove(Handle<T> handle, Zone* zone) {
// TODO(bmeurer): Optimize this case.
ZoneHandleSet<T> that;
for (size_t i = 0; i < size(); ++i) {
Handle<T> value = at(i);
if (value.address() != handle.address()) {
that.insert(value, zone);
}
}
std::swap(*this, that);
}
friend bool operator==(ZoneHandleSet<T> const& lhs,
ZoneHandleSet<T> const& rhs) {
if (lhs.data_ == rhs.data_) return true;
if ((lhs.data_ & kTagMask) == kListTag &&
(rhs.data_ & kTagMask) == kListTag) {
List const* const lhs_list = lhs.list();
List const* const rhs_list = rhs.list();
if (lhs_list->length() == rhs_list->length()) {
for (int i = 0; i < lhs_list->length(); ++i) {
if (lhs_list->at(i) != rhs_list->at(i)) return false;
}
return true;
}
}
return false;
}
friend bool operator!=(ZoneHandleSet<T> const& lhs,
ZoneHandleSet<T> const& rhs) {
return !(lhs == rhs);
}
friend size_t hash_value(ZoneHandleSet<T> const& set) {
return static_cast<size_t>(set.data_);
}
private:
typedef ZoneList<T**> List;
List const* list() const {
DCHECK_EQ(kListTag, data_ & kTagMask);
return bit_cast<List const*>(data_ - kListTag);
}
T** singleton() const {
DCHECK_EQ(kSingletonTag, data_ & kTagMask);
return bit_cast<T**>(data_ - kSingletonTag);
}
enum Tag : intptr_t {
kSingletonTag = 0,
kEmptyTag = 1,
kListTag = 2,
kTagMask = 3
};
STATIC_ASSERT(kTagMask < kPointerAlignment);
intptr_t data_;
};
} // namespace internal
} // namespace v8
#endif // V8_ZONE_ZONE_HANDLE_SET_H_