我正在尝试为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的一个特性:
- 实现编译时
sizeof
和bitsizeof
操作符(#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
进行某种形式的提前预测来决定接下来的内容,但最好避免使用它,除非您确实需要它。