blob: 5f08aa4693bb650c12f0825a9be9681d1bf115f0 [file] [log] [blame] [edit]
// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "vm/object_id_ring.h"
#include "platform/assert.h"
#include "vm/dart_api_state.h"
#include "vm/json_stream.h"
namespace dart {
#ifndef PRODUCT
ObjectIdRing::~ObjectIdRing() {
ASSERT(table_ != nullptr);
free(table_);
table_ = nullptr;
}
void ObjectIdRing::Invalidate() {
serial_num_ = 0;
wrapped_ = false;
for (int32_t i = 0; i < capacity_; i++) {
table_[i] = Object::null();
}
}
int32_t ObjectIdRing::GetIdForObject(ObjectPtr object, IdPolicy policy) {
// We do not allow inserting |Object::null()| because we use it as a
// placeholder for unpopulated slots in |table_|.
ASSERT(object != Object::null());
if (policy == kAllocateId) {
return AllocateNewId(object);
}
ASSERT(policy == kReuseId);
int32_t id = FindExistingIdForObject(object);
if (id != kInvalidId) {
// Return a previous id for |object|.
return id;
}
return AllocateNewId(object);
}
int32_t ObjectIdRing::FindExistingIdForObject(ObjectPtr raw_obj) {
for (int32_t i = 0; i < capacity_; i++) {
if (table_[i] == raw_obj) {
return IdOfIndex(i);
}
}
return kInvalidId;
}
ObjectPtr ObjectIdRing::GetObjectForId(int32_t id, LookupResult* kind) {
int32_t index = IndexOfId(id);
if (index == kInvalidId) {
*kind = kExpired;
return Object::null();
}
ASSERT(index >= 0);
ASSERT(index < capacity_);
// The `index == kInvalidId` check above should make it impossible for
// `table_[index]` to be |Object::null()|.
ASSERT(table_[index] != Object::null());
*kind = kValid;
ASSERT(IdOfIndex(index) == id);
return table_[index];
}
void ObjectIdRing::VisitPointers(ObjectPointerVisitor* visitor) const {
ASSERT(table_ != nullptr);
visitor->VisitPointers(table_, capacity_);
}
void ObjectIdRing::PrintJSON(JSONStream* js) {
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
ASSERT(zone != nullptr);
JSONObject jsobj(js);
jsobj.AddProperty("type", "_IdZone");
jsobj.AddProperty("name", "default");
{
JSONArray objects(&jsobj, "objects");
Object& obj = Object::Handle();
for (int32_t i = 0; i < capacity_; i++) {
obj = table_[i];
if (obj.IsNull()) {
// Collected object.
continue;
}
objects.AddValue(obj, false);
}
}
}
ObjectIdRing::ObjectIdRing(int32_t capacity) {
serial_num_ = 0;
wrapped_ = false;
table_ = nullptr;
SetCapacityAndMaxSerial(capacity, kMaxId);
}
void ObjectIdRing::SetCapacityAndMaxSerial(int32_t capacity,
int32_t max_serial) {
ASSERT(capacity > 0);
ASSERT(max_serial <= kMaxId);
capacity_ = capacity;
if (table_ != nullptr) {
free(table_);
}
table_ = reinterpret_cast<ObjectPtr*>(calloc(capacity_, kWordSize));
for (int32_t i = 0; i < capacity_; i++) {
table_[i] = Object::null();
}
// The maximum serial number is a multiple of the capacity, so that when
// the serial number wraps, the index into table_ wraps with it.
max_serial_ = max_serial - (max_serial % capacity_);
}
int32_t ObjectIdRing::NextSerial() {
int32_t r = serial_num_;
serial_num_++;
if (serial_num_ >= max_serial_) {
serial_num_ = 0;
wrapped_ = true;
}
return r;
}
int32_t ObjectIdRing::AllocateNewId(ObjectPtr raw_obj) {
ASSERT(raw_obj->IsHeapObject());
int32_t id = NextSerial();
ASSERT(id != kInvalidId);
int32_t index = IndexOfId(id);
ASSERT(index != kInvalidId);
table_[index] = raw_obj;
return id;
}
int32_t ObjectIdRing::IndexOfId(int32_t id) {
if (!IsValidId(id)) {
return kInvalidId;
}
ASSERT((id >= 0) && (id < max_serial_));
return id % capacity_;
}
int32_t ObjectIdRing::IdOfIndex(int32_t index) {
if (index < 0) {
return kInvalidId;
}
if (index >= capacity_) {
return kInvalidId;
}
int32_t id = kInvalidId;
if (wrapped_) {
// Serial numbers have wrapped around 0.
ASSERT(serial_num_ < capacity_);
if (index < serial_num_) {
// index < serial_num_ have been handed out and are sequential starting
// at 0.
id = index;
} else {
// the other end of the array has the high ids.
const int32_t bottom = max_serial_ - capacity_;
id = bottom + index;
}
} else if (index < serial_num_) {
// Index into the array where id range splits.
int32_t split_point = serial_num_ % capacity_;
if (index < split_point) {
// index < split_point has serial_numbers starting at
// serial_num_ - split_point.
int bottom = serial_num_ - split_point;
ASSERT(bottom >= 0);
id = bottom + index;
} else {
// index >= split_point has serial_numbers starting at
// serial_num_ - split_point - capacity_.
int bottom = serial_num_ - capacity_ - split_point;
ASSERT(bottom >= 0);
id = bottom + index;
}
}
ASSERT(!IsValidId(id) || (IndexOfId(id) == index));
return id;
}
bool ObjectIdRing::IsValidContiguous(int32_t id) const {
ASSERT(id != kInvalidId);
ASSERT((id >= 0) && (id < max_serial_));
if (id >= serial_num_) {
// Too large.
return false;
}
int32_t bottom = 0;
if (serial_num_ >= capacity_) {
bottom = serial_num_ - capacity_;
}
return id >= bottom;
}
bool ObjectIdRing::IsValidId(int32_t id) {
if (id == kInvalidId) {
return false;
}
if (id < 0) {
return false;
}
if (id >= max_serial_) {
return false;
}
ASSERT((id >= 0) && (id < max_serial_));
if (wrapped_) {
// Serial number has wrapped around to 0.
if (serial_num_ >= capacity_) {
// Serial number is larger than capacity, the serial
// numbers are contiguous again.
wrapped_ = false;
return IsValidContiguous(id);
} else {
// When the serial number first wraps, the valid serial number range
// spans two intervals:
// #1 [0, serial_num_)
// #2 [max_serial_ - (capacity_ - serial_num), max_serial_)
//
// Check for both.
if (id < serial_num_) {
// Interval #1
return true;
}
// Interval #2
const int32_t max_serial_num = max_serial_;
const int32_t bottom = max_serial_num - (capacity_ - serial_num_);
return id >= bottom && bottom < max_serial_num;
}
}
ASSERT(wrapped_ == false);
return IsValidContiguous(id);
}
#endif // !PRODUCT
} // namespace dart