RFC-0047:表格

RFC-0047:資料表
狀態已接受
領域
  • FIDL
說明

在 FIDL 語言中新增正向和回溯相容的複合資料類型。

作者
提交日期 (年-月-日)2018-07-27
審查日期 (年-月-日)2018-09-20

摘要

在 FIDL 語言中新增正向和回溯相容的複合資料類型。

與其他 RFC 的關係

此 RFC 後來經過以下修訂:

提振精神

FIDL 結構沒有提供隨時間變更結構定義的機制。 資料表與結構體類似,但可在每個欄位中加入序數,以允許結構進化:

  • 現有程式碼可新增及忽略新欄位
  • 舊版 (已淘汰) 的欄位可能會由較新的程式碼略過

資料表比結構更複雜,因此處理資料表時 速度較慢,序列化出來會佔用較多空間。 因此,最好將結構體保持原樣並導入新技術。

此外,有了可改良的結構定義,您就能輕鬆 FIDL 的變體,可以放心序列化到磁碟或網路。

表格範例可能如下:

table Station {
    1: string name;
    3: bool encrypted;
    2: uint32 channel;
};

設計

原文語言

請將 table_declaration 新增至 FIDL 文法:

declaration = const-declaration | enum-declaration | interface-declaration |
              struct-declaration | union-declaration | table-declaration ;

table-declaration = ( attribute-list ) , "table" , IDENTIFIER , "{" , ( table-field , ";" )* , "}" ;

table-field = table-field-ordinal , table-field-declaration ;

table-field-ordinal = ordinal , ":" ;

table-field-declaration = struct-field | "reserved" ;

注意:

  • 序數必須從 1 開始,序空間則不允許有間隔 (如果最大為最大值) 序數是 7,則 1,2,3,4,5,6,7 全都必須包含。
  • 沒有任何兩個欄位可聲明相同序數。
  • 「預訂」] 欄位會在檢查序數衝突之後,遭到編譯器捨棄。 這個 API 可以加上註解,讓部分舊版資料表使用欄位, 以避免日後修訂時意外重複使用該序詞。
  • 資料表禁止可為空值的欄位。

凡是目前可在該語言中使用結構體的位置,都可以使用表格。 尤其是:

  • 結構體和聯集可以包含資料表
  • 資料表可以包含結構體和聯集
  • 介面引數可以是資料表
  • 也能將資料表設為

傳輸格式

資料表會以封裝的 vector<envelope> 形式儲存。 向量的每個元素都是一個序數元素 (因此索引 0 代表序數 1) 索引 1 是序數 2,以此類推)。 下文說明信封。

表格只能儲存最大信封至最後一個存在的信封 (也就是最大序數組合)。 這可確保以標準表示法呈現。舉例來說,如果沒有設定欄位, 編碼是空白向量 如果資料表的欄位設為序數 5,但欄位的值設為序數 3,那麼正確 編碼是 3 個信封的向量

信封

envelope 會儲存變數大小的未解譯酬載。 酬載可能包含任意數量的位元組和控制代碼。 這個機構允許封裝另一封郵件中的 FIDL 訊息。

信封會儲存為包含下列內容的記錄:

  • num_bytes:信封中的 32 位元無正負號位元組數,一律為 8 的倍數 如果信封為空值,則需為零
  • num_handles:信封中的 32 位元無正負號帳號代碼, 如果信封為空值,則需為零
  • data:64 位元存在狀態指標或指標指向外線資料

data 欄位有兩個不同行為。

編碼用於轉移時,data 表示內容存在:

  • FIDL_ALLOC_ABSENT (全部 0 位元):信封為空值
  • FIDL_ALLOC_PRESENT (全部 1 位元):信封為非空值, 「資料是下一個虛線物件」

如果經過解碼以便使用,data 會指向內容。

  • 0:信封為空值
  • <valid pointer>:信封為非空值,資料位於指定的記憶體位址

若是控點,則在內容結束後,信封會為控點保留儲存空間。 解碼時,假設 data 不是空值,則 data 會指向資料的第一個位元組。

信封會填充到接下來的 8 位元組物件對齊方式 (實際上是 不需要額外的邊框間距

語言繫結

資料表不會產生結構體等資料欄位,而是為每個欄位產生一組方法。 例如,在 C++ 中,我們會:

class SampleTable {
 public:
  // For "1: int32 foo;"
  const int32* foo();         // getter, returns nullptr if foo not present
  bool has_foo();             // presence check
  int32* mutable_foo();       // mutable getter, forces a default value if not set
  void set_foo(int32 x);      // set value
  void clear_foo();           // remove from structure
  optional<int32> take_foo(); // get foo if present, remove from structure
};

格式指南

我應該使用結構體還是表格?

結構體和資料表提供語意相似的概念 決定要採用的方法似乎很複雜

如為極高等級處理序間通訊 (IPC),或適用於永久儲存空間 許多人不必為序列化效能擔心:

  • 表格提供一些前瞻相容性和回溯相容性功能, 概念:在採用大部分概念時都優先採用。
  • 利用結構體的效能優勢, 未來不太可能改變 (例如 struct Vec3 { float x; float y; float z }、 或 Ipv4Address)。

序列化效能會成為覆寫問題 (這種情況常見於 為裝置驅動程式的資料路徑) 傳回後,我們就只偏好結構體 就必須在介面中加入新方法,以便因應日後的變更。

回溯相容性

本次異動包含兩個關鍵字:tablereserved。 沒有回溯相容性疑慮。

成效

您可自行選擇使用這項功能,如果不使用,則不會影響 IPC 效能。 我們預期效能差異會是可測量的雜訊中。

安全性

不會影響安全性。

測試

您需要為每個語言繫結進行額外的測試,以及針對 fidlc 進行測試。

請擴充 echo 套件以使用資料表。

為資料表編碼/解碼加入模糊化器的做法,或許更有幫助 — 常會遇到棘手情況

缺點、替代方案和未知

這個領域還有兩個重要問題需要回答:

  • 欄位識別字串和字串 (序數強制傳送結構定義)
  • 如果是序數:每則訊息的稀疏度與密集序數空間

以聯集向量形式的資料表

建議將 table 視為vector<union>。 這會造成兩個問題:

  • 導入這種格式的閱讀器效率必須降低 比起建議的資料表格式,讀取器最有效率的閱讀器實作方式 ,因此對峰值效能永久限制了。
  • 兩者無法保證任何線路相容性! 向量必須包含長度和主體,因此聯集必須 無法將提案文件轉換為表格 (還有 我們不希望轉型成花費的時間)

相反地,只要介紹信封基式,我們就能寫下訊息 相容性保證以相同方式說明... 資料表和可延伸聯集 (開發中) 之間有一些棘手的實作細節,

序數對比字串

使用泛型需要在編譯時具備結構定義。 但可讓實作方式更有效率 (字串處理方式一律會 低於整數處理速度)。 由於 FIDL 在編譯期間已需要具備結構定義,因此希望 以上字串的序數沒有爭議

稠密與稀疏型封裝

稠密與稀疏式空間的問題可能較具爭議性。 現有的實作中包含兩個營隊:

  • Thrift 和 Protobuf 使用稀疏序數 — 欄位可為任何序數值。
  • FlatBuffers 和 Cap'n'Proto 使用密集的序數空間 — 欄位必須連續表示序數。

Protobuf 線格式 (搭配可剖析的一般 Protobuf 實作時) 到固定大小的結構體中,發生錯誤:已解碼記憶體使用的記憶體容量 與透過線傳輸的位元組數無關。 如要查看此內容,請設想含有 10, 000 (選用) int64s 欄位的訊息。 寄件者可選擇只傳送一封郵件,導致郵件只有少許位元組 但記憶體容量將近 100 KB 以這些 RPC 形式傳送許多作業,通常很容易就能阻止流量控管 實作並造成 OOM

稀疏序數的替代實作策略 (如先前的對話所述), 觸發條件會傳送 (ordinal, value) 個元組的已排序陣列。 選擇就地解碼的導入作業必須依賴資料二進位搜尋 來尋找序數 它可避免了先前提到的錯誤流量控制錯誤,但會引入可能相當大的內容 效率不彰,因為我們所執行的二元搜尋次數可能相當可觀。

Cap'n'Proto 實作了非常複雜的演算法來處理日常事務 這些情況都不複雜,因此我們想避免這類複雜作業

FlatBuffers 的線路格式與本文件所述非常相似: 利用密集序數空間提供單一陣列查詢,以找出 欄位資料 (或為空值)。

既有藝術品和參考資料

  • FlatBuffers 演算法與這個演算法類似,但這裡已進行調整 配合 FIDL 慣例
  • Protobuf (我們相信) 最初是廣受歡迎的序數/值表示法 多年來,這項計劃的可靠性無庸置疑。
  • 上述的《Cap'n'Proto》和《 Thrift》皆提供小扭曲。