本文說明如何更新或擴充元件檢查檔案格式。
擴充格式時,請務必不要破壞任何現有功能,尤其是檢查讀取器和驗證測試。不過,將所有變更打包成單一變更,審查起來可能會很吃力。一般來說,鏈結中應有 5 到 9 個變更或步驟,可改變 VMO 格式:
- (可能不適用) 更新 VMO 格式文件,選擇類型編號。
- 更新 Rust 讀取器。
- 更新 C++ 讀取器。
- 更新 Rust 寫入器。
- 更新 C++ 寫入器。
- (可能) 更新驗證工具。
- 更新說明文件。
- (非變更)(選用) 傳送功能公告。
選擇型號
在「檢查檔案格式」中查看類型表格,然後選擇可用的類型編號。目前的規格共有 256 種可能的類型。
如要預訂新類型,請更新檢查檔案格式。
實作
如果沒有讀取器或寫入器,測試讀取器或寫入器會很麻煩,而且如果沒有讀取器和寫入器,也很難取得適當的區塊層級 API。
如要測試讀取者和寫入者,請按照下列步驟操作:
選擇一種語言,並完全以該語言設計及實作這項功能,同時使用單元測試模擬 API 的實際使用情形。
將變更分成讀取器和寫入器變更,並將寫入器堆疊在讀取器上方。
此時,讀者中依附於 Writer API 的測試可能已在讀者變更中損毀。
將變更重新納入基準,並使用較低層級的功能重新編寫測試 (保留原始測試版本)。
通常您可以將所有變更放入讀取器變更中,但這樣會導致讀取器 API 的測試變得複雜。
測試結果會很難看。重新設定寫入器變更的基準,並移除修改後的測試,改用以高階 API 編寫的原始測試。
以這兩項變更做為參考,並以第二種語言複製這些變更。
(選用) 視變更內容而定,您可能必須一邊進行變更,一邊更新驗證工具測試,因為現有區塊的現有格式變更可能會導致測試失敗。
下列各節將說明如何組合各項變更,但實際上,請先將這些內容視為設計整個系統的提示,再如上所述分割設計。
更新 Rust 實作項目
本節中的範例會建立名為 MyFoo 的新型別。
設定
- 包含測試:
fx set core.x64 --with //src/lib/diagnostics:tests
執行測試:
fx test inspect-format-tests fuchsia-inspect-tests
讀取者變更
位元欄位更新
如要定義新的 Block Type,請更新
BlockType列舉。更新為
BlockType定義的方法和函式。如要變更現有區塊中的欄位或建立新的
BlockType,請更新位元欄位版面配置。執行
inspect-format-tests,確認變更已編譯:
fx test inspect-format-tests
更新區塊定義,加入讀取和寫入新欄位的方法。此時在區塊程式庫中加入寫入功能是適當的,因為沒有這項功能,幾乎不可能編寫讀取器測試。
編寫區塊測試,運用新功能。
編寫測試,對區塊的前 8 到 16 個位元組進行判斷。
這通常是指將預期內容寫入
&[u8](包含十六進位值),並判斷其是否等同於區塊用做容器的緩衝區。
更新讀者
您可以在 mod.rs 中找到讀取器程式碼。 這項變更中的測試可能很棘手,因為高階 API 撰寫器尚未存在。
作者變更
狀態
主要異動在於State功能。
您可以在這裡分配區塊,並轉換為新類型。
如果您只是要修改現有區塊,可能只需要在這裡進行變更。
建立新的值類型
您可以在型別目錄中新增型別檔案。
在建立的檔案中建立新類型。以現有型別為例。型別一律可存取內部
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是 Set 的引數型別。如需支援陣列的數值運算:
using MyFood = internal::ArrayProperty<T>,其中T是陣列中位置的引數型別。如需支援插入直方圖:
using MyFoo = internal::{Linear,Exponential}Histogram<T>,其中T是 Insert 的引數。
Bespoke
建立新的型別包裝函式。例如
class MyFoo final。確認您的類別已將
internal::State設為友元類別。注意:如要取得可複製的起點,請參閱class Link這篇文章。
狀態動作更新
State 類別是所有類型所有作業的實際實作項目。本節說明如何實作完成包裝函式實作所需的作業。
更新
State標題:新增「建立」和「免費」方法。舉例來說:
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_是否為空值。