Kaitai Struct:有什么方法可以使整个主体类型依赖于第一个字节的presence/type?
Kaitai Struct: Any way to make entire body type dependent on presence/type of first byte?
我正在尝试为 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 中对其进行编码吗?
附属免责声明:我是 Kaitai Struct maintainer (see my GitHub profile)。
查看 PostgreSQL docs,似乎 StartupMessage
只能是“第一条消息”:
The first byte of a message identifies the message type, and the next four bytes specify the length of the rest of the message (this length count includes itself, but not the message-type byte). The remaining contents of the message are determined by the message type. For historical reasons, the very first message sent by the client (the startup message) has no initial message-type byte.
我不知道您使用 Kaitai Struct-generated 解析器的应用程序将如何运行,因此我不确定如何以适合您的方式使用此信息。您问题中的 .ksy
片段表明您将使用解析器 class PostgresWireProtocolFrontendV3
的新实例处理每条消息,因此我建议只创建一个新的 .ksy
文件对于 StartupMessage
:
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 feature:
- Implement compile-time
sizeof
and bitsizeof
operators (#84)
- Type-based:
sizeof<u4>
, bitsizeof<b13>
, sizeof<user_type>
- Value-based:
file_header._sizeof
, flags._bitsizeof
(file_header
, flags
are fields defined in the current type)
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)))
当然,我不知道你的用例,所以我只是猜测它可能是什么,但希望至少其中一些对你有用。
So I need to somehow say "The object can be one of these two types", but I'm unsure how to do it.
这不是 Kaitai Struct works. Kaitai Struct has (intentionally) no backtracking, it's designed to handle non-ambiguous binary formats (see ). While it is possible to use instances
做某种前瞻性决定接下来发生什么的方式,除非你真的需要它,否则最好避免它。
我正在尝试为 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 中对其进行编码吗?
附属免责声明:我是 Kaitai Struct maintainer (see my GitHub profile)。
查看 PostgreSQL docs,似乎 StartupMessage
只能是“第一条消息”:
The first byte of a message identifies the message type, and the next four bytes specify the length of the rest of the message (this length count includes itself, but not the message-type byte). The remaining contents of the message are determined by the message type. For historical reasons, the very first message sent by the client (the startup message) has no initial message-type byte.
我不知道您使用 Kaitai Struct-generated 解析器的应用程序将如何运行,因此我不确定如何以适合您的方式使用此信息。您问题中的 .ksy
片段表明您将使用解析器 class PostgresWireProtocolFrontendV3
的新实例处理每条消息,因此我建议只创建一个新的 .ksy
文件对于 StartupMessage
:
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 feature:
- Implement compile-time
sizeof
andbitsizeof
operators (#84)
- Type-based:
sizeof<u4>
,bitsizeof<b13>
,sizeof<user_type>
- Value-based:
file_header._sizeof
,flags._bitsizeof
(file_header
,flags
are fields defined in the current type)
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)))
当然,我不知道你的用例,所以我只是猜测它可能是什么,但希望至少其中一些对你有用。
So I need to somehow say "The object can be one of these two types", but I'm unsure how to do it.
这不是 Kaitai Struct works. Kaitai Struct has (intentionally) no backtracking, it's designed to handle non-ambiguous binary formats (see instances
做某种前瞻性决定接下来发生什么的方式,除非你真的需要它,否则最好避免它。