#ifndef FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_ #define FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_ #include #include #include #include #include #include template class Index: public SerializeIF{ /** * Index is the Type used for the list of indices. The template parameter is the type which describes the index, it needs to be a child of SerializeIF to be able to make it persistent */ static_assert(std::is_base_of::value,"Wrong Type for Index, Type must implement SerializeIF"); public: Index():blockStartAddress(0),size(0),storedPackets(0){} Index(uint32_t startAddress):blockStartAddress(startAddress),size(0),storedPackets(0){ } void setBlockStartAddress(uint32_t newAddress){ this->blockStartAddress = newAddress; } uint32_t getBlockStartAddress() const { return blockStartAddress; } const T* getIndexType() const { return &indexType; } T* modifyIndexType(){ return &indexType; } /** * Updates the index Type. Uses = operator * @param indexType Type to copy from */ void setIndexType(T* indexType) { this->indexType = *indexType; } uint32_t getSize() const { return size; } void setSize(uint32_t size) { this->size = size; } void addSize(uint32_t size){ this->size += size; } void setStoredPackets(uint32_t newStoredPackets){ this->storedPackets = newStoredPackets; } void addStoredPackets(uint32_t packets){ this->storedPackets += packets; } uint32_t getStoredPackets() const{ return this->storedPackets; } ReturnValue_t serialize(uint8_t** buffer, uint32_t* size, const uint32_t max_size, bool bigEndian) const { ReturnValue_t result = AutoSerializeAdapter::serialize(&blockStartAddress,buffer,size,max_size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } result = indexType.serialize(buffer,size,max_size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } result = AutoSerializeAdapter::serialize(&this->size,buffer,size,max_size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } result = AutoSerializeAdapter::serialize(&this->storedPackets,buffer,size,max_size,bigEndian); return result; } ReturnValue_t deSerialize(const uint8_t** buffer, int32_t* size, bool bigEndian){ ReturnValue_t result = AutoSerializeAdapter::deSerialize(&blockStartAddress,buffer,size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } result = indexType.deSerialize(buffer,size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } result = AutoSerializeAdapter::deSerialize(&this->size,buffer,size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } result = AutoSerializeAdapter::deSerialize(&this->storedPackets,buffer,size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } return result; } uint32_t getSerializedSize() const { uint32_t size = AutoSerializeAdapter::getSerializedSize(&blockStartAddress); size += indexType.getSerializedSize(); size += AutoSerializeAdapter::getSerializedSize(&this->size); size += AutoSerializeAdapter::getSerializedSize(&this->storedPackets); return size; } bool operator==(const Index& other){ return ((blockStartAddress == other.getBlockStartAddress()) && (size==other.getSize())) && (indexType == *(other.getIndexType())); } private: uint32_t blockStartAddress; uint32_t size; uint32_t storedPackets; T indexType; }; template class IndexedRingMemoryArray: public SerializeIF, public ArrayList, uint32_t>{ /** * Indexed Ring Memory Array is a class for a ring memory with indices. It assumes that the newest data comes in last * It uses the currentWriteBlock as pointer to the current writing position * The currentReadBlock must be set manually */ public: IndexedRingMemoryArray(uint32_t startAddress, uint32_t size, uint32_t bytesPerBlock, SerializeIF* additionalInfo, bool overwriteOld) :ArrayList,uint32_t>(NULL,(uint32_t)10,(uint32_t)0),totalSize(size),indexAddress(startAddress),currentReadSize(0),currentReadBlockSizeCached(0),lastBlockToReadSize(0), additionalInfo(additionalInfo),overwriteOld(overwriteOld){ //Calculate the maximum number of indices needed for this blocksize uint32_t maxNrOfIndices = floor(static_cast(size)/static_cast(bytesPerBlock)); //Calculate the Size needeed for the index itself uint32_t serializedSize = 0; if(additionalInfo!=NULL){ serializedSize += additionalInfo->getSerializedSize(); } //Size of current iterator type Index tempIndex; serializedSize += tempIndex.getSerializedSize(); //Add Size of Array serializedSize += sizeof(uint32_t); //size of array serializedSize += (tempIndex.getSerializedSize() * maxNrOfIndices); //size of elements serializedSize += sizeof(uint16_t); //size of crc //Calculate new size after index if(serializedSize > totalSize){ error << "IndexedRingMemory: Store is too small for index" << std::endl; } uint32_t useableSize = totalSize - serializedSize; //Update the totalSize for calculations totalSize = useableSize; //True StartAddress uint32_t trueStartAddress = indexAddress + serializedSize; //Calculate True number of Blocks and reset size of true Number of Blocks uint32_t trueNumberOfBlocks = floor(static_cast(totalSize) / static_cast(bytesPerBlock)); //allocate memory now this->entries = new Index[trueNumberOfBlocks]; this->size = trueNumberOfBlocks; this->maxSize_ = trueNumberOfBlocks; this->allocated = true; //Check trueNumberOfBlocks if(trueNumberOfBlocks<1){ error << "IndexedRingMemory: Invalid Number of Blocks: " << trueNumberOfBlocks; } //Fill address into index uint32_t address = trueStartAddress; for (typename IndexedRingMemoryArray::Iterator it = this->begin();it!=this->end();++it) { it->setBlockStartAddress(address); it->setSize(0); it->setStoredPackets(0); address += bytesPerBlock; } //Initialize iterators currentWriteBlock = this->begin(); currentReadBlock = this->begin(); lastBlockToRead = this->begin(); //Check last blockSize uint32_t lastBlockSize = (trueStartAddress + useableSize) - (this->back()->getBlockStartAddress()); if((lastBlockSizesize > 1)){ //remove the last Block so the second last block has more size this->size -= 1; debug << "IndexedRingMemory: Last Block is smaller than bytesPerBlock, removed last block" << std::endl; } } /** * Resets the whole index, the iterators and executes the given reset function on every index type * @param typeResetFnc static reset function which accepts a pointer to the index Type */ void reset(void (*typeResetFnc)(T*)){ currentReadBlock = this->begin(); currentWriteBlock = this->begin(); lastBlockToRead = this->begin(); currentReadSize = 0; currentReadBlockSizeCached = 0; lastBlockToReadSize = 0; for(typename IndexedRingMemoryArray::Iterator it = this->begin();it!=this->end();++it){ it->setSize(0); it->setStoredPackets(0); (*typeResetFnc)(it->modifyIndexType()); } } void resetBlock(typename IndexedRingMemoryArray::Iterator it,void (*typeResetFnc)(T*)){ it->setSize(0); it->setStoredPackets(0); (*typeResetFnc)(it->modifyIndexType()); } /* * Reading */ void setCurrentReadBlock(typename IndexedRingMemoryArray::Iterator it){ currentReadBlock = it; currentReadBlockSizeCached = it->getSize(); } void resetRead(){ currentReadBlock = this->begin(); currentReadSize = 0; currentReadBlockSizeCached = this->begin()->getSize(); lastBlockToRead = currentWriteBlock; lastBlockToReadSize = currentWriteBlock->getSize(); } /** * Sets the last block to read to this iterator. * Can be used to dump until block x * @param it The iterator for the last read block */ void setLastBlockToRead(typename IndexedRingMemoryArray::Iterator it){ lastBlockToRead = it; lastBlockToReadSize = it->getSize(); } /** * Set the read pointer to the first written Block, which is the first non empty block in front of the write block * Can be the currentWriteBlock as well */ void readOldest(){ resetRead(); currentReadBlock = getNextNonEmptyBlock(); currentReadBlockSizeCached = currentReadBlock->getSize(); } /** * Sets the current read iterator to the next Block and resets the current read size * The current size of the block will be cached to avoid race condition between write and read * If the end of the ring is reached the read pointer will be set to the begin */ void readNext(){ currentReadSize = 0; if((this->size != 0) && (currentReadBlock.value ==this->back())){ currentReadBlock = this->begin(); }else{ currentReadBlock++; } currentReadBlockSizeCached = currentReadBlock->getSize(); } /** * Returns the address which is currently read from * @return Address to read from */ uint32_t getCurrentReadAddress() const { return getAddressOfCurrentReadBlock() + currentReadSize; } /** * Adds readSize to the current size and checks if the read has no more data left and advances the read block * @param readSize The size that was read * @return Returns true if the read can go on */ bool addReadSize(uint32_t readSize) { if(currentReadBlock == lastBlockToRead){ //The current read block is the last to read if((currentReadSize+readSize) return true currentReadSize += readSize; return true; }else{ //Reached end of read -> return false currentReadSize = lastBlockToReadSize; return false; } }else{ //We are not in the last Block if((currentReadSize + readSize)::Iterator it(currentReadBlock); //Search if any block between this and the last block is not empty for(;it!=lastBlockToRead;++it){ if(it == this->end()){ //This is the end, next block is the begin it = this->begin(); if(it == lastBlockToRead){ //Break if the begin is the lastBlockToRead break; } } if(it->getSize()!=0){ //This is a non empty block. Go on reading with this block currentReadBlock = it; currentReadBlockSizeCached = it->getSize(); return true; } } //reached lastBlockToRead and every block was empty, check if the last block is also empty if(lastBlockToReadSize!=0){ //go on with last Block currentReadBlock = lastBlockToRead; currentReadBlockSizeCached = lastBlockToReadSize; return true; } //There is no non empty block left return false; } //Size is larger than 0 return true; } } } uint32_t getRemainigSizeOfCurrentReadBlock() const{ if(currentReadBlock == lastBlockToRead){ return (lastBlockToReadSize - currentReadSize); }else{ return (currentReadBlockSizeCached - currentReadSize); } } uint32_t getAddressOfCurrentReadBlock() const { return currentReadBlock->getBlockStartAddress(); } /** * Gets the next non empty Block after the current write block, * @return Returns the iterator to the block. If there is non, the current write block is returned */ typename IndexedRingMemoryArray::Iterator getNextNonEmptyBlock() const { for(typename IndexedRingMemoryArray::Iterator it = getNextWrite();it!=currentWriteBlock;++it){ if(it == this->end()){ it = this->begin(); if(it == currentWriteBlock){ break; } } if(it->getSize()!=0){ return it; } } return currentWriteBlock; } /** * Returns a copy of the oldest Index type * @return Type of Index */ T* getOldest(){ return (getNextNonEmptyBlock()->modifyIndexType()); } /* * Writing */ uint32_t getAddressOfCurrentWriteBlock() const{ return currentWriteBlock->getBlockStartAddress(); } uint32_t getSizeOfCurrentWriteBlock() const{ return currentWriteBlock->getSize(); } uint32_t getCurrentWriteAddress() const{ return getAddressOfCurrentWriteBlock() + getSizeOfCurrentWriteBlock(); } void clearCurrentWriteBlock(){ currentWriteBlock->setSize(0); currentWriteBlock->setStoredPackets(0); } void addCurrentWriteBlock(uint32_t size, uint32_t storedPackets){ currentWriteBlock->addSize(size); currentWriteBlock->addStoredPackets(storedPackets); } T* modifyCurrentWriteBlockIndexType(){ return currentWriteBlock->modifyIndexType(); } void updatePreviousWriteSize(uint32_t size, uint32_t storedPackets){ typename IndexedRingMemoryArray::Iterator it = getPreviousBlock(currentWriteBlock); it->addSize(size); it->addStoredPackets(storedPackets); } /** * Checks if the block has enough space for sizeToWrite * @param sizeToWrite The data to be written in the Block * @return Returns true if size to write is smaller the remaining size of the block */ bool hasCurrentWriteBlockEnoughSpace(uint32_t sizeToWrite){ typename IndexedRingMemoryArray::Iterator next = getNextWrite(); uint32_t addressOfNextBlock = next->getBlockStartAddress(); uint32_t availableSize = ((addressOfNextBlock+totalSize) - (getAddressOfCurrentWriteBlock()+getSizeOfCurrentWriteBlock()))%totalSize; return (sizeToWrite < availableSize); } /** * Checks if the store is full if overwrite old is false * @return Returns true if it is writeable and false if not */ bool isNextBlockWritable(){ //First check if this is the end of the list typename IndexedRingMemoryArray::Iterator next; next = getNextWrite(); if((next->getSize()!=0) && (!overwriteOld)){ return false; } return true; } /** * Updates current write Block Index Type * @param infoOfNewBlock */ void updateCurrentBlock(T* infoOfNewBlock){ currentWriteBlock->setIndexType(infoOfNewBlock); } /** * Succeed to next block, returns FAILED if overwrite is false and the store is full * @return */ ReturnValue_t writeNext(){ //Check Next Block if(!isNextBlockWritable()){ //The Index is full and does not overwrite old return HasReturnvaluesIF::RETURN_FAILED; } //Next block can be written, update Metadata currentWriteBlock = getNextWrite(); currentWriteBlock->setSize(0); currentWriteBlock->setStoredPackets(0); return HasReturnvaluesIF::RETURN_OK; } /** * Serializes the Index and calculates the CRC. * Parameters according to HasSerializeIF * @param buffer * @param size * @param max_size * @param bigEndian * @return */ ReturnValue_t serialize(uint8_t** buffer, uint32_t* size, const uint32_t max_size, bool bigEndian) const{ uint8_t* crcBuffer = *buffer; uint32_t oldSize = *size; if(additionalInfo!=NULL){ additionalInfo->serialize(buffer,size,max_size,bigEndian); } ReturnValue_t result = currentWriteBlock->serialize(buffer,size,max_size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } result = AutoSerializeAdapter::serialize(&this->size,buffer,size,max_size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } uint32_t i = 0; while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->size)) { result = SerializeAdapter >::serialize(&this->entries[i], buffer, size, max_size, bigEndian); ++i; } if(result != HasReturnvaluesIF::RETURN_OK){ return result; } uint16_t crc = Calculate_CRC(crcBuffer,(*size-oldSize)); result = AutoSerializeAdapter::serialize(&crc,buffer,size,max_size,bigEndian); return result; } /** * Get the serialized Size of the index * @return The serialized size of the index */ uint32_t getSerializedSize() const { uint32_t size = 0; if(additionalInfo!=NULL){ size += additionalInfo->getSerializedSize(); } size += currentWriteBlock->getSerializedSize(); size += AutoSerializeAdapter::getSerializedSize(&this->size); size += (this->entries[0].getSerializedSize()) * this->size; uint16_t crc = 0; size += AutoSerializeAdapter::getSerializedSize(&crc); return size; } /** * DeSerialize the Indexed Ring from a buffer, deSerializes the current write iterator * CRC Has to be checked before! * @param buffer * @param size * @param bigEndian * @return */ ReturnValue_t deSerialize(const uint8_t** buffer, int32_t* size, bool bigEndian){ ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; if(additionalInfo!=NULL){ result = additionalInfo->deSerialize(buffer,size,bigEndian); } if(result != HasReturnvaluesIF::RETURN_OK){ return result; } Index tempIndex; result = tempIndex.deSerialize(buffer,size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } uint32_t tempSize = 0; result = AutoSerializeAdapter::deSerialize(&tempSize,buffer,size,bigEndian); if(result != HasReturnvaluesIF::RETURN_OK){ return result; } if(this->size != tempSize){ return HasReturnvaluesIF::RETURN_FAILED; } uint32_t i = 0; while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->size)) { result = SerializeAdapter >::deSerialize( &this->entries[i], buffer, size, bigEndian); ++i; } if(result != HasReturnvaluesIF::RETURN_OK){ return result; } typename IndexedRingMemoryArray::Iterator cmp(&tempIndex); for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ if(*(cmp.value) == *(it.value)){ currentWriteBlock = it; return HasReturnvaluesIF::RETURN_OK; } } //Reached if current write block iterator is not found return HasReturnvaluesIF::RETURN_FAILED; } uint32_t getIndexAddress() const { return indexAddress; } /* * Statistics */ uint32_t getStoredPackets() const { uint32_t size = 0; for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ size += it->getStoredPackets(); } return size; } uint32_t getTotalSize() const { return totalSize; } uint32_t getCurrentSize() const{ uint32_t size = 0; for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ size += it->getSize(); } return size; } bool isEmpty() const{ return getCurrentSize()==0; } double getPercentageFilled() const{ uint32_t filledSize = 0; for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ filledSize += it->getSize(); } return (double)filledSize/(double)this->totalSize; } typename IndexedRingMemoryArray::Iterator getCurrentWriteBlock() const{ return currentWriteBlock; } /** * Get the next block of the currentWriteBlock. * Returns the first one if currentWriteBlock is the last one * @return Iterator pointing to the next block after currentWriteBlock */ typename IndexedRingMemoryArray::Iterator getNextWrite() const{ typename IndexedRingMemoryArray::Iterator next(currentWriteBlock); if((this->size != 0) && (currentWriteBlock.value == this->back())){ next = this->begin(); }else{ ++next; } return next; } /** * Get the block in front of the Iterator * Returns the last block if it is the first block * @param it iterator which you want the previous block from * @return pointing to the block before it */ typename IndexedRingMemoryArray::Iterator getPreviousBlock(typename IndexedRingMemoryArray::Iterator it) { if(this->begin() == it){ typename IndexedRingMemoryArray::Iterator next((this->back())); return next; } typename IndexedRingMemoryArray::Iterator next(it); --next; return next; } private: //The total size used by the blocks (without index) uint32_t totalSize; //The address of the index const uint32_t indexAddress; //The iterators for writing and reading typename IndexedRingMemoryArray::Iterator currentWriteBlock; typename IndexedRingMemoryArray::Iterator currentReadBlock; //How much of the current read block is read already uint32_t currentReadSize; //Cached Size of current read block uint32_t currentReadBlockSizeCached; //Last block of current write (should be write block) typename IndexedRingMemoryArray::Iterator lastBlockToRead; //current size of last Block to read uint32_t lastBlockToReadSize; //Additional Info to be serialized with the index SerializeIF* additionalInfo; //Does it overwrite old blocks? const bool overwriteOld; }; #endif /* FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_ */