#ifndef FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_ #define FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_ #include #include "../globalfunctions/CRC.h" #include "../returnvalues/returnvalue.h" #include "../serialize/SerialArrayListAdapter.h" #include "../serviceinterface/ServiceInterfaceStream.h" #include "ArrayList.h" 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, size_t* size, size_t maxSize, Endianness streamEndianness) const { ReturnValue_t result = SerializeAdapter::serialize(&blockStartAddress, buffer, size, maxSize, streamEndianness); if (result != returnvalue::OK) { return result; } result = indexType.serialize(buffer, size, maxSize, streamEndianness); if (result != returnvalue::OK) { return result; } result = SerializeAdapter::serialize(&this->size, buffer, size, maxSize, streamEndianness); if (result != returnvalue::OK) { return result; } result = SerializeAdapter::serialize(&this->storedPackets, buffer, size, maxSize, streamEndianness); return result; } ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, Endianness streamEndianness) { ReturnValue_t result = SerializeAdapter::deSerialize(&blockStartAddress, buffer, size, streamEndianness); if (result != returnvalue::OK) { return result; } result = indexType.deSerialize(buffer, size, streamEndianness); if (result != returnvalue::OK) { return result; } result = SerializeAdapter::deSerialize(&this->size, buffer, size, streamEndianness); if (result != returnvalue::OK) { return result; } result = SerializeAdapter::deSerialize(&this->storedPackets, buffer, size, streamEndianness); if (result != returnvalue::OK) { return result; } return result; } size_t getSerializedSize() const { uint32_t size = SerializeAdapter::getSerializedSize(&blockStartAddress); size += indexType.getSerializedSize(); size += SerializeAdapter::getSerializedSize(&this->size); size += SerializeAdapter::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 ((lastBlockSize < bytesPerBlock) && (this->size > 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) < lastBlockToReadSize) { // the block has more data -> 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) < currentReadBlockSizeCached) { // The current Block has more data currentReadSize += readSize; return true; } else { // The current block is written completely readNext(); if (currentReadBlockSizeCached == 0) { // Next block is empty typename IndexedRingMemoryArray::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 returnvalue::FAILED; } // Next block can be written, update Metadata currentWriteBlock = getNextWrite(); currentWriteBlock->setSize(0); currentWriteBlock->setStoredPackets(0); return returnvalue::OK; } /** * Serializes the Index and calculates the CRC. * Parameters according to HasSerializeIF * @param buffer * @param size * @param maxSize * @param streamEndianness * @return */ ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize, Endianness streamEndianness) const { uint8_t* crcBuffer = *buffer; uint32_t oldSize = *size; if (additionalInfo != NULL) { additionalInfo->serialize(buffer, size, maxSize, streamEndianness); } ReturnValue_t result = currentWriteBlock->serialize(buffer, size, maxSize, streamEndianness); if (result != returnvalue::OK) { return result; } result = SerializeAdapter::serialize(&this->size, buffer, size, maxSize, streamEndianness); if (result != returnvalue::OK) { return result; } uint32_t i = 0; while ((result == returnvalue::OK) && (i < this->size)) { result = SerializeAdapter::serialize(&this->entries[i], buffer, size, maxSize, streamEndianness); ++i; } if (result != returnvalue::OK) { return result; } uint16_t crc = Calculate_CRC(crcBuffer, (*size - oldSize)); result = SerializeAdapter::serialize(&crc, buffer, size, maxSize, streamEndianness); return result; } /** * Get the serialized Size of the index * @return The serialized size of the index */ size_t getSerializedSize() const { uint32_t size = 0; if (additionalInfo != NULL) { size += additionalInfo->getSerializedSize(); } size += currentWriteBlock->getSerializedSize(); size += SerializeAdapter::getSerializedSize(&this->size); size += (this->entries[0].getSerializedSize()) * this->size; uint16_t crc = 0; size += SerializeAdapter::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 streamEndianness * @return */ ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, Endianness streamEndianness) { ReturnValue_t result = returnvalue::OK; if (additionalInfo != NULL) { result = additionalInfo->deSerialize(buffer, size, streamEndianness); } if (result != returnvalue::OK) { return result; } Index tempIndex; result = tempIndex.deSerialize(buffer, size, streamEndianness); if (result != returnvalue::OK) { return result; } uint32_t tempSize = 0; result = SerializeAdapter::deSerialize(&tempSize, buffer, size, streamEndianness); if (result != returnvalue::OK) { return result; } if (this->size != tempSize) { return returnvalue::FAILED; } uint32_t i = 0; while ((result == returnvalue::OK) && (i < this->size)) { result = SerializeAdapter::deSerialize(&this->entries[i], buffer, size, streamEndianness); ++i; } if (result != returnvalue::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 returnvalue::OK; } } // Reached if current write block iterator is not found return returnvalue::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_ */