| // 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 { | |
| if (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 |