本文档介绍了如何更新或扩展组件检查文件 格式。
在扩展格式时,切勿破坏任何现有功能, 特别是“检查读者”和“验证”测试不过,将所有更改打包到一个文件中 审核内容可能会让人无所适从一般情况下,在一个链中应有 5-9 个更改或步骤 更改 VMO 格式:
- (可能不适用)通过更新 VMO 格式文档来选择类型编号。
- 更新了 Rust 读取器。
- 更新 C++ 读取器。
- 更新了 Rust writer。
- 更新了 C++ 写入器。
- (可能)更新验证器。
- 更新了文档。
- (未更改)(可选)发送功能通知。
选择编号类型
在检查文件格式中查看类型表格,然后选择一个可用的类型编号。当前规范中总共有 256 种可能的类型。
为了保留新类型,请更新检查文件格式。
实现
对一个读者或作者进行测试十分麻烦,要获得正确的 没有读取器和写入器的块级 API。
如需测试 reader 和 writer,请执行以下操作:
选择一种语言,使用单元设计并完全以该语言实现该功能 对 API 的实际使用情况进行建模。
将更改拆分为单独的读取方和写入方更改,将写入方堆叠在顶部 。
此时,读取器中依赖于写入器 API 的测试可能会因读取器更改而失效。
将基准更新为该更改,然后使用较低级别的功能重写测试(保留测试的原始版本)。
通常,您可以将对块代码的所有更改放入读取器更改, 为读取器 API 编写测试的麻烦。
测试会很难看。重新基于 Writer 更改,并移除修改后的测试,将其替换为使用高级 API 编写的原始测试。
请将这两项更改作为参考,并用第二语言复制它们。
(可选)根据更改的内容,您可能需要更新 validator会不断地进行测试,因为现有 都有可能破坏它们
以下部分概述了如何综合运用各项更改,但实际操作时,可参考这些内容作为提示 在按上述方式拆分设计之前,统一设计整个系统。
更新 Rust 实现
本部分中的示例将创建一个名为 MyFoo
的新类型。
设置
- 添加测试:
fx set core.x64 --with //src/lib/diagnostics:tests
运行测试:
fx test inspect-format-tests fuchsia-inspect-tests
读者变更
位字段更新
如果定义了新的分块类型,请更新
BlockType
枚举。更新为
BlockType
定义的方法和函数。如果要更改现有块中的字段或创建新的
BlockType
,请更新 位字段布局。运行
inspect-format-tests
以验证更改是否已编译:
fx test inspect-format-tests
更新块定义,以添加用于读取和写入新数据的方法 字段。目前,在块库中添加写入功能是合适的; 没有它,几乎不可能编写读者测试。
编写用于执行新功能的块测试。
编写在块的前 8-16 个字节上做出断言的测试。
通常,这意味着将预期内容写入包含十六进制值的
&[u8]
,并断言其与块用作容器的缓冲区等效。
更新读取器
您可以在 mod.rs 中找到读取器代码。此变更中的测试可能会有些棘手,因为高级 API 编写器不存在 。
更改作者
状态
主要变更将在 State
功能中进行。您可以在此处分配块并将其转换为新类型。
如果您只是要修改现有块,那么这可能是您唯一需要的位置
进行更改。
创建新的值类型
有一个 types 目录,您可以在其中为您的类型添加新文件。
在创建的文件中创建新类型。使用现有类型作为示例。类型始终 有权访问内部
State
。使用它来创建必要的 方法,调用在State
中创建的方法。向
Node
添加一个用于创建新类型的方法。确保您的类型在 VMO 中具有 RAII 语义。如果您的类型是值,则这可能由从第 1 步中现有类型复制的样板自动完成。
最后,返回并从 Reader 更改更新测试,以使用新的 API!
更新 C++ 实现
本部分中的示例将创建一个名为 MyFoo
的新类型。
如上所述,本部分应在 Gerrit 中进行两项更改。
设置
- 添加测试:
fx set core.x64 --with //zircon/system/ulib/inspect:tests
- 运行测试。
fx test inspect-cpp-unittest
读者变更
位字段更新
本部分介绍了如何为新类型定义位字段。
更新屏蔽定义。
更改
BlockType
以添加新类型。例如:kMyFoo = #;
如果您的类型需要新的头文件(通常如果它不是
VALUE
),请使用结构体为您的类型定义头文件位字段。例如:struct MyFooBlockFields final : public BlockFields
。如果您的类型需要新的载荷(需要使用块的第二个 8 个字节), 使用结构体为您的类型定义载荷位字段。例如:
struct MyFooBlockPayload final
。如果您的类型包含枚举(例如格式),请在 block.h 顶部定义一个新的枚举。例如:
enum class MyFooBlockFormat : uint8_t
。
实现类型读取器
本部分介绍了如何使新类型可读。
根据您的类型更新检查层次结构:
值(Node
的子项)
根据您的类型使用新数字更新
PropertyFormat
枚举。必须在 该特定枚举,无需与您选择的格式类型序数匹配。创建新的值类型。例如:
using MyFooValue = internal::Value<T, static_cast<size_t>(PropertyFormat::kMyFoo)>;
使用新值更新
PropertyValue
变体。注意:fit::internal::variant
中的索引必须与PropertyFormat
的值一致。
不是值
- 您需要在层次结构文件中创建自己的内存表示对象。
更新实际读取器。
更新
InnerScanBlocks
以分派您的类型。如果您要创建新的Property
,可以 只需添加BlockType
即可。如果您需要自定义解析器,请实现
InnerParseMyFoo
,它接受父级(如果需要)和指向已扫描分块的指针。
作者更改
类型封装容器声明
本部分介绍了如何为新类型声明 C++ RAII 样式的封装容器。
类型封装容器包含该类型拥有的块的索引。您将对 在这些块上执行操作,包括创建和删除, 状态操作更新。
更新写入者类型定义。
确定您是否可以重复使用现有封装容器,或者是否需要使用自定义类型:
重复使用
如果您需要支持添加、减法和设置,请使用
using MyFoo = internal::NumericProperty<T>
, 其中T
是这些操作的参数类型。如果您需要支持 Set:
using MyFoo = internal::Property<T>
,其中T
是其参数 设置为“设置”。如果您需要支持对数组进行数字运算,请执行以下操作:
using MyFood = internal::ArrayProperty<T>
,其中T
是 数组。如果您需要支持向直方图插入数据,请使用
using MyFoo = internal::{Linear,Exponential}Histogram<T>
,其中T
是 Insert 的参数。
定制
创建一个新的类型封装容器。例如
class MyFoo final
。确保您的类有
internal::State
为好友类。注意:请参阅class Link
可复制的起点。
状态操作更新
State
类是所有类型的所有操作的实际实现。本部分介绍了如何实现完成封装容器实现所需的操作。
更新了
State
标头:添加了 Create 和 Free 方法。例如:
MyFoo CreateMyFoo(<args>); void FreeMyFoo(MyFoo* property);
,其中args
通常包含名称、父级和一些初始值。为需要支持的每种操作类型添加方法。例如,如果您的类型可以是 Set,则为
void SetMyFoo(MyFoo* property, T)
,其中T
与您更新后的 types.h 中的类型相同。
更新
State
:实现新类型的方法。不同类型的实现会有所不同。本部分简要介绍了每种方法必须执行的操作:
MyFoo CreateMyFoo(Args...)
负责分配多个块、设置其值,并将其返回为封装在MyFoo
中的值。您可以使用私有构造函数 以根据其封装的BlockIndex
对象创建MyFoo
。存在各种内部帮助程序 以简化此操作。如需查看示例,请参阅CreateIntProperty
。void FreeMyFoo(MyFoo* property)
负责释放MyFoo
。有时,释放块需要满足特定的排序要求或进行必要的更新。如需查看有关如何释放值的示例,请参阅InnerFreeValue
。操作(例如
void SetMyFoo(MyFoo* property, T value)
)会更改分配给MyFoo
的块的值,以实现操作。有关示例,请参阅SetIntProperty
。
实现类型封装容器
本部分介绍了如何实现之前声明的封装容器方法。
更新写入者类型定义:
如果您使用的是现有模板化类型,则需要为新基本替换每个方法 类型
T
。例如,如果您输入了using MyFoo = internal::Property<T>
,则会编写:template<> void internal::Property<T>::OPERATION(...) { ... }
如果您创建了自己的类型,只需为声明的方法创建定义即可。您需要执行以下操作:
让构造函数调用
state_->CreateMyFoo(...);
让析构函数调用
state_->FreeMyFoo(...);
让其他方法调用 State 上的相应实现。
请让所有构造函数和方法在调用之前检查
state_
是否不为 null。