Circular buffer data parser

I program STM32 to C\C++, there is a ring buffer of incoming data (whether text or binary). Naturally, you know the format of the data that comes in, the separators, and everything else. The buffer is much smaller than the received data packet, so catching the end of the packet and only then parsing the packet is not an option. What is needed is a dynamic data parser. The following option comes to mind: declare a variable that specifies the parsing stage, and in the loop while of the switch statement, we gradually switch between the parsing stages.

uint8_t Stage;
bool PacketProcess;

PacketProcess = false;
while (!PacketProcess)
{
  switch (Stage)
  {
    case 0: // ожидаем появления в буфере команды
      if (в буфере есть команда)
      {
        принимаем команду;
        Stage = 1;
      }
      break;
    case 1: // ожидаем появления в буфере разделителя (например запятая)
      if (в буфере есть разделитель)
      {
        считываем разделитель;
        Stage = 2;
      }
      break;
    case 2: // ожидаем появления в буфере данных 1
      if (в буфере есть данные 1)
      {
        принимаем данные 1;
        Stage = 3;
      }
      break;
    case 3: // ожидаем появления в буфере разделителя (например запятая)
      if (в буфере есть разделитель)
      {
        считываем разделитель;
        Stage = 4;
      }
    case 4: // ожидаем появления в буфере данных 2
      if (в буфере есть данные 2)
      {
        принимаем данные 2;
        Stage = 5;
      }
      break;
    case 5: // ожидаем появления в буфере конца пакета
      if (в буфере есть конец пакета)
      {
        считываем конец пакета;
        Stage = 0;
        PacketProcess = true;
      }
      break;
  }
}

As the commands increase, the parser swells exponentially.
The question is actually as follows: how to organize the algorithm more correctly?

Author: kizoso, 2017-04-11

2 answers

Alternatively, parse the substructures in the package, preferably making them small, and parse what came, rather than expecting a strict data order. It is important that the buffer is the size of the largest structure + margin for incoming data while you parse. It would be easier if the sample package format was specified. I would make a parser like this:

enum structureMarker {part_one, part_two, part_three, etc};

pasePartOne(ptr* begin, ptr* end, dataStructOne& data)
{
    // ... some parsing logic
}


while(hasdata())
{
    if(isSubDataDelimeter(pDataHead))
    {
        switch(getMarkerType(pParsedTail+1))
        {
            case part_one: pasePartOne(pParsedTail+1, pDataHead, fullstruct.one); break;
            case part_two: ... /// etc
            ... /// etc
        }
    }
}

To get rid of the bloat of the parser code, you either need to parse the simplest types of structures combined into something like structures with keys (but this gives an overhead), but there will be fewer parsers. The type of substructure can be determined by the size(not very, but a "clever feint"), or by the first byte. This way, the parser code will be simpler.

 1
Author: Артем Колосов, 2017-04-11 10:59:43

Maybe so:

struct element el;

while (1) {
    GetElement(&el);
    if ( TypOfElement(&el) ==  END_OF_PKT) break;
    if ( TypOfElement(&el) ==  DELIMITER) continue;
    if ( TypOfElement(&el) ==  DATA) PackData(&el);
}
 0
Author: Sergey, 2017-04-11 10:00:48