简体   繁体   中英

Parsing Message with Varying Fields

I have a byte stream that represents a message in my application. There are 5 fields in the message for demonstration. The first byte in the stream indicates which message fields are present for the current stream. For instance 0x2 in the byte-0 means only the Field-1 is present for the current stream.

The mask field might have 2^5=32 different values. To parse this varying width of message, I wrote the example structure and parser below. My question is, is there any other way to parse such dynamically changing fields? If the message had 64 fields with I would have to write 64 cases, which is cumbersome.

#include <iostream>

typedef struct
{
    uint8_t iDummy0;
    int iDummy1;
}__attribute__((packed, aligned(1)))Field4;

typedef struct
{
    int iField0;
    uint8_t ui8Field1;
    short i16Field2;
    long long i64Field3;
    Field4 stField4;
}__attribute__((packed, aligned(1)))MessageStream;

char* constructIncomingMessage()
{
    char* cpStream = new char(1+sizeof(MessageStream)); // Demonstrative message byte array
                                                            // 1 byte for Mask, 20 bytes for messageStream

    cpStream[0] = 0x1F; // the 0-th byte is a mask marking
                                // which fields are present for the messageStream
                                // all 5 fields are present for the example
    return cpStream;
}

void deleteMessage( char* cpMessage)
{
    delete cpMessage;
}

int main() {
    MessageStream messageStream; // Local storage for messageStream
    uint8_t ui8FieldMask; // Mask to indicate which fields of messageStream
                            // are present for the current incoming message
    const uint8_t ui8BitIsolator = 0x01;
    uint8_t ui8FieldPresent; // ANDed result of Mask and Isolator

    std::size_t szParsedByteCount = 0; // Total number of parsed bytes

    const std::size_t szMaxMessageFieldCount = 5; // There can be maximum 5 fields in
                                                    // the messageStream

    char* cpMessageStream = constructIncomingMessage();
    ui8FieldMask = (uint8_t)cpMessageStream[0];
    szParsedByteCount += 1;

    for(std::size_t i = 0; i<szMaxMessageFieldCount; ++i)
    {
        ui8FieldPresent = ui8FieldMask & ui8BitIsolator;

        if(ui8FieldPresent)
        {
            switch(i)
            {
                case 0:
                {
                    memcpy(&messageStream.iField0, cpMessageStream+szParsedByteCount, sizeof(messageStream.iField0));
                    szParsedByteCount += sizeof(messageStream.iField0);
                    break;
                }
                case 1:
                {
                    memcpy(&messageStream.ui8Field1, cpMessageStream+szParsedByteCount, sizeof(messageStream.ui8Field1));
                    szParsedByteCount += sizeof(messageStream.ui8Field1);
                    break;
                }
                case 2:
                {
                    memcpy(&messageStream.i16Field2, cpMessageStream+szParsedByteCount, sizeof(messageStream.i16Field2));
                    szParsedByteCount += sizeof(messageStream.i16Field2);
                    break;
                }
                case 3:
                {
                    memcpy(&messageStream.i64Field3, cpMessageStream+szParsedByteCount, sizeof(messageStream.i64Field3));
                    szParsedByteCount += sizeof(messageStream.i64Field3);
                    break;
                }
                case 4:
                {
                    memcpy(&messageStream.stField4, cpMessageStream+szParsedByteCount, sizeof(messageStream.stField4));
                    szParsedByteCount += sizeof(messageStream.stField4);
                    break;
                }
                default:
                {
                    std::cerr << "Undefined Message field number: " << i << '\n';
                    break;
                }
            }
        }
        ui8FieldMask >>= 1; // shift the mask
    }

    delete deleteMessage(cpMessageStream);
    return 0;
}

The first thing I'd change is to drop the __attribute__((packed, aligned(1))) on Field4. This is a hack to create structures which mirror a packed wire-format, but that's not the format you're dealing with anyway.

Next, I'd make MessageStream a std::tuple of std::optional<T> fields.

You now know that there are std::tuple_size<MessageStream> possible bits in the mask. Obviously you can't fit 64 bits in a ui8FieldMask but I'll assume that's a trivial problem to solve.

You can write a for-loop from 0 to std::tuple_size<MessageStream> to extract the bits from ui8FieldMask to see which bits are set. The slight problem with that logic is that you'll need compile-time constants I for std::get<size_t I>(MessageStream) , and a for-loop only gives you run-time variables.

Hence, you'll need a recursive template <size_t I> extract(char const*& cpMessageStream, MessageStream&) , and of course a specialization extract<0> . In extract<I> , you can use typename std::tuple_element<I, MessageStream>::type to get the std::optional<T> at the I'th position in your MessageStream .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM