与ragel一起使用二进制变长协议的fgoto

时间:2017-12-17 07:25:52

标签: ragel

我尝试将解析器编写为简单的二进制协议

  • 0个或更多帧外字节
  • 使用STX(0x02)
  • 开始帧
  • Messaje长度的两个长度字节。
  • 一个命令字节。
  • 0个或更多数据字节。
  • 一个校验字节。

ragel脚本

static int len;
static unsigned char command;
static int indexx;
static unsigned char data[0x10000];
static unsigned char checksum;
%%{

  machine themachine;

  action out_of_frame_action  { }
  action start_of_frame_action {}
  action first_byte_len_action { len = fc *256; }
  action second_byte_len_action { len += fc; }
  action command_action { command = fc; indexx=0; len--;}
  action data_action { 
            if(!len) 
                fgoto check; 
            else
            {
                data[indexx++];
                len --;
            }
  }
  action checksum_action { checksum = fc; }

  stx = 0x02;
  first_byte_len = any;
  second_byte_len = any;
  command = any;
  data = any*;
  checksum = any;

  main :=  (^stx* $ out_of_frame_action) 
           (stx > start_of_frame_action) 
       (first_byte_len > first_byte_len_action) 
       (second_byte_len > second_byte_len_action) 
       (command > command_action) 
       (data $ data_action) ;

 check :=   (checksum % checksum_action)  ;

}%%

machine state

但结果机器不会跳转到检查(5)状态,因此不会执行checksum_action并且不会返回第一个状态继续解析。

有什么问题?

关注@Roman recomendations我发布了一个完整的例子。这个例子不起作用,因为得到错误的校验和。

#include <stdint.h>
#include <stdio.h>

static int len;
static unsigned char command;

static int indexx;
static unsigned char data[0x10000];
static unsigned char checksum;

%%{

  machine themachine;

  stx = 0x02;

  action out_of_frame_action
  {
      printf("OOF %02X\n",fc);
  }

  action start_of_frame_action
  {
      printf("STX ");
  }

  action first_byte_len_action
  {
      printf("fbl[%d] ",(int)(fc));
      len = 256*((unsigned char)fc);
  }

  action second_byte_len_action
  {
      len += (unsigned char )fc ;
      printf("sbl[%d] Len=%d ",(int)(fc),len);
      indexx=0;
      len-=2; // Checksum and command are included on message len
  }

  action command_action
  {
      command = fc;
      printf("CMM=%02X ", command);
  }

  action check_len
  {
      len > 0
  }

  action data_action
  {
      data[indexx++]=fc;
      printf("[%02X]",(unsigned char) fc);
      len--;
  }

  action checksum_action
  {
      checksum = fc;
      printf(" Chk=%02X \n",checksum);
  }


  first_byte_len = any ;
  second_byte_len = any;
  command = any;
  data = any*;
  checksum = any;


  check = (checksum % checksum_action);

  main := ((^stx* $ out_of_frame_action)
           (stx > start_of_frame_action)
           (first_byte_len > first_byte_len_action)
           (second_byte_len > second_byte_len_action)
           (command > command_action)
           (data when check_len $ data_action)
           check
          )**;

}%%

%% write data;

int main(void)
{
    uint8_t buf[] = {
        0x00,                                    // OOF 00
        0x00,                                    // OOF 00
        0x02,0x00,0x03,0x20,0x01,0x21,           // STX fbl[0] sbl[3] Len = 3 CMM=20 [01] Chk=21
        0x00,                                    // OOF 00
        0x00,                                    // OOF 00
        0x02,0x00,0x05,0x81,0x01,0x02,0x03,0x87, // STX fbl[0] sbl[5] Len = 5 CMM=81 [01][02][03] Chk=87
        0x02,0x00,0x03,0x03,0x01,0x04,           // STX fbl[0] sbl[3] Len = 3 CMM=03 [01] Chk=04
        0x02,0x00,0x05,0x07,0x01,0x02,0x03,0x0D  // STX fbl[0] sbl[5] Len = 5 CMM=07 [01][02][03] Chk=0D
    };

    int cs;
    uint8_t *p, *pe, *eof;


    p = buf;
    eof = pe = buf + sizeof(buf)/sizeof(uint8_t);

    %% write init;
    %% write exec;

}

我改变了

check = (checksum % checksum_action);

通过

check = (checksum > checksum_action);

但不是解决方案。

2 个答案:

答案 0 :(得分:0)

  1. 如果您为我们提供了样本输入和完整的来源以验证整个程序,那将是非常好的
  2. 我不确定为什么你的goto不起作用,但我可能根本不使用fgoto来简化事情。这里没有必要,你可以使用语义条件(when
  3. 您的机器没有返回初始状态,因为没有任何内容告诉它。
  4. 所以我对原始代码进行了一些小改动:

    --- main.c.orig 2017-12-17 11:48:49.369200291 +0300
    +++ main.c      2017-12-20 22:51:11.096283379 +0300
    @@ -12,14 +12,12 @@
            action first_byte_len_action { len = fc *256; }
            action second_byte_len_action { len += fc; }
            action command_action { command = fc; indexx=0; len--;}
    +       action check_len {
    +               len
    +       }
            action data_action { 
    -               if(!len) 
    -                       fgoto check; 
    -               else
    -               {
    -                       data[indexx++];
    -                       len --;
    -               }
    +               data[indexx++];
    +               len--;
            }
            action checksum_action { checksum = fc; }
    
    @@ -30,13 +28,13 @@
            data = any*;
            checksum = any;
    
    -main :=  (^stx* $ out_of_frame_action) 
    +       check = (checksum % checksum_action);
    +main :=  ((^stx* $ out_of_frame_action) 
                    (stx > start_of_frame_action) 
                    (first_byte_len > first_byte_len_action) 
                    (second_byte_len > second_byte_len_action) 
                    (command > command_action) 
    -               (data $ data_action) ;
    +         (data when check_len $ data_action) check)**;
    
    -check :=   (checksum % checksum_action)  ;
    
     }%%
    

    注意when如何使用data简化其操作,并注意主要中最长匹配的kleene星型运算符**,指示它在成功后循环回到开头帧解析。这让我们看到了这个漂亮的图表,它可能更接近你想要的东西:

    Fixed state machine

答案 1 :(得分:0)

在您的第一个示例中,fgoto会使用校验和数据,以便以后在您需要校验和时不可用。您可以使用fhold来防止它被使用。 checksum_action也应该返回主页(并使用$转换而不是&#39;%&#39;这只会在EOF时发生。 )。

action data_action { 
          if(!len) { 
              fhold;
              fgoto check; 
          } else {
              data[indexx++] = fc;
              len --;
          }
}

action checksum_action { checksum = fc; fnext main; }

(或者您可以将校验和逻辑放在data_action代码中)。