首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >凯泰结构:有没有办法使整个身体类型依赖于第一个字节的存在/类型?

凯泰结构:有没有办法使整个身体类型依赖于第一个字节的存在/类型?
EN

Stack Overflow用户
提问于 2022-04-16 23:33:01
回答 1查看 108关注 0票数 0

我试图为Postgres协议V3编写一个Katai定义:

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

所以我需要说“对象可以是这两种类型中的一种”,但是我不知道该如何做.

大多数消息的布局如下:

代码语言:javascript
复制
|-----------------------------------------------|
| Type  | Length | (Rest of payload)
|-----------------------------------------------|
| Char  | Int32  |  Bytes
|-----------------------------------------------|

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

代码语言:javascript
复制
 |-----------------------------------------------|
 | Length | Protocol Version | (Rest of payload)
 |-----------------------------------------------|
 |  Int32 |      Int32       |  Bytes
 |-----------------------------------------------|

到目前为止,我尝试过这样的方法:

代码语言:javascript
复制
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

但不幸的是,这似乎行不通。

有什么办法在凯泰对此进行编码吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-04-20 19:33:02

会员免责声明:我是凯泰结构的维护者(见我的GitHub配置文件)。

纵观PostgreSQL文档StartupMessage似乎只能是“第一条消息”:

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

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

代码语言:javascript
复制
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文档中,这个字段被描述为“消息内容的长度(以字节为单位),包括自我长度”)。虚拟sizeof运算符是一个0.9特征

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

valid也是在0.9中引入的,目前还没有适当的文档(对不起),但是您可以在#435中阅读它的描述。

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

代码语言:javascript
复制
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 (有意)没有回溯,它被设计用来处理不含糊的二进制格式(参见https://stackoverflow.com/a/55111070/12940655)。虽然可以使用instances进行某种展望以决定接下来的内容,但最好避免它,除非您真的需要它。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71898221

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档