Guide to Implementing Communication Protocols in C
  • Introduction
  • Code Generation vs C++ Library
  • Main Challenges
  • Goal
  • Audience
  • Code Examples
  • Final Outcome
  • Contribution
  • Message
    • Reading and Writing
    • Dispatching and Handling
    • Extending Interface
  • Fields
    • Automating Basic Operations
    • Working With Fields
    • Common Field Types
  • Generic Library
    • Generalising Message Interface
    • Generalising Message Implementation
    • Generalising Fields Implementation
  • Transport
    • PAYLOAD Layer
    • ID Layer
    • SIZE Layer
    • SYNC Layer
    • CHECKSUM Layer
    • Defining Protocol Stack
  • Achievements
  • Appendices
    • Appendix A - tupleForEach
    • Appendix B - tupleAccumulate
    • Appendix C - tupleForEachFromUntil
    • Appendix D - tupleForEachType
    • Appendix E - AlignedUnion
Powered by GitBook
On this page

Was this helpful?

  1. Transport

SIZE Layer

This layer is responsible to handle the remaining length information.

  • During read operation it reads the information about number of

    bytes required to complete the message deserialisation and compares

    it to the number of bytes available for reading. If input buffer has

    enough data, the read operation of the next (wrapped) layer is invoked.

  • During write operation, the layer must calculate and write the

    number of bytes required to serialise the message prior to invoking the

    write operation of the next (wrapped) layer.

namespace comms
{
// TField is type of the field used to read/write SIZE information
// TNext is the next layer this one wraps
template <typename TField, typename TNext>
class MsgSizeLayer
{
public:
    // Type of the field object used to read/write SIZE information.
    using Field = TField;

    // Take type of the ReadIterator from the next layer
    using ReadIterator = typename TNext::ReadIterator;

    // Take type of the WriteIterator from the next layer
    using WriteIterator = typename TNext::WriteIterator;

    // Take type of the message interface from the next layer
    using Message = typename TNext::Message;

    // Take type of the message interface pointer from the next layer
    using MsgPtr = typename TNext::MsgPtr; 

    template <typename TMsgPtr>
    ErrorStatus read(TMsgPtr& msgPtr, ReadIterator& iter, std::size_t len)
    {
        Field field;
        auto es = field.read(iter, len);
        if (es != ErrorStatus::Success) {
            return es;
        }
        auto actualRemainingSize = (len - field.length());
        auto requiredRemainingSize = static_cast<std::size_t>(field.value());
        if (actualRemainingSize < requiredRemainingSize) {
            return ErrorStatus::NotEnoughData;
        }
        es = reader.read(msgPtr, iter, requiredRemainingSize);
        if (es == ErrorStatus::NotEnoughData) {
            return ErrorStatus::ProtocolError;
        }
        return es;
    } 

    ErrorStatus write(const Message& msg, WriteIterator& iter, std::size_t len) const
    {
        Field field;
        field.value() = m_next.length(msg);
        auto es = field.write(iter, len);
        if (es != ErrorStatus::Success) {
            return es;
        }
        return m_next.write(msg, iter, len - field.length());
    }

private:
    TNext m_next;
};
} // namespace comms

Also note, that write() function requires knowledge of how many bytes it will take to the next layer to serialise the message. It requires every layer to define length(...) member function in addition to read() and write().

namespace comms
{
template <typename TMessage>
class MsgDataLayer
{
public:
    static constexpr std::size_t length(const TMessage& msg)
    {
        return msg.length();
    }
};
} // namespace comms
namespace comms
{
template <
    typename TField, 
    typename TAllMessages, 
    typename TNext, 
    typename... TOptions>
class MsgIdLayer
{
public:
    std::size_t length(const Message& msg) const
    {
        TField field;
        field.value() = msg.id();
        return field.length() + m_next.length(msg);
    }
};
} // namespace comms
namespace comms
{
template <typename TField, typename TNext>
class MsgSizeLayer
{
public:
    std::size_t length(const Message& msg) const
    {
        TField field;
        field.value() = m_next.length(msg);
        return field.length() + field.value();
    }
};
} // namespace comms
PreviousID LayerNextSYNC Layer

Last updated 5 years ago

Was this helpful?

Please note, that reference to the smart pointer holding the message object is passed to the read() function using undefined type (template parameter) instead of using the MsgPtr internal type. Some communication protocols may serialise SIZE information before the ID, others may do the opposite. The SIZE layer is not aware of what other layers it wraps. If ID information is serialised before the SIZE, the MsgPtr type definition is probably taken from , which is defined to be void.

The length() member function of the may be defined as:

The length() member function of the may be defined as:

And the length() member function of the itself may be defined as:

PAYLOAD Layer
PAYLOAD Layer
ID Layer
SIZE Layer