|  | // 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 |