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 的設計方式是讓訊息的編碼解碼作業,可以在記憶體中就地進行。

訊息編碼是標準的,也就是說,每則訊息都只有一種編碼。

圖表:訊息的兩種形式,分別是編碼 (用於傳輸) 和解碼 (用於記憶體中的消耗)。

已編碼的訊息

系統已準備好編碼訊息,可轉移至其他程序:這類訊息不含指標 (記憶體位址) 或控制代碼 (功能)。

編碼期間...

  • 訊息中的所有子物件指標都會替換成旗標,指出參照物件是否存在。
  • 系統會將訊息中的所有控制代碼擷取至相關聯的控制代碼向量,並替換為表示參照對象是否存在的旗標。

產生的編碼訊息控制代碼向量隨後可使用 zx_channel_write() 或類似的 IPC 機制傳送至其他程序。這類 IPC 還有其他限制。請參閱交易訊息

已解碼的訊息

系統已準備好解碼訊息,可在程序位址空間中使用:訊息可能包含指標 (記憶體位址) 或控制代碼 (功能)。

解碼期間:

  • 系統會使用編碼的 present 和 not-present 旗標,重建訊息中的所有子物件指標。
  • 系統會使用編碼的 present 和 not-present 旗標,從相關聯的控制代碼向量還原訊息中的所有控制代碼。

產生的解碼訊息可直接從記憶體取用。

內嵌物件

物件也可能包含內嵌物件,這些物件會匯總至包含物件的主體中,例如嵌入式結構體和結構體的固定大小陣列。

範例

在下列範例中,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 struct 都包含兩個 Point,這些 Point內嵌儲存 (因為數量固定),而每個 Point 的原始資料型別 (xy) 也會內嵌儲存。 原因相同,成員類型數量固定。

這張圖表顯示 Region 結構的記憶體配置,其中包含 Rect 結構的行外向量。

如果下層物件的大小固定,我們會使用內嵌儲存空間;如果大小可變動 (包括裝箱結構體),則會使用行外儲存空間。

類型詳細資料

本節將說明所有 FIDL 物件的編碼。

基本

  • 小端序格式儲存的值。
  • 自然對齊,
    • 每個 m 位元組基本型別都會儲存在 m 位元組的邊界。
  • 不得省略。

支援的原始型別如下:

類別 類型
布林值 bool
帶號整數 int8int16int32int64
不帶正負號的整數 uint8uint16uint32uint64
IEEE 754 浮點 float32float64
字串 (不是原始型別,請參閱「字串」)

數字型別會加上以位元為單位的型別大小。

布林類型 bool 會儲存為單一位元組,且只有 01 值。

所有浮點值都代表有效的 IEEE 754 位元模式。

圖表:以小端序表示帶正負號的整數。

圖表:顯示浮點值的小端序表示法。

列舉和位元

位元欄位和列舉會儲存為基礎原始型別 (例如 uint32)。

帳號代碼

控制碼是 32 位元整數,但會經過特殊處理。編碼以供轉移時,控點的線路表示法會替換為存在和不存在的指標,控點本身則會儲存在個別的控點向量中。解碼後,控制代碼存在指標會替換為零 (如果不存在) 或有效控制代碼 (如果存在)。

控制代碼本身「不會」從一個應用程式轉移到另一個應用程式。

就這方面而言,控制碼就像指標,會參照每個應用程式專屬的內容。控制代碼會從一個應用程式的環境移至另一個應用程式的環境。

圖表:顯示編碼/解碼期間,程序環境與控制代碼向量之間的控制代碼轉譯。

值為零時,表示選用控制代碼不存在1

如需透過 FIDL 轉移控制代碼的詳細範例,請參閱「控制代碼的生命週期」。

匯總物件

匯總物件可做為其他物件的容器。視類型而定,這些資料可能會內嵌或外嵌。

陣列

  • 固定長度的同質元素序列。
  • 元素自然對齊。
    • 陣列的對齊方式與其元素的對齊方式相同。
    • 後續每個元素都會對齊元素對齊界線。
  • 陣列的步幅正好等於元素的大小 (包括滿足元素對齊限制所需的邊框間距)。
  • 一律為必要。
  • 布林值陣列沒有特殊情況。每個 bool 元素都會照常佔用一個位元組。

陣列標示方式:

  • array<T, N>:其中 T 可以是任何 FIDL 型別 (包括陣列),而 N 是陣列中的元素數量。注意:陣列大小不得超過 232-1。詳情請參閱 RFC-0059

圖表:顯示陣列的記憶體配置,其中包含固定長度的元素序列。

向量

  • 同質元素的可變長度序列。
  • 可選用;缺少的向量和空白向量不同。
  • 可指定大小上限,例如最多 40 個元素的向量 vector<T>:40
  • 儲存為 16 位元組的記錄,包含:
    • size:64 位元無符號元素數量 注意:向量大小不得超過 232-1。詳情請參閱 RFC-0059
    • data:64 位元存在指標或指向行外元素資料的指標
  • 編碼以供轉移時,data 表示內容存在:
    • 0:缺少向量
    • UINTPTR_MAX:向量存在,資料是下一個行外物件
  • 解碼供取用時,data 是指向內容的指標:
    • 0:缺少向量
    • <valid pointer>:向量存在,資料位於指定記憶體位址
  • 布林值向量沒有特殊情況。每個 bool 元素都會照常佔用一個位元組。

向量的表示方式如下:

  • 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 個位元組的填補):

示意圖:顯示含有 int32 和 int8 欄位的結構體記憶體配置,包括填補。

具有 boolstring 欄位的結構體對齊方式為 8 個位元組 (由於 string),大小為 24 個位元組 (bool 後有 7 個位元組的填補):

示意圖:顯示具有布林值和字串欄位的結構體記憶體配置,包括邊框間距。

具有 bool 和兩個 uint8 欄位的結構體,對齊方式為 1 個位元組,大小為 3 個位元組 (沒有填補!):

示意圖:顯示結構體的記憶體配置,其中包含 bool 和兩個 uint8 欄位,沒有填補。

結構可以是:

  • 空白,也就是沒有任何欄位。這類結構的大小為 1 個位元組,對齊方式為 1 個位元組,完全等同於包含值為零的 uint8 的結構。
  • 必要條件:結構的內容會以內嵌方式儲存。
  • 選用 - 結構的內容會儲存在行外,並透過間接參照存取。

結構體的儲存空間取決於是否在參考點裝箱。

  • 非方塊結構:
    • 內容會內嵌儲存在所屬的容器型別中,因此可建構非常有效率的匯總結構。
    • 內嵌時,結構體版面配置不會變更;結構體欄位不會重新封裝,以填補容器中的間隙。
  • 方塊結構:
    • 內容會儲存在行外,並透過間接參照存取。
    • 編碼以供轉移時,儲存的參照會指出結構是否存在:
    • 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 (或以編碼格式儲存零,表示「不存在」)。
  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,系統會使用內嵌表示法

圖表:信封的內嵌表示法 (酬載 <= 4 個位元組)。

  • 如果未設定位元 0,則會使用行外表示法

圖表:信封的行外表示法 (酬載 > 4 個位元組)。

只有在酬載大小 <= 4 個位元組時,才能設定位元 0。只有在封包為零封包,或酬載大小大於 4 個位元組時,才能取消設定位元 0。

有了 num_bytesnum_handles,我們就能略過不明信封內容。

num_bytes 一律為 8 的倍數,因為超出範圍的物件會以 8 位元組對齊。

表格

  • 記錄類型,包含元素數量和指標。
  • 指標指向信封陣列,每個信封都包含一個元素。
  • 每個元素都與序數相關聯。
  • 序數是連續的,間隔會產生空白信封費用,因此不建議使用。

資料表會以宣告的名稱表示 (例如「Value」),且一律為必要:

  • 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;
};

圖表:顯示具有序數和信封的聯集記憶體配置。

交易訊息

交易訊息一律包含標頭,後面可選擇加上內文

標頭和內文都是如上所述的 FIDL 訊息,也就是資料集合。

標頭格式如下:

圖表:顯示交易訊息標頭的欄位。

  • zx_txid_t txid、交易 ID (32 位元)
    • txids with the high bit set are reserved for use by 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() 方法。

圖表:顯示 Divide 作業的方法回應訊息。

如下所示,123 + 456579。 此處的 txid 值現在為 2,這只是指派給交易的下一個交易編號。ordinal1,表示 Add(),請注意,結果需要 4 個位元組的填補,才能讓 body 物件的大小為 8 個位元組的倍數。

圖表:顯示「新增」作業的方法回應訊息。

最後,Clear() 方法與 Add()Divide() 有兩項重要差異: * 它沒有 body (也就是說,它只包含 header),且 * 它不會向介面索取回應 (txid 為零)。

這張圖表顯示 Clear 作業的方法要求訊息,沒有主體。

活動要求

舉例來說,我們 Calculator 中的 OnError() 事件就是一個事件。

伺服器會向用戶端傳送未經要求的事件要求,指出發生非同步事件,如通訊協定宣告所指定。

Calculator 範例中,我們可以想像嘗試除以零會導致 OnError() 事件在連線關閉前,傳送「除以零」狀態碼。這樣一來,用戶端就能區分連線是因錯誤而關閉,還是因其他原因關閉 (例如計算機程序異常終止)。

圖表:顯示 OnError 作業的事件要求訊息。

請注意,txid 為零 (表示這不是交易的一部分),且 ordinal4 (表示 OnError() 方法)。

body 包含事件引數,就像這些引數封裝在 struct 中一樣,與方法結果訊息相同。請注意,主體會經過填補,以維持 8 位元組對齊。

墓誌銘 (控制訊息序數 0xFFFFFFFFFFFFFFFF)

墓誌銘是序數為 0xFFFFFFFFFFFFFFFF 的事件 (txid 為零)。伺服器可能會在關閉連線前傳送墓誌銘做為最後一則訊息,指出連線關閉的原因。墓誌銘顯示後,就無法再透過該連線傳送任何訊息。墓誌銘不會從用戶端傳送至伺服器。

墓誌銘的線路表示法等同於這個 FIDL: fidl struct { error zx.Status; }; 墓誌銘日後可能會在 FIDL 中正式定義。

詳細資料

大小和對齊方式

sizeof(T) 表示 T 型別物件的大小 (以位元組為單位)。

alignof(T) 表示以位元組為單位的對齊因數,用於儲存 T 類型的物件。

FIDL 基本型別會儲存在訊息中的位移位置,這些位置是型別大小 (以位元組為單位) 的倍數。因此,對於原始型別 Talignof(T) == sizeof(T)。這就是所謂的「自然對齊」。這項屬性很實用,可滿足現代 CPU 架構的一般對齊需求。

FIDL 複雜型別 (例如結構體和陣列) 會儲存在訊息中的偏移量,這些偏移量是所有欄位最大對齊因子的倍數。因此,對於複雜型別 Talignof(T) == max(alignof(F:T)) 會針對 T 中的所有欄位 F 執行。這項屬性很實用,可滿足一般的 C 結構體封裝需求 (可使用產生程式碼中的封裝屬性強制執行)。複雜型別的大小是儲存其成員 (適當對齊) 所需的位元組總數,加上最多為型別對齊因數的填補。

無論內容為何,FIDL 主要和次要物件都會在訊息中以 8 位元組的位移量對齊。FIDL 訊息的主要物件從偏移 0 開始。次要物件是訊息中指標唯一可能的參照對象,一律從 8 的倍數的偏移量開始。(因此訊息中的所有指標都會指向 8 的倍數的偏移量)。

FIDL 內嵌物件 (內嵌於主要或次要物件中的複雜型別) 會根據型別對齊。不會強制對齊 8 位元組。

類型

注意:

  • N 表示元素數量,無論是明確陳述 (如 array<T, N>,具有 NT 類型元素的陣列),還是隱含陳述 (由 7 個元素組成的 table 會具有 N=7)。
  • 離線大小一律會填補至 8 個位元組。我們會在下方標示內容大小,不含邊框間距。
  • sizeof(T)項目下方vector
    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 alignof(T)
vector 等人 16 N * sizeof(T) 8
struct sum(sizeof(fields)) + padding 0 8
box<struct> 8 sum(sizeof(fields)) + padding 8
envelope 8 sizeof(field) 8
table 16 M * sizeof(envelope) + sum(aligned_to_8(sizeof(present fields)) 8
unionunion:optional 16 sizeof(selected variant) 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,也就是說,InlineObject 結構體內有內嵌的 table 標頭,而表格封包位於深度 1,指向深度 2 的 content_c 字串標頭,位元組則位於可透過指標間接存取的行外物件中,因此位於深度 3。

驗證

訊息驗證的目的是盡早發現線路格式錯誤,避免引發安全性或穩定性問題。

解碼從對等互連收到的訊息時,必須驗證訊息,以免不良資料傳播到服務進入點以外的地方。

將訊息編碼傳送給對等互連時,建議您驗證訊息,以便找出違反完整性限制的訊息。

為盡量減少執行階段的額外負擔,驗證作業通常應在單次傳遞訊息編碼或解碼程序中執行,這樣只需要單一遍歷。由於訊息是以深度優先的遍歷順序編碼,因此遍歷會展現良好的記憶體區域性,效率應該相當高。

如果是簡單的訊息,驗證可能非常簡單,只會進行幾項大小檢查。我們建議程式設計人員使用 FIDL 繫結程式庫代為驗證訊息,但如有需要,也可以手動驗證。

符合 FIDL 繫結的項目必須檢查下列所有完整性限制:

  • 郵件的總大小 (包括所有非內嵌子物件) 恰好等於包含該郵件的緩衝區總大小。所有子物件都已納入考量。
  • 訊息參照的控點總數完全等於控點表格的總大小。所有帳號代碼都已列出。
  • 未超過複雜物件的遞迴深度上限。
  • 所有列舉值都落在定義的範圍內。
  • 所有聯集標記值都落在定義的範圍內。
  • 僅編碼:
    • 遍歷期間遇到的所有子物件指標,都會精確參照預期會出現子物件的下一個緩衝區位置。因此,指標絕不會參照緩衝區以外的位置。
  • 僅解碼:
    • 所有參照子物件的現有和非現有旗標,都只會保留 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. 將零控制代碼定義為「沒有控制代碼」表示將線路格式結構預設初始化為全零是安全的。0 也是 ZX_HANDLE_INVALID 常數的值。

  2. 如要深入瞭解這個主題,請參閱「The Lost Art of Structure Packing」。