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