Парсер данных в кольцевом буфере

206
12 апреля 2017, 15:34

Программирую STM32 на C\C++, имеется кольцевой буфер входящих данных (не важно, текстовых или бинарных). Естественно известен формат данных, которые поступают, разделители и все прочее. Буфер имеет размер много меньше, чем принимаемый пакет данных, поэтому ловить конец пакета, и только после этого парсить пакет не вариант. Необходим именно динамический парсер данных. На ум приходит следующий вариант: объявляем переменную, которая указывает этап парсинга, и в цикле while оператора switch постепенно переключаемся между этапами парсинга.

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;
  }
}

При увеличении команд парсер распухает в геометрической прогрессии.
Вопрос собственно в следующем: как грамотней организовать алгоритм?

Answer 1

Как вариант, парсить подструктуры в пакете, желательно делая их маленькими, и парьсить что пришло, а не ожидать строгий порядок данных. Важно, чтобы буфер был размером с самую большую структуру + запас на входящие данные пока вы парсите. Было бы проще, если бы был указан примерный формат пакета. Я бы делал парсер по такому подобию:

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
        }
    }
}

чтобы избавиться от распухания кода парсера нужно либо делать парсинг самых простых типов объединенных в что-то типа структур с ключами (но это дает оверхед), но зато парсеров будет меньше. Тип подструктуры можно определять по размеру(не очень, но "хитрый финт"), либо по первому байту. Таким образом код парсера будет по-проще.

Answer 2

Может так:

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);
}
READ ALSO
Как зашифровать код

Как зашифровать код

Делаю приложение под андроид на JavaScriptПриложение билджу с помощью cordova, но проблема в том, что я использую api одного сервиса и его ключ прописан...

245