// 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_ARRAY_TABLE_H #define STA_ARRAY_TABLE_H #include // memcpy #include "Vector.hh" #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); // 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_; static constexpr ObjectId idx_mask_ = block_size - 1; }; template ArrayTable::ArrayTable() : size_(0), blocks_size_(0), blocks_capacity_(1024), blocks_(new ArrayBlock*[blocks_capacity_]), prev_blocks_(nullptr), free_block_idx_(block_idx_null), free_idx_(object_idx_null) { } template ArrayTable::~ArrayTable() { deleteBlocks(); delete [] blocks_; delete [] prev_blocks_; } template void ArrayTable::deleteBlocks() { for (int i = 0; i < blocks_size_; i++) delete blocks_[i]; } template void ArrayTable::make(uint32_t count, TYPE *&array, ObjectId &id) { 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 = (count > block_size) ? count : block_size; 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) internalError("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 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) internalError("null ObjectId reference is undefined."); else { 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; } //////////////////////////////////////////////////////////////// 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 #endif