FIDL 传输格式规范

如需详细了解 FIDL 的整体目标、目标和要求, 请参阅概念概览

核心概念

消息

FIDL 消息是数据的集合。

该消息是一个连续的结构,由单个 内嵌主要对象,后跟零个或多个 脱线次要对象

对象按遍历顺序存储,并会根据内边距进行填充。

绘制

主要对象和次要对象

第一个对象称为“主要对象”。 它是一个具有固定大小的结构,其类型和大小可从 上下文。阅读消息时,必须知道预期 即格式不是自描述格式。背景信息 应该能够明确说明相应情况例如 在将消息作为 IPC 的一部分读取的情况下 (请参阅事务性消息标头), 上下文由标头中包含的数据完全指定 (特别是,序数可让收件人知道 预期类型)。要读取静态数据 但假定编码器和解码器 了解正在编码或解码的类型(例如, 系统会将这些信息编译到 编码器和解码器)。

主要对象可以指次要对象(例如 例如字符串、向量、并集等)。 或可选数据是必填项

次要对象以遍历顺序脱行存储。

主要对象和次要对象都是 8 字节对齐的, 无间断(对齐所需的除外)。

主要对象及其次要对象统称为 消息

交易消息

事务性 FIDL 消息(事务性消息)用于 将数据从一个应用发送到另一个应用

事务性消息部分介绍了如何 事务性消息由可选的标头消息组成 。

遍历顺序

消息的遍历顺序由递归深度优先原则决定 它包含的所有对象的步行,可通过沿着链获得 引用。

采用以下结构:

type Cart = struct {
    items vector<Item>;
};

type Item = struct {
    product Product;
    quantity uint32;
};

type Product = struct {
    sku string;
    name string;
    description string:optional;
    price uint32;
};

Cart 消息的深度优先遍历顺序由以下代码定义 伪代码:

visit Cart:
    for each Item in Cart.items vector data:
        visit Item.product:
            visit Product.sku
            visit Product.name
            visit Product.description
            visit Product.price
        visit Item.quantity

双形式:编码与解码

相同的邮件内容可以用以下两种形式之一表示: 即经过编码和解码。 它们具有相同的尺寸和整体布局,但它们 指针(内存地址)或句柄(功能)的表示形式。

FIDL 设计为可以对消息进行编码解码 都保存在内存中

消息编码是规范的,因为消息中只有一个编码。 特定消息

绘制

已编码的消息

经过编码的消息已已准备好转移到另一个进程:它 不包含指针(内存地址)或句柄(功能)。

编码期间...

  • 邮件中指向子对象的所有指针都替换为 指明其所指代词是否存在,
  • 邮件中的所有标识名都将提取到关联的标识名中 vector,并被替换为指示其所引用对象是否为 是否存在。

然后,系统会将生成的经过编码的消息句柄矢量发送到 使用 zx_channel_write() 或类似 IPC 的其他进程 机制。 此类 IPC 还有其他限制。请参阅 事务性消息

已解码的消息

已准备可在进程地址中使用的解码消息 空间:它可能包含指针(内存地址)或句柄(功能)。

解码期间:

  • 使用 编码的存在标记和不存在标记。
  • 消息中的所有标识名都会从关联的 处理矢量

生成的解码消息可直接从内存中使用。

内嵌对象

对象还可能包含内联对象,这些内联对象汇总在 所包含对象的正文,例如嵌入式结构体和 结构体。

示例

在以下示例中,Region 结构包含一个向量 Rect 结构,其中每个 Rect 由两个 Point 组成。 每个 Point 均由 xy 值组成。

type Region = struct {
    rects vector<Rect>;
};

type Rect = struct {
    top_left Point;
    bottom_right Point;
};

type Point = struct {
    x uint32;
    y uint32;
};

按遍历顺序检查对象意味着我们将从 Region 结构体 - 它是主要对象。

rects 成员是 vector,因此其内容以外部形式存储。 这意味着,vector 内容紧跟在 Region 对象之后。

每个 Rect 结构体都包含两个 Point,它们以内嵌方式存储 (因为它们的数量是固定的),而每个 Point 的 原始数据类型(xy)也以内嵌方式存储。 原因都一样成员类型数量是固定的

绘制

当从属实例大小时,我们使用内嵌存储空间 对象是固定的,且超出行(包括 盒装结构体)。

类型详细信息

在本部分中,我们将介绍所有 FIDL 对象的编码。

原语

  • 小端字节序格式存储的值。
  • 包装自然对齐。
    • 每个 m 字节基元都存储在 m 字节边界上。
  • 非可选。

支持以下基元类型:

类别 类型
布尔值 bool
有符号整数 int8int16int32int64
无符号整数 uint8uint16uint32uint64
IEEE 754 floating-point float32float64
字符串 (非基元,请参阅字符串

数字类型的后缀是其大小(以位为单位)。

布尔值类型 bool 存储为单个字节,并且只有 值 01

所有浮点值均表示有效的 IEEE 754 位模式。

绘制

绘制

枚举和位

位字段和枚举存储为其底层基元 类型(例如uint32)。

句柄

句柄是一个 32 位整数,但经过特殊处理。 在编码以进行传输时,句柄的传输表示形式会替换为 存在和不存在指示,而句柄本身存储在 单独的句柄矢量。解码后,句柄存在状态指示为 替换为零(如果不存在)或有效句柄(如果有)。

标识名本身不会从一个应用转移到 另一个。

在这方面,手柄就像指针一样它们会引用 都是独一无二的 句柄从一个应用的上下文移至另一个应用的上下文。

绘制

值 0 可用于表示缺少可选句柄1

如需查看有关将标识名转移过来的详细示例,请参阅标识名的生命周期。 FIDL。

汇总对象

聚合对象充当其他对象的容器。 它们可能会以内联或外行的方式存储这些数据,具体取决于其类型。

数组

  • 固定长度的同构元素序列。
  • 其元素采用了自然对齐的方式。
    • 数组的对齐方式与其元素的对齐方式相同。
    • 每个后续元素都会在元素的对齐边界上对齐。
  • 该数组的步长正好等于元素的大小( 包含满足元素对齐约束所需的内边距)。
  • 一律不可选。
  • 布尔值数组没有特殊情况。每个布尔值元素 字节。

数组的表示形式:

  • array<T, N>:其中 T 可以是任何 FIDL 类型 (包括数组),N 是数组中的元素数量。 注意:数组的大小不得超过 232-1。 如需了解详情,请参阅 RFC-0059

绘制

矢量

  • 可变长度的同构元素序列。
  • 可以选填;不存在向量和空向量是不同的。
  • 可以指定大小上限,例如vector<T>:40 最多包含 40 个元素矢量。
  • 存储为一条 16 字节的记录,其中包括: <ph type="x-smartling-placeholder">
      </ph>
    • size:64 位无符号元素数 注意:矢量的大小不得超过 232-1。 如需了解详情,请参阅 RFC-0059
    • data:64 位存在状态指示或指向外行元素数据的指针
  • 在编码以进行传输时,data 表示 内容: <ph type="x-smartling-placeholder">
      </ph>
    • 0:矢量不存在
    • UINTPTR_MAX:矢量存在,数据是下一个外行对象
  • 在解码以供使用时,data 是一个 指向内容的链接: <ph type="x-smartling-placeholder">
      </ph>
    • 0:矢量不存在
    • <valid pointer>:矢量存在,数据位于指定的内存地址
  • 布尔值矢量没有特殊情况。每个布尔值元素 字节。

向量表示如下:

  • vector<T>:必需的元素类型 T 向量(验证错误) data 缺失时会发生)
  • vector<T>:optional:元素类型 T 的可选矢量
  • vector<T>:Nvector<T>:<N, optional>:最大长度为 N 个元素的矢量

T 可以是任何 FIDL 类型。

绘制

字符串

字符串以 uint8 字节的矢量的形式实现,并具有约束条件 这些字节必须是有效的 UTF-8。

结构

一个结构体包含一系列类型化字段。

在内部,该结构具有内边距 所有成员的对齐要求。 在外部,结构在 8 字节边界上对齐,因此可能包含 最终填充以满足该要求。

以下是一些示例。

具有 int32int8 字段的结构体对齐 4 个字节(由于 int32),大小为 8 个字节(int8 之后的 3 个字节填充):

绘制

包含 boolstring 字段的结构体的对齐方式为 8 个字节 (由于 string 的原因)且大小为 24 个字节(在 bool):

绘制

具有 bool 和两个 uint8 字段的结构体的对齐方式为 1 字节 大小为 3 个字节(无填充!):

绘制

结构可以是:

  • empty 表示其中没有任何字段。此类结构的大小为 1 个字节, 对齐 1 个字节,完全等同于包含 uint8,值为 0。
  • 必需 - 结构的内容内嵌存储。
  • 可选 - 结构的内容外行存储 通过间接引用访问

结构的存储取决于其在参照点处是否装箱。

  • 非盒装结构: <ph type="x-smartling-placeholder">
      </ph>
    • 内容以内嵌方式存储在其包含类型中, 构建高效的聚合结构。
    • 进行内嵌时,结构布局不会更改;其字段不是 重新打包以填补容器中的空白
  • 盒装结构: <ph type="x-smartling-placeholder">
      </ph>
    • 内容存储到在线之外,并可通过间接 参考。
    • 在编码以进行传输时,存储的引用表示 结构:
    • 0:引用不存在
    • UINTPTR_MAX:引用存在,构造内容 是下一个输出行外对象
    • 在解码以供使用时,存储的引用是一个指针:
    • 0:引用不存在
    • <valid pointer>:引用存在,结构内容位于 指示的内存地址

结构体由其声明的名称(例如 Circle)表示,可以装箱:

  • Point:必需 Point
  • box<Color>:盒装,始终可选 Color

以下示例说明了:

  • 结构布局(顺序、打包和对齐)
  • 必需的结构 (Point)
  • 盒装 (Color)
type Circle = struct {
    filled bool;
    center CirclePoint; // CirclePoint will be stored in-line
    radius float32;
    color box<Color>; // Color will be stored out-of-line
    dashed bool;
};

type CirclePoint = struct {
    x float32;
    y float32;
};

type Color = struct {
    r float32;
    g float32;
    b float32;
};

Color 内容将填充到 8 字节次要对象对齐边界。 详细介绍布局:

绘制

  1. 第一个成员 filled bool 占用一个字节,但需要三个字节 填充,因为下一个成员采用 4 字节对齐 。
  2. center CirclePoint 成员是一个必需结构体的示例。因此 其内容(xy 32 位浮点数)内嵌在内,整个代码 消耗 8 个字节。
  3. radius 是一个 32 位内容,需要 4 字节对齐。自下一个 可用位置已位于 4 字节对齐边界上,无填充 为必填字段。
  4. color box<Color> 成员就是一个盒装结构的一个示例。由于 color 数据不一定会存在,这是最高效的处理方式 这是为了将指针作为内嵌数据保存在该结构中。这样, 如果确实存在 color 成员,则该指针将指向其数据 (或者,如果是编码格式,则指示“存在”),并且 数据本身存储外联(在 Circle 的数据之后), 结构)。如果 color 成员不存在,则指针为 NULL (或者,在编码格式中,通过存储 0 来表示“不存在”)。
  5. dashed bool 不需要任何特殊对齐,因此接下来会运行。 不过,现在我们到了对象的末尾,所有对象都必须 8 字节对齐。这意味着我们需要额外的 7 个字节的填充。
  6. color 的外联数据遵循 Circle 数据结构,以及 包含三个 32 位 float 值(rgb);需要 4 个 字节对齐,因此可以在没有内边距的情况下相互依存。但是,正如 对于 Circle 对象,我们要求对象本身 8 字节对齐,因此需要 4 个字节的填充。

总体而言,此结构需要 48 个字节。

不过,通过将 dashed bool 移到 filled bool 之后, 您可以节省大量空间2

绘制

  1. 两个 bool 值是“打包” 浪费空间。
  2. 内边距已缩减至两个字节,因此非常适合 添加一个 16 位值,或者再添加一些 bool 或 8 位整数。
  3. 请注意,Color 框之后不需要内边距;所有内容 在 8 字节边界上完全对齐。

该结构现在需要 40 个字节。

信封

信封是数据的容器,供表和联合在内部使用。时间是 且未采用 FIDL 语言。具有固定的 8 字节格式。

全零的信包标头称为“零信包”。它 用于表示不存在的信封。否则,存在信封 其标记的位 0 指示数据以内嵌方式还是非在线方式存储:

  • 如果设置了位 0,则使用内嵌表示法

绘制

  • 如果未设置位 0,则使用脱机表示法

绘制

只有在有效负载的大小小于等于 4 字节时才能设置位 0。位 0 可以是 仅当信封是零个信封或 载荷 >4 个字节。

有了 num_bytesnum_handles,我们就可以跳过未知的信封内容。

num_bytes 始终是 8 的倍数,因为外行对象 8 字节对齐。

表格

  • 由元素数量和指针组成的记录类型。
  • 指针指向一个信封数组,其中每个信封都包含一个元素。
  • 每个元素都与一个序数相关联。
  • 序数是连续的,间隔会产生空信封费用,因此不建议使用。

表由其声明的名称表示(例如,),且绝不是可选的:

  • Value:必需 Value

以下示例展示了表如何根据其字段进行布局。

type Value = table {
    1: command int16;
    2: data Circle;
    3: offset float64;
};

绘制

联合体

  • 由序数和信封组成的记录类型。
  • 序数表示成员选择,由 uint64 表示。
  • 每个元素均与用户指定的序数相关联。
  • 序数是依序排列。与表格不同,序数中的间隙不会产生导线 格式空间成本。
  • 不存在可选并集时,会使用 0 序数和零信封来表示。
  • 不允许空联合。

联合由其声明的名称(例如 Value)和可选性表示:

  • Value:必需 Value
  • Value:optional:可选的 Value

以下示例展示了联合如何根据其字段进行布局。

type UnionValue = strict union {
    1: command int16;
    2: data Circle;
    3: offset float64;
};

绘制

事务性消息

在事务性消息中,始终有一个标头,后跟 可选的 body

标头和正文都是 FIDL 消息,如上文所定义;即 数据集合。

标头采用以下格式:

绘制

  • zx_txid_t txid,事务 ID(32 位) <ph type="x-smartling-placeholder">
      </ph>
    • 具有高位设置的 txid 已预留给以下人员使用: zx_channel_call()
    • 未设置高位的 txid 已预留以供用户空间使用
    • txid0 值将用于 需要另一方作出响应。 注意:如需详细了解 txid 分配,请参阅 zx_channel_call().
  • uint8[3] flags 不得通过绑定进行检查。这些标志可用于 以实现电线格式的软过渡。请参阅标头标记 ,了解有关当前标志定义的说明。
  • uint8 magic number,用于确定两种传输格式是否兼容。
  • uint64 ordinal
    • 零序数无效。
    • 设置了最高有效位的序数保留 控制消息和未来的扩展。
    • 未设置最高有效位的序数表示方法调用 和响应。

事务消息有三种类型:

  • 方法请求,
  • 方法响应,以及
  • 事件请求。

在接下来的几个示例中,我们将使用以下接口:

type DivisionError = strict enum : uint32 {
    DIVIDE_BY_ZERO = 1;
};

protocol Calculator {
    Add(struct {
        a int32;
        b int32;
    }) -> (struct {
        sum int32;
    });
    Divide(struct {
        dividend int32;
        divisor int32;
    }) -> (struct {
        quotient int32;
        remainder int32;
    }) error DivisionError;
    Clear();
    -> OnError(struct {
        status_code uint32;
    });
};

Add()Divide() 方法对方法请求进行了说明 (从客户端发送到服务器),以及一个方法响应 (从服务器发送回客户端)。

Clear() 方法是不

错误的说法是“空”正文:这意味着 header 后面是body。在使用 Clear() 时, 没有 body,只有一个 header

方法请求消息

接口的客户端向服务器发送方法请求消息 才能调用 方法。

方法响应消息

服务器向客户端发送方法响应消息以表明已完成 方法调用并提供一个(可能为空的)结果。

只有定义为提供(可能为空)结果的双向方法请求 都会引发方法响应。 单向方法请求不得生成方法响应。

方法响应消息提供与先前的方法请求相关联的结果。 邮件正文包含方法结果,就好像这些结果封装在 struct

在这里,我们看到 912 / 43 的答案是 21,余数为 9。 请注意 1txid 值,此值用于标识交易。 2ordinal 值表示该方法,在本例中为 Divide() 方法。

绘制

如下所示,123 + 456579。 在这里,txid 值现在为 2,这只是下一笔交易 分配给交易的编号。 ordinal1,表示 Add()。请注意,结果需要 4 个字节的内边距,以使 body 对象的大小 8 个字节的倍数。

绘制

最后,Clear() 方法与 Add() 方法不同, Divide(): * 它没有正文(即,只包含标头),并且 * 它不会从接口请求响应(txid 为零)。

绘制

事件请求

一个事件示例是 Calculator 中的 OnError() 事件。

服务器向客户端发送自发事件请求 以指明发生了异步事件,如 协议声明

Calculator 示例中,我们可以想象尝试除以 0 会导致系统发送 OnError() 事件并显示“除以零”状态代码 然后再关闭连接。这样,客户端就可以区分 与因错误而关闭的连接 (如计算器进程异常终止)。

绘制

请注意 txid 如何为零(表示这不是交易的一部分), 且 ordinal4(表示 OnError() 方法)。

body 包含事件参数,就像它们封装在 struct,与方法结果消息一样。 请注意,正文已填充以保持 8 字节对齐。

墓碑(控制消息序号 0xFFFFFFFFFFFFFFFF)

复数是一个事件(txid 零),其序数为 0xFFFFFFFFFFFFFFFF。服务器 在关闭连接前, 可以发送一封摘要信息作为最后一条消息, 提供连接关闭原因的指示。没有其他消息 可在墓碑后通过连接发送。墓碑并非来自 连接到服务器

墓碑的电线表示形式等同于以下 FIDL: fidl struct { error zx.Status; }; 将来可能会在 FIDL 中正式定义墓碑。

详细信息

大小和对齐

sizeof(T) 表示类型为 T 的对象的大小(以字节为单位)。

alignof(T) 表示用于存储类型为 T 的对象的对齐系数(以字节为单位)。

FIDL 基元类型存储在多个 其大小(以字节为单位)。因此,对于基元 T 而言,其值为 alignof(T) == sizeof(T)。这称为自然对齐。它具有 满足现代 CPU 的典型对齐要求的良好属性 架构。

FIDL 复杂类型(例如结构体和数组)存储在 是其所有消息中最大对齐系数的倍数, 字段。因此,对于复杂类型 Talignof(T) == max(alignof(F:T)) 会针对 T 中的所有字段 F 执行。它有很好的 属性(可以是 使用生成的代码中的打包属性强制执行的)。复杂大小 type 是存储其成员适当对齐所需的字节总数 最大范围的对齐方式因数。

FIDL 主要对象和次要对象在 无论消息内容如何FIDL 消息的主要对象 从偏移量 0 开始。次要对象,此类对象是 消息中的指针,始终从 8 的倍数的偏移量开始。 (因此,消息中的所有指针都指向 8 的倍数的偏移量)。

FIDL 内嵌对象(嵌入到主要或次要中的复杂类型) 对象)根据其类型对齐。不强制使用 8 个字节 对齐方式。

类型

注意:

  • N 表示元素的数量,无论是否明确声明(如 array<T, N>,包含 N 个类型为 T 元素的数组,或隐式地包含该元素 (由 7 个元素组成的 table 将具有 N=7)。
  • 行外大小始终填充为 8 个字节。我们会指明 尺寸如下所示(不包括内边距)。
  • 以下 vector 条目中的 sizeof(T)
    in_line_sizeof(T) + out_of_line_sizeof(T)
  • 以下 table 条目中的 M 是当前字段的最大序数。
  • 在下面的 struct 条目中,内边距指的是所需的内边距, 使 struct 与最宽的元素对齐。例如: struct{uint32;uint8} 包含 3 个字节的填充,这与 填充,以与 8 个字节的边界对齐。
类型 尺寸(内嵌) 大小(非在线) 对齐方式
bool 1 0 1
int8uint8 1 0 1
int16uint16 2 0 2
int32uint32float32 4 0 4
int64uint64float64 8 0 8
enumbits (底层类型) 0 (底层类型)
handle 等。 4 0 4
array<T, N> sizeof(T) * N 0 对齐
vector 等。 16 N * sizeof(T) 8
struct 求和(sizeof(fields)) + 填充 0 8
box<struct> 8 求和(sizeof(fields)) + 填充 8
envelope 8 sizeof(字段) 8
table 16 M * sizeof(envelope) +sum(aligned_to_8(sizeof(当前字段))) 8
unionunion:optional 16 sizeof(所选变体) 8

上面的 handle 条目是指所有类型的句柄,具体来说, handlehandle:optionalhandle:Hhandle:<H, optional>client_end:Protocolclient_end:<Protocol, optional>server_end:Protocolserver_end:<Protocol, optional>

同样,上面的 vector 条目是指所有类型的向量, 具体而言,vector<T>vector<T>:optionalvector<T>:Nvector<T>:<N, optional>stringstring:optionalstring:Nstring:<N, optional>

内边距

消息的创建者必须用零填充所有对齐填充间隙。

消息的使用者必须验证填充是否包含零(并生成 否则将抛出错误)。

最大递归深度

FIDL 矢量、可选结构、表和联合支持 递归消息。如果未选中,处理过于深层的消息 或导致资源耗尽,或造成无法检测到的无限循环。

为安全起见,所有 FIDL 消息的最大递归深度限制为 32 层间接引导。FIDL 编码器、解码器或验证器必须 该限制通过在调用过程中跟踪当前递归深度来实施 消息验证。

递归深度的正式定义:

  • FIDL 消息的内联对象定义为具有递归深度 0
  • 每次通过指针或信封遍历间接遍历时 用 1 递增递归深度。

如果任何时候,递归深度超过 32,就必须 终止并引发错误。

例如:

type InlineObject = struct {
    content_a string;
    vector vector<OutOfLineStructAtLevel1>;
    table TableInlineAtLevel0;
};

type OutOfLineStructAtLevel1 = struct {
    content_b string;
};

type TableInlineAtLevel0 = table {
    1: content_c string;
};

InlineObject 的实例进行编码时,我们有相应的递归深度:

  • content_a 的字节位于递归深度为 1 的位置,即 content_a 字符串标头内嵌在 InlineObject 结构体中,字节位于 可通过指针间接访问的外行对象。
  • content_b 的字节位于递归深度为 2 的位置,即 vector 标头内嵌在 InlineObject 结构体中, 因此,OutOfLineStructAtLevel1 结构体的递归深度为 1, content_b 字符串标头内嵌在 OutOfLineStructAtLevel1 中,并且 个字节位于可通过指针间接访问的外行对象中 从第 1 层开始,使深度为 2。
  • content_c 的字节位于递归深度为 3 的位置,即 table 标头内嵌在 InlineObject 结构体中,表信封位于 深度为 1,指向深度为 2 的 content_c 字符串标头,以及 字节位于可通过指针间接访问的外行对象中, 深入到 3 层。

验证

验证消息的目的是尽早发现传输格式错误 也有可能诱发安全或稳定性问题。

解码从对等端收到的消息时必须进行消息验证 以防止不良数据传播到服务入口点以外。

将消息编码为可选时,建议进行消息验证 以帮助对违反的完整性约束进行本地化。

为了尽可能减少运行时开销,验证通常应作为 一个密码,以便只发送 遍历。由于消息采用深度优先遍历顺序进行编码, 遍历展现出良好的内存局部性,因此应该非常高效。

对于简单的消息,验证可能非常简单,最多只需 很少检查尺寸虽然我们鼓励程序员依靠他们的 FIDL 绑定 代表他们验证消息的库,也可以进行验证 (如果需要的话)。

一致的 FIDL 绑定必须检查以下所有完整性约束:

  • 消息的总大小,包括消息的所有外行子对象 等于包含该数据的缓冲区的总大小。全部 子对象。
  • 消息引用的句柄总数正好等于 句柄表的总大小。会考虑所有标识名。
  • 不超过复杂对象的最大递归深度。
  • 所有枚举值都在其定义的范围内。
  • 所有联合标记值都在其定义的范围内。
  • 仅编码: <ph type="x-smartling-placeholder">
      </ph>
    • 遍历期间遇到的所有子对象的指针都精确引用 到子对象预计出现的下一个缓冲区位置。如 由此推论,指针绝不会引用缓冲区以外的位置。
  • 仅解码: <ph type="x-smartling-placeholder">
      </ph>
    • 所引用子对象的所有存在和不存在标志都会存储 值只能为 0UINTPTR_MAX
    • 所引用句柄的所有存在和不存在标记都会存储值 仅支持 0UINT32_MAX

标头标记

Flags[0]

当前用量 以往的用法
7(MSB) 未使用
6 未使用
5 未使用
4 未使用
3 未使用
2 未使用
1 指示是否使用 v2 传输格式 (RFC-0114)
0 未使用 指示静态联合体是否应编码为 xunions (RFC-0061)

Flags[1]

当前用量 以往的用法
7(MSB) 未使用
6 未使用
5 未使用
4 未使用
3 未使用
2 未使用
1 未使用
0 未使用

Flags[2]

当前用量 以往的用法
7(MSB) 未使用
6 未使用
5 未使用
4 未使用
3 未使用
2 未使用
1 未使用
0 未使用

  1. 定义零句柄以表示“没有句柄”这意味着您可以安全地 将 Wi-Fi 格式结构默认初始化为零。零也是 ZX_HANDLE_INVALID 常量的值。

  2. 深入了解《The Lost Art of Structure Packing》 相关论文。