199 lines
4.5 KiB
C++
199 lines
4.5 KiB
C++
// Parallax Static Timing Analyzer
|
|
// Copyright (c) 2019, Parallax Software, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// No part of this document may be copied, transmitted or
|
|
// disclosed in any form or fashion without the express
|
|
// written consent of Parallax Software, Inc.
|
|
|
|
#ifndef STA_OBJECT_TABLE_H
|
|
#define STA_OBJECT_TABLE_H
|
|
|
|
#include "Vector.hh"
|
|
#include "Error.hh"
|
|
#include "ObjectId.hh"
|
|
|
|
namespace sta {
|
|
|
|
template <class OBJECT>
|
|
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 TYPE>
|
|
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 ObjectId idx_bits = 7;
|
|
static constexpr ObjectId block_object_count = (1 << idx_bits);
|
|
|
|
private:
|
|
void makeBlock();
|
|
void freePush(TYPE *object,
|
|
ObjectId id);
|
|
|
|
size_t size_;
|
|
// Object ID of next free object.
|
|
ObjectId free_;
|
|
Vector<TableBlock<TYPE>*> blocks_;
|
|
static constexpr ObjectId idx_mask_ = block_object_count - 1;
|
|
};
|
|
|
|
template <class TYPE>
|
|
ObjectTable<TYPE>::ObjectTable() :
|
|
size_(0),
|
|
free_(object_id_null)
|
|
{
|
|
}
|
|
|
|
template <class TYPE>
|
|
ObjectTable<TYPE>::~ObjectTable()
|
|
{
|
|
blocks_.deleteContents();
|
|
}
|
|
|
|
template <class TYPE>
|
|
TYPE *
|
|
ObjectTable<TYPE>::make()
|
|
{
|
|
if (free_ == object_id_null)
|
|
makeBlock();
|
|
TYPE *object = pointer(free_);
|
|
ObjectIdx idx = free_ & idx_mask_;
|
|
object->setObjectIdx(idx);
|
|
ObjectId *free_next = reinterpret_cast<ObjectId*>(object);
|
|
free_ = *free_next;
|
|
size_++;
|
|
return object;
|
|
}
|
|
|
|
template <class TYPE>
|
|
void
|
|
ObjectTable<TYPE>::freePush(TYPE *object,
|
|
ObjectId id)
|
|
{
|
|
// Link free objects into a list linked by Object ID.
|
|
ObjectId *free_next = reinterpret_cast<ObjectId*>(object);
|
|
*free_next = free_;
|
|
free_ = id;
|
|
}
|
|
|
|
template <class TYPE>
|
|
void
|
|
ObjectTable<TYPE>::makeBlock()
|
|
{
|
|
BlockIdx block_index = blocks_.size();
|
|
TableBlock<TYPE> *block = new TableBlock<TYPE>(block_index, this);
|
|
blocks_.push_back(block);
|
|
// 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 <class TYPE>
|
|
TYPE *
|
|
ObjectTable<TYPE>::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 <class TYPE>
|
|
TYPE &
|
|
ObjectTable<TYPE>::ref(ObjectId id) const
|
|
{
|
|
if (id == object_id_null)
|
|
internalError("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 <class TYPE>
|
|
ObjectId
|
|
ObjectTable<TYPE>::objectId(const TYPE *object)
|
|
{
|
|
ObjectIdx idx = object->objectIdx();
|
|
const TableBlock<TYPE> *blk =
|
|
reinterpret_cast<const TableBlock<TYPE>*>(object - idx);
|
|
return (blk->index() << idx_bits) + idx;
|
|
}
|
|
|
|
template <class TYPE>
|
|
void
|
|
ObjectTable<TYPE>::destroy(TYPE *object)
|
|
{
|
|
ObjectId object_id = objectId(object);
|
|
object->~TYPE();
|
|
size_--;
|
|
freePush(object, object_id);
|
|
}
|
|
|
|
template <class TYPE>
|
|
void
|
|
ObjectTable<TYPE>::clear()
|
|
{
|
|
blocks_.deleteContentsClear();
|
|
size_ = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
template <class TYPE>
|
|
class TableBlock
|
|
{
|
|
public:
|
|
TableBlock(BlockIdx block_idx,
|
|
ObjectTable<TYPE> *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<TYPE>::block_object_count];
|
|
BlockIdx block_idx_;
|
|
ObjectTable<TYPE> *table_;
|
|
};
|
|
|
|
template <class TYPE>
|
|
TableBlock<TYPE>::TableBlock(BlockIdx block_idx,
|
|
ObjectTable<TYPE> *table) :
|
|
block_idx_(block_idx),
|
|
table_(table)
|
|
{
|
|
}
|
|
|
|
} // Namespace
|
|
#endif
|