Ragel: избегайте избыточного вызова функции предложения "когда"

Я пишу машину Ragel для довольно простого двоичного протокола, и то, что я представляю здесь, является еще более упрощенной версией, без какого-либо восстановления после ошибок, просто чтобы продемонстрировать проблему, которую я пытаюсь решить.

Итак, сообщение для анализа здесь выглядит так:

<1 byte: length> <$length bytes: user data> <1 byte: checksum>

Машина выглядит следующим образом:

%%{
   machine my_machine;
   write data;
   alphtype unsigned char;
}%%

%%{
   action message_reset {
      /* TODO */
      data_received = 0;
   }

   action got_len {
      len = fc;
   }

   action got_data_byte {
      /* TODO */
   }

   action message_received {
      /* TODO */
   }

   action is_waiting_for_data {
      (data_received++ < len);
   }

   action is_checksum_correct {
      1/*TODO*/
   }


   len = (any);
   fmt_separate_len = (0x80 any);
   data = (any);
   checksum = (any);

   message = 
      (
         # first byte: length of the data
         (len                                   @got_len)
         # user data
         (data       when is_waiting_for_data   @got_data_byte )*
         # place higher priority on the previous machine (i.e. data)
         <: 
         # last byte: checksum
         (checksum   when is_checksum_correct   @message_received)
      ) >to(message_reset)
      ;

   main := (msg_start: message)*;

   # Initialize and execute.
   write init;
   write exec;
}%%

Как видите, сначала мы получаем 1 байт, который представляет длину; тогда мы получаем data байтов, пока мы не получим необходимое количество байтов (проверка выполняется is_waiting_for_data), и когда мы получаем следующий (дополнительный) байт, мы проверяем, является ли это правильной контрольной суммой (по is_checksum_correct). Если это так, машина будет ждать следующего сообщения; в противном случае эта конкретная машина зависает (я специально не включил здесь восстановление после ошибок, чтобы упростить диаграмму).

Диаграмма выглядит так:

$ ragel -Vp ./msg.rl | dot -Tpng -o msg.png

Нажмите, чтобы увидеть изображение

Как видите, в состоянии 1, пока мы получаем пользовательские данные, выполняются следующие условия:

0..255(is_waiting_for_data, !is_checksum_correct),
0..255(is_waiting_for_data, is_checksum_correct)

Таким образом, на каждый байт данных он вызывает избыточно is_checksum_correctхотя результат не имеет значения вообще.

Условие должно быть максимально простым: 0..255(is_waiting_for_data)

Как этого добиться?

1 ответ

Как is_checksum_correct должен работать? when условие происходит до того, как контрольная сумма прочитана, в соответствии с тем, что вы опубликовали. Мое предложение было бы проверить контрольную сумму внутри message_received и обработать любую ошибку там. Таким образом, вы можете избавиться от второго when и проблема больше не будет существовать.

Похоже, что семантические условия являются относительно новой функцией в Ragel, и, хотя они выглядят действительно полезными, возможно, они еще недостаточно зрелы, если вам нужен оптимальный код.

Другие вопросы по тегам