2018-07-12 16:29:32 +02:00
#ifndef FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_
#define FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_
2020-08-13 20:53:35 +02:00
#include "ArrayList.h"
#include "../globalfunctions/CRC.h"
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../serialize/SerialArrayListAdapter.h"
2018-07-12 16:29:32 +02:00
#include <cmath>
template < typename T >
class Index : public SerializeIF {
2021-01-28 11:50:33 +01:00
/**
* 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 < SerializeIF , T >:: value , "Wrong Type for Index, Type must implement SerializeIF" );
2018-07-12 16:29:32 +02:00
public :
2021-01-28 11:50:33 +01:00
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 != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
result = indexType . serialize ( buffer , size , maxSize , streamEndianness );
if ( result != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
result = SerializeAdapter :: serialize ( & this -> size , buffer , size , maxSize , streamEndianness );
if ( result != HasReturnvaluesIF :: RETURN_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 != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
result = indexType . deSerialize ( buffer , size , streamEndianness );
if ( result != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
result = SerializeAdapter :: deSerialize ( & this -> size , buffer , size , streamEndianness );
if ( result != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
result = SerializeAdapter :: deSerialize ( & this -> storedPackets , buffer , size , streamEndianness );
if ( result != HasReturnvaluesIF :: RETURN_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 < T >& other ){
return (( blockStartAddress == other . getBlockStartAddress ()) && ( size == other . getSize ())) && ( indexType == * ( other . getIndexType ()));
}
2018-07-12 16:29:32 +02:00
private :
2021-01-28 11:50:33 +01:00
uint32_t blockStartAddress ;
uint32_t size ;
uint32_t storedPackets ;
T indexType ;
2018-07-12 16:29:32 +02:00
};
template < typename T >
class IndexedRingMemoryArray : public SerializeIF , public ArrayList < Index < T > , uint32_t > {
2021-01-28 11:50:33 +01:00
/**
* 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
*/
2018-07-12 16:29:32 +02:00
public :
2021-01-28 11:50:33 +01:00
IndexedRingMemoryArray ( uint32_t startAddress , uint32_t size , uint32_t bytesPerBlock , SerializeIF * additionalInfo ,
bool overwriteOld ) : ArrayList < Index < T > , 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 < double > ( size ) / static_cast < double > ( 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 < T > 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 < double > ( totalSize ) / static_cast < double > ( bytesPerBlock ));
//allocate memory now
this -> entries = new Index < T > [ 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 < T >:: 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 < T >:: Iterator it = this -> begin (); it != this -> end (); ++ it ){
it -> setSize ( 0 );
it -> setStoredPackets ( 0 );
( * typeResetFnc )( it -> modifyIndexType ());
}
}
void resetBlock ( typename IndexedRingMemoryArray < T >:: Iterator it , void ( * typeResetFnc )( T * )){
it -> setSize ( 0 );
it -> setStoredPackets ( 0 );
( * typeResetFnc )( it -> modifyIndexType ());
}
/*
* Reading
*/
void setCurrentReadBlock ( typename IndexedRingMemoryArray < T >:: 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 < T >:: 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 < T >:: 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 < T >:: Iterator getNextNonEmptyBlock () const {
for ( typename IndexedRingMemoryArray < T >:: 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 < T >:: 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 < T >:: 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 < T >:: 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 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 != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
result = SerializeAdapter :: serialize ( & this -> size , buffer , size , maxSize , streamEndianness );
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 ,
maxSize , streamEndianness );
++ i ;
}
if ( result != HasReturnvaluesIF :: RETURN_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 = HasReturnvaluesIF :: RETURN_OK ;
if ( additionalInfo != NULL ){
result = additionalInfo -> deSerialize ( buffer , size , streamEndianness );
}
if ( result != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
Index < T > tempIndex ;
result = tempIndex . deSerialize ( buffer , size , streamEndianness );
if ( result != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
uint32_t tempSize = 0 ;
result = SerializeAdapter :: deSerialize ( & tempSize , buffer , size , streamEndianness );
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 ,
streamEndianness );
++ i ;
}
if ( result != HasReturnvaluesIF :: RETURN_OK ){
return result ;
}
typename IndexedRingMemoryArray < T >:: Iterator cmp ( & tempIndex );
for ( typename IndexedRingMemoryArray < T >:: 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 < T >:: 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 < T >:: 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 < T >:: Iterator it = this -> begin (); it != this -> end (); ++ it ){
filledSize += it -> getSize ();
}
return ( double ) filledSize / ( double ) this -> totalSize ;
}
typename IndexedRingMemoryArray < T >:: 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 < T >:: Iterator getNextWrite () const {
typename IndexedRingMemoryArray < T >:: 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 < T >:: Iterator getPreviousBlock ( typename IndexedRingMemoryArray < T >:: Iterator it ) {
if ( this -> begin () == it ){
typename IndexedRingMemoryArray < T >:: Iterator next (( this -> back ()));
return next ;
}
typename IndexedRingMemoryArray < T >:: Iterator next ( it );
-- next ;
return next ;
}
2018-07-12 16:29:32 +02:00
private :
2021-01-28 11:50:33 +01:00
//The total size used by the blocks (without index)
uint32_t totalSize ;
2018-07-12 16:29:32 +02:00
2021-01-28 11:50:33 +01:00
//The address of the index
const uint32_t indexAddress ;
2018-07-12 16:29:32 +02:00
2021-01-28 11:50:33 +01:00
//The iterators for writing and reading
typename IndexedRingMemoryArray < T >:: Iterator currentWriteBlock ;
typename IndexedRingMemoryArray < T >:: Iterator currentReadBlock ;
2018-07-12 16:29:32 +02:00
2021-01-28 11:50:33 +01:00
//How much of the current read block is read already
uint32_t currentReadSize ;
2018-07-12 16:29:32 +02:00
2021-01-28 11:50:33 +01:00
//Cached Size of current read block
uint32_t currentReadBlockSizeCached ;
2018-07-12 16:29:32 +02:00
2021-01-28 11:50:33 +01:00
//Last block of current write (should be write block)
typename IndexedRingMemoryArray < T >:: Iterator lastBlockToRead ;
//current size of last Block to read
uint32_t lastBlockToReadSize ;
2018-07-12 16:29:32 +02:00
2021-01-28 11:50:33 +01:00
//Additional Info to be serialized with the index
SerializeIF * additionalInfo ;
2018-07-12 16:29:32 +02:00
2021-01-28 11:50:33 +01:00
//Does it overwrite old blocks?
const bool overwriteOld ;
2018-07-12 16:29:32 +02:00
};
#endif /* FRAMEWORK_CONTAINER_INDEXEDRINGMEMORY_H_ */