如何用 flex 和 bison 解析非常大的缓冲区

How to parse very large buffer with flex and bison

我编写了一个 json 库,它使用 flex 和 bison 来解析序列化的 json(即字符串)——并将它们反序列化为 json 对象。它适用于小字符串。

但是,它无法处理非常大的字符串(我尝试了几乎 3 GB 的字符串)并出现此错误:

‘fatal flex scanner internal error--end of buffer missed’

我想知道我可以传递给这个函数的缓冲区的最大大小是多少:

//js: serialized json stored in std::string 

yy_scan_bytes(js.data(), js.size()); 

以及如何使 flex/bison 使用大缓冲区?

在我看来,您使用的是旧版本的 flex 骨架(因此也是 flex),其中假定字符串长度适合 ints。您观察到的错误消息可能是 int 溢出为负值的结果。

我相信如果你切换到 2.5.37 或更新的版本,你会发现大部分 int 已经变成 size_t 并且你调用 [= 应该没有问题14=] 具有大小超过 2 GB 的输入缓冲区。 (例如,该函数的原型现在采用 size_t 而不是 int。)

但是,我很难相信这样做是个好主意。首先,yy_scan_bytes 复制整个字符串 ,因为词法扫描器想要一个允许它修改的字符串,并且因为它想确保自己该字符串具有 two NUL 字节结尾。制作该副本会不必要地占用大量内存,如果您无论如何都要复制缓冲区,您最好将其复制为可管理的部分(例如 64Kib 甚至 1MiB)。只有在以下情况下才会证明有问题你有比块大小大得多的单个标记,因为 flex 绝对没有针对大的单个标记进行优化。但对于所有正常用例,它可能会好得多。

Flex 不提供将巨大的输入缓冲区拆分为块的接口,但您可以通过重新定义 YY_INPUT 宏来轻松完成此操作。 (如果这样做,您可能最终会使用 yyin 作为指向您自己的缓冲区结构的指针,这在理论上是不可移植的。但是,它可以在任何 Posix 体系结构上工作,其中所有对象指针具有相同的表示。)

当然,您通常不想等到内存中积累了 3GB 的数据才开始解析它。您可以在读取数据时逐步解析。 (您可能仍需要重新定义 YY_INPUT,具体取决于您读取数据的方式。)