Kaitai Struct:有没有办法让整个主体类型依赖于第一个字节的存在/类型?



我正在尝试为Postgres Wire Protocol V3编写一个Katai定义:

我遇到的问题是,除了StartupMessage以外的每条消息都遵循相同的格式。StartupMessage的形状不同

所以我需要说"对象可以是这两种类型之一",但我不确定怎么做

大多数消息的布局是:

|-----------------------------------------------|
| Type  | Length | (Rest of payload)
|-----------------------------------------------|
| Char  | Int32  |  Bytes
|-----------------------------------------------|

但是对于启动消息,开头没有Type字符来标识它:

 |-----------------------------------------------|
 | Length | Protocol Version | (Rest of payload)
 |-----------------------------------------------|
 |  Int32 |      Int32       |  Bytes
 |-----------------------------------------------|

到目前为止,我已经尝试了这样做:

meta:
    id: postgres_wire_protocol_frontend_v3
    file-extension: postgres_wire_protocol_frontend_v3
    endian: be
seq:
    - id: type
      type: str
      encoding: ASCII
      size: 1
    - id: length
      type: u4
    - id: body
      size: length
      type:
          switch-on: type
          cases:
              '"B"': bind_message
              '"E"': execute_message
              '"Q"': query_message
              _: startup_message

但不幸的是,这似乎不起作用=/

有没有办法在开泰语中对其进行编码?

附属免责声明:我是Kaitai Struct的维护者(参见我的GitHub配置文件)。

查看PostgreSQL文档,似乎StartupMessage只能是"第一个消息":

消息的第一个字节标识消息类型,接下来的四个字节指定消息其余部分的长度(该长度计数包括消息本身,但不包括消息类型字节)。消息的其余内容由消息类型决定。由于历史原因,客户端发送的第一个消息(启动消息)没有初始消息类型字节。

我不知道使用Kaitai struct生成的解析器的应用程序将如何操作,所以我不确定如何以适合您的方式使用此信息。问题中的.ksy代码片段表明,您将使用解析器类PostgresWireProtocolFrontendV3的新实例处理每条消息,因此我建议仅为StartupMessage创建一个新的.ksy文件:

meta:
  id: postgres_protocol_startup_message
seq:
  - id: len_message
    type: u4
  - id: body
    size: len_message - len_message._sizeof
    type: message_body
types:
  message_body:
    seq:
      - id: version_major
        type: u2
        valid: 3
      - id: version_minor
        type: u2
        valid: 0
      # ...

注意:len_message._sizeof将在编译时被翻译为4(这是必需的,因为PostgreSQL文档中该字段描述为"以字节为单位的消息内容长度,包括self.")虚sizeof操作符是0.9的一个特性:

  • 实现编译时sizeofbitsizeof操作符(#84)
    • 基于类型:sizeof<u4>, bitsizeof<b13>, sizeof<user_type>
    • 基于值:file_header._sizeof, flags._bitsizeof (file_header, flags是当前类型定义的字段)

valid也在0.9中被引入,目前还没有合适的文档(抱歉),但是你可以在#435中阅读它的描述。

在你的应用程序代码中,你可能会知道通信的状态(即你是否正在处理第一条消息),所以我假设你会做这样的事情:

message_raw = b'...'  # TODO: receive from the socket (probably)
if is_first_message:
    startup_message = PostgresProtocolStartupMessage(KaitaiStream(BytesIO(message_raw)))
    # ...
    is_first_message = False
else:
    message = PostgresWireProtocolFrontendV3(KaitaiStream(BytesIO(message_raw)))

当然,我不知道你的用例,所以我只是猜测它可能是什么,但希望至少其中一些对你有用。


所以我需要以某种方式说&;对象可以是这两种类型之一&;,但我不确定如何做。

这不是Kaitai Struct的工作方式。Kaitai Struct(故意)没有回溯,它被设计用来处理非二义性的二进制格式(参见https://stackoverflow.com/a/55111070/12940655)。虽然可以使用instances进行某种形式的提前预测来决定接下来的内容,但最好避免使用它,除非您确实需要它。

相关内容

最新更新