CHECKSUM Layer
This layer is responsible to calculate and validate the checksum information.
During read operation it remembers the initial value of the read iterator,
then invokes the read operation of the next (wrapped) layer. After the
next layer reports successful completion of its read operation, the expected checksum
value is read. Then, the real checksum on the read data bytes is calculated and
compered to the expected one. If the values match, the read operation is
reported as successfully complete. If not, the created message object is
deleted and error reported.
During write operation it lets the next (wrapped) layer to finish its
writing, calculates the checksum value on the written
data bytes, and writes the result into output buffer.
Before jumping into writing the code, there is a need to be aware of couple of issues:
The generic code of the CHECKSUM Layer mustn't depend on any particular
checksum calculation algorithm. I'd recommend providing the calculator class as a template
parameter,
operator()of which is responsible to implement the checksumcalculation logic.
The checksum calculation after write operation requires the iterator to
go back and calculate the checksum on the written data bytes. It can easily
be done when used iterator is
one. Sometimes it may not
be the case (for example the output data is written into
std::vector using
There is a need to have a generic way to handle such cases.
Implementing Read
Let's start with implementing the read first.
It is clear that TCalc class is expected to have operator() member function, which receives the iterator for reading and number of bytes in the buffer.
As an example let's implement generic total sum of bytes calculator:
Implementing Write
Now, let's tackle the write problem. As it was mentioned earlier, there is a need to recognise the type of the iterator used for writing and behave accordingly. If the iterator is properly defined, the std::iterator_traits class will define iterator_category internal type.
For random access iterators the WriteIteratorCategoryTag class will be either std::random_access_iterator_tag or any other class that inherits from it. Using this information, we can use Tag Dispatch Idiom to choose the right writing functionality.
Please pay attention, that writeOutput() function above is unable to properly calculate the checksum of the written data. There is no way the iterator can be reversed back and used as input instead of output. As the result the function returns special error status: ErrorStatus::UpdateRequired. It is an indication that the write operation is not complete and the output should be updated using random access iterator.
Implementing Update
Please note, that every other layer must also implement the update() member function, which will just advance the provided iterator by the number of bytes required to write its field and invoke update() member function of the next (wrapped) layer.
And so on for the rest of the layers. Also note, that the code above will work, only when the field has the same serialisation length for any value. If this is not the case (Base-128 encoding is used), the previously written value needs to be read, instead of just advancing the iterator, to make sure the iterator is advanced right amount of bytes:
The variable serialisation length encoding will be forced using some kind of special option. It can be identified at compile time and Tag Dispatch Idiom can be used to select appropriate update functionality.
The caller, that requests protocol stack to serialise a message, must check the error status value returned by the write() operation. If it is ErrorStatus::UpdateRequired, the caller must create random-access iterator to the already written buffer and invoke update() function with it, to make sure the written information is correct.
Last updated
Was this helpful?