优化已知长度的任何数据的Ragel语义条件

时间:2018-02-14 16:23:05

标签: ragel

Ragel manual 6.5语义条件中有一个例子,演示了如何使用 when 子句为可变大小的结构编写语法。

action rec_num { i = 0; n = getnumber(); }
action test_len { i++ < n }
data_fields = (
’d’
[0-9]+ %rec_num
’:’
( [a-z] when test_len )*
)**;

它适用于小型结构,但是对于较大的结构,它会减慢速度,因为解析器会尝试评估每个角色的条件。

我想要做的是跳过扫描,只是将数据复制到缓冲区,这样的语法(注意任何* ):

action rec_num { i = 0; n = getnumber(); }
action test_len { i++ < n }
data_fields = (
’d’
[0-9]+ %rec_num
’:’
( any* when test_len )*
)**;

所以我想直接复制长度为 n 的缓冲区而不进行迭代。如何在不离开解析器上下文的情况下执行此操作?

1 个答案:

答案 0 :(得分:1)

你可能需要自己动手。 ragel用户指南提到您可以更改机器中的fpc / p变量,因此这应该足够安全。这假设您在一个块中处理所有数据(即,数据字段不会被分解)

machine foo;

action rec_num { i = 0; n = getnumber(); }
action test_len { i++ < n }
action buffer_data_field {
  /* p is still pointing to ':' at this point. */
  if (p + 1 + n >= pe) { fgoto *foo_error; }
  buffer(p + 1, n);
  p += n;
}
action buffer_data_field_eof {
  /* check for eof while data was expected */
  /* p is pointing past the ':' at this point */
  if (n) { fgoto *foo_error; }
}

data_fields = (
'd'
[0-9]+ %rec_num
':' 
$buffer_data_field
$eof(buffer_data_field_eof)

)**;

如果数据被分块,您可以将缓冲分开:

buffer_data :=
    any+
    $eof{ fnext *foo_error; }
    ${
        size_t avail = pe - p;
        if (avail >= n) {
            buffer(p, n);
            p += n - 1;
            fnext data_fields;
        } else {
            buffer_partial(p, avail);
            n -= avail;
            p += avail - 1;
        }
    }
    ;


data_fields = (
    'd'
    [0-9]+ %rec_num
    ':'  ${ if (n) fnext buffer_data; }
    )**;