// OpenSTA, Static Timing Analyzer
// Copyright (c) 2024, 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 .
#pragma once
#include // memcpy
#include
#include "ObjectId.hh"
#include "Error.hh"
namespace sta {
template
class ArrayBlock;
// Array tables allocate arrays of objects in blocks and use 32 bit IDs to
// reference the array. Paging performance is improved by allocating
// blocks instead of individual arrays, and object sizes are reduced
// by using 32 bit references instead of 64 bit pointers.
// They are similar to ObjectTables but do not support delete/destroy or
// reclaiming deleted arrays.
template
class ArrayTable
{
public:
ArrayTable();
~ArrayTable();
void make(uint32_t count,
TYPE *&array,
ObjectId &id);
void destroy(ObjectId id,
uint32_t count);
// Grow as necessary and return pointer for id.
TYPE *ensureId(ObjectId id);
TYPE *pointer(ObjectId id) const;
TYPE &ref(ObjectId id) const;
size_t size() const { return size_; }
void clear();
static constexpr int idx_bits = 7;
static constexpr int block_size = (1 << idx_bits);
static constexpr int block_id_max = 1 << (object_id_bits - idx_bits);
private:
ArrayBlock *makeBlock(uint32_t size);
void pushBlock(ArrayBlock *block);
void deleteBlocks();
size_t size_;
// Block index of free block (blocks_[size - 1]).
BlockIdx free_block_idx_;
// Index of next free object in free_block_idx_.
ObjectIdx free_idx_;
// Don't use std::vector so growing blocks_ can be thread safe.
size_t blocks_size_;
size_t blocks_capacity_;
ArrayBlock* *blocks_;
ArrayBlock* *prev_blocks_;
// Linked list of free arrays indexed by array size.
std::vector free_list_;
static constexpr ObjectId idx_mask_ = block_size - 1;
};
template
ArrayTable::ArrayTable() :
size_(0),
free_block_idx_(block_idx_null),
free_idx_(object_idx_null),
blocks_size_(0),
blocks_capacity_(1024),
blocks_(new ArrayBlock*[blocks_capacity_]),
prev_blocks_(nullptr)
{
}
template
ArrayTable::~ArrayTable()
{
deleteBlocks();
delete [] blocks_;
delete [] prev_blocks_;
}
template
void
ArrayTable::deleteBlocks()
{
for (size_t i = 0; i < blocks_size_; i++)
delete blocks_[i];
}
template
void
ArrayTable::make(uint32_t count,
TYPE *&array,
ObjectId &id)
{
// Check the free list for a previously destroyed array with the right size.
if (count < free_list_.size()
&& free_list_[count] != object_id_null) {
id = free_list_[count];
array = pointer(id);
ObjectId *head = reinterpret_cast(array);
free_list_[count] = *head;
}
else {
ArrayBlock *block = blocks_size_ ? blocks_[free_block_idx_] : nullptr;
if ((free_idx_ == object_idx_null
&& free_block_idx_ == block_idx_null)
|| free_idx_ + count >= block->size()) {
uint32_t size = block_size;
if (blocks_size_ == 0
// First block starts at idx 1.
&& count > block_size - 1)
size = count + 1;
else if (count > block_size)
size = count;
block = makeBlock(size);
}
// makeId(free_block_idx_, idx_bits)
id = (free_block_idx_ << idx_bits) + free_idx_;
array = block->pointer(free_idx_);
free_idx_ += count;
}
size_ += count;
}
template
ArrayBlock *
ArrayTable::makeBlock(uint32_t size)
{
BlockIdx block_idx = blocks_size_;
ArrayBlock *block = new ArrayBlock(size);
pushBlock(block);
free_block_idx_ = block_idx;
// ObjectId zero is reserved for object_id_null.
free_idx_ = (block_idx > 0) ? 0 : 1;
return block;
}
template
void
ArrayTable::pushBlock(ArrayBlock *block)
{
blocks_[blocks_size_++] = block;
if (blocks_size_ >= block_id_max)
criticalError(223, "max array table block count exceeded.");
if (blocks_size_ == blocks_capacity_) {
size_t new_capacity = blocks_capacity_ * 1.5;
ArrayBlock** new_blocks = new ArrayBlock*[new_capacity];
memcpy(new_blocks, blocks_, blocks_capacity_ * sizeof(ArrayBlock*));
if (prev_blocks_)
delete [] prev_blocks_;
// Preserve block array for other threads to reference.
prev_blocks_ = blocks_;
blocks_ = new_blocks;
blocks_capacity_ = new_capacity;
}
}
template
void
ArrayTable::destroy(ObjectId id,
uint32_t count)
{
if (count >= free_list_.size())
free_list_.resize(count + 1);
TYPE *array = pointer(id);
// Prepend id to the free list.
ObjectId *head = reinterpret_cast(array);
*head = free_list_[count];
free_list_[count] = id;
size_ -= count;
}
template
TYPE *
ArrayTable::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 *
ArrayTable::ensureId(ObjectId id)
{
BlockIdx blk_idx = id >> idx_bits;
ObjectIdx obj_idx = id & idx_mask_;
// Make enough blocks for blk_idx to be valid.
for (BlockIdx i = blocks_size_; i <= blk_idx; i++) {
ArrayBlock *block = new ArrayBlock(block_size);
pushBlock(block);
}
return blocks_[blk_idx]->pointer(obj_idx);
}
template
TYPE &
ArrayTable::ref(ObjectId id) const
{
if (id == object_id_null)
criticalError(222, "null ObjectId reference is undefined.");
BlockIdx blk_idx = id >> idx_bits;
ObjectIdx obj_idx = id & idx_mask_;
return blocks_[blk_idx]->ref(obj_idx);
}
template
void
ArrayTable::clear()
{
deleteBlocks();
blocks_size_ = 0;
size_ = 0;
free_block_idx_ = block_idx_null;
free_idx_ = object_idx_null;
free_list_.clear();
}
////////////////////////////////////////////////////////////////
template
class ArrayBlock
{
public:
ArrayBlock(uint32_t size);
~ArrayBlock();
uint32_t size() const { return size_; }
TYPE &ref(ObjectIdx idx) { return objects_[idx]; }
TYPE *pointer(ObjectIdx idx) { return &objects_[idx]; }
private:
uint32_t size_;
TYPE *objects_;
};
template
ArrayBlock::ArrayBlock(uint32_t size) :
size_(size),
objects_(new TYPE[size])
{
}
template
ArrayBlock::~ArrayBlock()
{
delete [] objects_;
}
} // Namespace