// OpenSTA, Static Timing Analyzer // Copyright (c) 2025, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. // // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // This notice may not be removed or altered from any source distribution. #pragma once #include "Vector.hh" #include "Error.hh" #include "ObjectId.hh" namespace sta { template class TableBlock; // Object tables allocate objects in blocks and use 32 bit IDs to // reference an object. Paging performance is improved by allocating // blocks instead of individual objects, and object sizes are reduced // by using 32 bit references instead of 64 bit pointers. // // Class TYPE must define member functions // ObjectIdx objectIdx() const // void setObjectIdx(ObjectIdx idx) // to get/set the index of the object in a block, which can be a bit // field ObjectTable::idx_bits (7 bits) wide. template class ObjectTable { public: ObjectTable(); ~ObjectTable(); TYPE *make(); void destroy(TYPE *object); TYPE *pointer(ObjectId id) const; TYPE &ref(ObjectId id) const; ObjectId objectId(const TYPE *object); size_t size() const { return size_; } void clear(); // Objects are allocated in blocks of 128. static constexpr int idx_bits = 7; static constexpr int block_object_count = (1 << idx_bits); static constexpr int block_id_max = 1 << (object_id_bits - idx_bits); private: void makeBlock(); void freePush(TYPE *object, ObjectId id); size_t size_; // Object ID of next free object. ObjectId free_; Vector*> blocks_; static constexpr ObjectId idx_mask_ = block_object_count - 1; }; template ObjectTable::ObjectTable() : size_(0), free_(object_id_null) { } template ObjectTable::~ObjectTable() { blocks_.deleteContents(); } template TYPE * ObjectTable::make() { if (free_ == object_id_null) makeBlock(); TYPE *object = pointer(free_); ObjectIdx idx = free_ & idx_mask_; object->setObjectIdx(idx); ObjectId *free_next = reinterpret_cast(object); free_ = *free_next; size_++; return object; } template void ObjectTable::freePush(TYPE *object, ObjectId id) { // Link free objects into a list linked by Object ID. ObjectId *free_next = reinterpret_cast(object); *free_next = free_; free_ = id; } template void ObjectTable::makeBlock() { BlockIdx block_index = blocks_.size(); TableBlock *block = new TableBlock(block_index, this); blocks_.push_back(block); if (blocks_.size() >= block_id_max) criticalError(224, "max object table block count exceeded."); // ObjectId zero is reserved for object_id_null. int last = (block_index > 0) ? 0 : 1; for (int i = block_object_count - 1; i >= last; i--) { TYPE *obj = block->pointer(i); ObjectId id = (block_index << idx_bits) + i; freePush(obj, id); } } template TYPE * ObjectTable::pointer(ObjectId id) const { if (id == object_id_null) return nullptr; else { BlockIdx blk_idx = id >> idx_bits; ObjectIdx obj_idx = id & idx_mask_; return blocks_[blk_idx]->pointer(obj_idx); } } template TYPE & ObjectTable::ref(ObjectId id) const { if (id == object_id_null) criticalError(225, "null ObjectId reference is undefined."); else { BlockIdx blk_idx = id >> idx_bits; ObjectIdx obj_idx = id & idx_mask_; return blocks_[blk_idx]->ptr(obj_idx); } } template ObjectId ObjectTable::objectId(const TYPE *object) { ObjectIdx idx = object->objectIdx(); const TableBlock *blk = reinterpret_cast*>(object - idx); return (blk->index() << idx_bits) + idx; } template void ObjectTable::destroy(TYPE *object) { ObjectId object_id = objectId(object); size_--; freePush(object, object_id); } template void ObjectTable::clear() { blocks_.deleteContentsClear(); size_ = 0; } //////////////////////////////////////////////////////////////// template class TableBlock { public: TableBlock(BlockIdx block_idx, ObjectTable *table); BlockIdx index() const { return block_idx_; } TYPE &ref(ObjectIdx idx) { return objects_[idx]; } TYPE *pointer(ObjectIdx idx) { return &objects_[idx]; } private: TYPE objects_[ObjectTable::block_object_count]; BlockIdx block_idx_; ObjectTable *table_; }; template TableBlock::TableBlock(BlockIdx block_idx, ObjectTable *table) : block_idx_(block_idx), table_(table) { } } // Namespace