本文件列出 FIDL 編譯器產生的所有錯誤。
fidlc
。這個網域中的錯誤 ID 一律會以前置字串顯示
fi-
後接 4 位數字的代碼,例如 fi-0123
。
fi-0001:無效字元
勒克斯無法將字元轉換為位於 指定位置。
library test.bad.fi0001;
type ßar = struct {
value uint64;
};
請改用替代或移除字元來修正無效字元。
library test.good.fi0001;
type Foo = struct {
value uint64;
};
無效字元取決於位置。詳情請參閱 FIDL 語言規格: FIDL 語法每個部分允許的字元。
fi-0002:非預期的換行符號
字串常值不允許分割在多行之間:
library test.bad.fi0002;
const BAD_STRING string:1 = "Hello
World";
請改用逸出順序 \n
來代表換行符號:
library test.good.fi0002;
const GOOD_STRING string:11 = "Hello\nWorld";
fi-0003:逸出序列無效
Lexer 在 逸出序列。
library test.bad.fi0003;
const UNESCAPED_BACKSLASH string:2 = "\ ";
const BACKSLASH_TYPO string:1 = "\i";
const CODE_POINT_TYPO string:1 = "\Y1F604";
請改用有效字元來開始逸出序列,或是移除 非預期中的反斜線字元
library test.good.fi0003;
const ESCAPED_BACKSLASH string:2 = "\\ ";
const REMOVED_BACKSLASH string:1 = "i";
const SMALL_CODE_POINT string:3 = "\u{2604}";
const BIG_CODE_POINT string:4 = "\u{01F604}";
請參閱 FIDL 文法規格 以取得有效的逸出序列。
fi-0004:無效的十六進位數字
字串常值中的萬國碼 (Unicode) 逸出不得含有無效的十六進位數字:
library test.bad.fi0004;
const SMILE string = "\u{1G600}";
您必須以十六進位指定有效的 Unicode 碼點,範圍從 0 到 10FFFF。
每個十六進位數字必須是介於 0 到 9 之間的數字,並使用小寫英文字母,從 a
到 f
,
或從 A
到 F
的大寫字母。在本例中,G
是 F
的錯字:
library test.good.fi0004;
const SMILE string = "\u{1F600}";
fi-0005
fi-0006:預計宣告
如果 FIDL 預期收到宣告並找到其他內容,就會發生這個錯誤。
這通常是拼寫錯誤所致。有效的宣告如下:type
、alias
、
const
、using
、protocol
和service
。
library test.bad.fi0006;
cosnt SPELLED_CONST_WRONG string:2 = ":("; // Expected a declaraction (such as const).
如要修正這項錯誤,請檢查頂層宣告是否有誤,並確保 您只使用 FIDL 支援的功能。
library test.good.fi0006;
const SPELLED_CONST_RIGHT string:2 = ":)";
fi-0007:未預期的符記
剖析期間發生未預期的權杖時,就會發生這項錯誤。 一般而言,這是錯字:
library test.bad.fi0007;
alias MyType = vector<uint8>:<,256,optional>; // Extra leading comma
如要修正這個問題,一般會移除非預期的權杖 ,請提供其餘缺少的語法:
library test.good.fi0007;
alias MyType = vector<uint8>:<256, optional>;
fi-0008:未預期的符記
當 FIDL 剖析器遇到文法無效錯誤時,就會發生這個錯誤
產生下一個符記這種情況有很多種,例如列舉成員缺少 =
,
額外的權杖,例如 library = what.is.that.equals.doing.there
等。
library test.bad.unexpectedtokenofkind;
type Numbers = flexible enum {
ONE; // FIDL enums don't have a default value.
};
一般來說,如要解決這個問題,系統會新增缺少的權杖或移除 再多一個
library test.good.fi0008;
type Numbers = flexible enum {
ONE = 1;
};
如要避免發生這個錯誤,請仔細檢查 *.fidl
檔案,確保這些檔案
通常文法正確
fi-0009:未預期的 ID
這個錯誤通常是因為識別碼使用的位置不正確:
using test.bad.fi0009;
請改用正確的 ID:
library test.good.fi0009;
fi-0010:ID 無效
系統發現某個 ID 不符合有效條件
識別碼FIDL ID 可包含英數字元和底線
(特別是 A-Z
、a-z
、0-9
和 _
),以及每個 ID
開頭必須是英文字母,結尾則須為英文字母或數字。
library test.bad.fi0010a;
// Foo_ is not a valid identifier because it ends with '_'.
type Foo_ = struct {
value uint64;
};
如要解決這個問題,請變更 ID,確保僅包含有效 ID 開頭必須是英文字母,結尾則須為英文字母或數字。
library test.good.fi0010a;
type Foo = struct {
value uint64;
};
如果將多部分 (虛線) 的 ID 傳遞至 屬性。
library test.bad.fi0010b;
@foo(bar.baz="Bar", zork="Zoom")
type Empty = struct{};
如要修正這個問題,請變更屬性,讓屬性中僅使用單部分 ID。
library test.good.fi0010b;
@foo(bar="Bar", zork="Zoom")
type Empty = struct {};
fi-0011:無效的程式庫名稱元件
程式庫名稱只能使用英文字母和數字 (A-Z
、a-z
和 0-9
)。
且開頭必須是英文字母
library test.bad.fi0011.name_with_underscores;
如要修正這個問題,請確認所有程式庫名稱元件均符合相關規定。
library test.good.fi0011.namewithoutunderscores;
fi-0012:類型的版面配置類別無效
類型宣告必須指定 FIDL 已知的版面配置:
library test.bad.fi00012;
type Foo = invalid {};
有效的版面配置為 bits
、enum
、struct
、table
和 union
:
library test.good.fi0012;
type Empty = struct {};
版面配置是 FIDL 的可參數說明
類型。這是指一系列可能進一步接收的
指定形狀例如,struct
是一種版面配置
定義特定成員時會成為具體類型;而
array
是指定類型要重複時,會變得具體的版面配置
並按照指定次數來請求
版面配置全都以 FIDL 語言建構而成,使用者無法以各種方式 您可以指定自己的版面配置,也可以自行建立通用類型的範本。
fi-0013:包裝類型無效
如果傳遞給列舉或位元宣告的值並非 類別 ID 或 ID 或 ID 等。舉例來說,如果您改為提供字串值做為 "支援類型:
library test.bad.fi0013;
type TypeDecl = enum : "int32" {
FOO = 1;
BAR = 2;
};
如要修正這項錯誤,請確認列舉或位元的支援類型為類型 或 ID。
library test.good.fi0013;
type TypeDecl = enum : int32 {
FOO = 1;
BAR = 2;
};
fi-0014:屬性含空白括號的屬性
如果屬性含有括號但沒有引數,就會發生這個錯誤。
library test.bad.fi0014;
@discoverable()
protocol MyProtocol {};
如要修正這個問題,請從不含引數的屬性中移除括號,或者提交屬性 引數
library test.good.fi0014;
@discoverable
protocol MyProtocol {};
FIDL 一般不允許做為樣式選擇的屬性使用空白引數清單。
fi-0015:屬性引數必須全部命名為
為求明確,如果屬性有多個引數, 屬性的引數必須明確命名。
當屬性含有多個引數,但卻沒有引數時,就會發生這個錯誤 明確提供引數的名稱
library test.bad.fi0015;
@foo("abc", "def")
type MyStruct = struct {};
如要修正這個問題,請使用 name=value
語法提供所有引數的名稱。
library test.good.fi0015;
@foo(bar="abc", baz="def")
type MyStruct = struct {};
fi-0016:成員之前遺漏序數
聯集或資料表中的欄位缺少序數時,就會發生這項錯誤。
library test.bad.fi0016a;
type Foo = table {
x int64;
};
library test.bad.fi0016b;
type Bar = union {
foo int64;
bar vector<uint32>:10;
};
如要修正這個錯誤,請明確指定資料表或聯集的序數:
library test.good.fi0016;
type Foo = table {
1: x int64;
};
type Bar = union {
1: foo int64;
2: bar vector<uint32>:10;
};
不同於結構體,資料表和聯集的設計目的是讓回溯相容 調整內容為了實現這個目標 值,就必須是序數,才能識別資料表欄位或聯集變體。目的地: 避免混淆,並在變更過程中 或聯集,您必須明確指定序數。
fi-0017:序數超出
資料表和聯集的序數必須是有效的無正負號 32 位元整數。負面評價 當序或中度大於 4,294,967,295 時,就會導致這類錯誤。
library test.bad.fi0017a;
type Foo = table {
-1: foo string;
};
library test.bad.fi0017b;
type Bar = union {
-1: foo string;
};
如要修正這個錯誤,請確認所有序數都在允許範圍內。
library test.good.fi0017;
type Foo = table {
1: foo string;
};
type Bar = union {
1: foo string;
};
fi-0018:中等開頭必須是 1
table
和 union
成員序數值都不得為 0:
library test.bad.fi0018;
type Foo = strict union {
0: foo uint32;
1: bar uint64;
};
而是應從 1 開始編號:
library test.good.fi0018;
type Foo = strict union {
1: foo uint32;
2: bar uint64;
};
fi-0019:嚴格位元、列舉或聯集不得為空
嚴格位元、列舉或聯集不得含有零個成員:
library test.bad.fi0019;
type Numbers = strict enum {};
相反地,必須至少有一名成員:
library test.good.fi0019a;
type Numbers = flexible enum {};
或者,您也可以標示宣告 flexible
,而不是 strict
:
library test.good.fi0019b;
type Numbers = strict enum {
ONE = 1;
};
空白位元、列舉或聯集不會顯示任何資訊,因此通常不應
在 API 中使用然而,彈性資料類型是專為演變而設計,
而定義開頭是空白的彈性位元或列舉,加上
預期稍後會新增成員請務必仔細思考
定義新資料類型時使用 strict
或 flexible
。
fi-0020:無效的通訊協定成員
當通訊協定中的項目無法辨識有效時,就會發生這個錯誤 通訊協定成員,例如通訊協定中的某個項目不是通訊協定時 組合、單向方法、雙向方法或事件。
library test.bad.fi0020;
protocol Example {
NotAMethodOrCompose;
};
如要修正這項錯誤,請移除無效商品或將其轉換為正確商品 預期採用的通訊協定項目類型語法
library test.good.fi0020;
protocol Example {
AMethod();
};
fi-0021
fi-0022:無法將屬性附加至 ID
如果在宣告類型中加入屬性,就會發生這項錯誤 屬於 ID 類型的話舉例來說,將屬性放在 欄位名稱,但 struct 宣告中欄位類型之前,會將 與 屬性的類型,而不是欄位本身。如果 欄位類型是指名稱所參照的既有類型 無法套用任何屬性
library test.bad.fi0022;
type Foo = struct {
// uint32 is an existing type, extra attributes cannot be added to it just
// for this field.
data @foo uint32;
};
如果意圖是將屬性套用至欄位,則屬性應 。
屬性可以套用至宣告的類型。也就是說 struct 欄位或其他類似宣告的型別是匿名型別 而非 ID 類型,則可將屬性套用至類型。
library test.good.fi0022;
type Foo = struct {
// The foo attribute is associated with the data1 field, not the uint32
// type.
@foo
data1 uint32;
// The type of data2 is a newly declared anonymous structure, so that new
// type can have an attribute applied to it.
data2 @foo struct {};
};
fi-0023:類型宣告中的屬性
使用內嵌版面配置時 可以直接將屬性放置在版面配置之前不過,當您宣告 在頂層輸入,您無法進行以下操作:
library test.bad.fi0023;
type Foo = @foo struct {};
相反地,您必須在 type
關鍵字前面加入屬性:
library test.good.fi0023;
@foo
type Foo = struct {};
之所以採取這種做法,是因為如果允許在兩個位置使用屬性,可能會令人混淆。
fi-0024:方法參數清單的文件註解
方法參數清單不得包含文件註解:
library test.bad.fi0024;
protocol Example {
Method(/// This is a one-way method.
struct {
b bool;
});
};
library test.good.fi0024;
protocol Example {
/// This is a one-way method.
Method(struct {
b bool;
});
};
修正這個錯誤後,這個錯誤就不會再存在。 為了描述方法酬載,遷移發生保留的錯誤 而非參數清單
fi-0025:匯入群組必須位於檔案頂端
除了檔案頂端的 library
宣告以外,沒有任何
至於檔案的 using
匯入前的其他宣告,它們必須存在:
library test.bad.fi0025;
alias i16 = int16;
using dependent;
type UsesDependent = struct {
field dependent.Something;
};
如要解決這個錯誤,請將所有的 using
匯入項目直接放在區塊中
library
宣告後:
library test.good.fi0025;
using dependent;
alias i16 = int16;
type UsesDependent = struct {
field dependent.Something;
};
這項規則反映了 FIDL 團隊做出的美妙決定 如果依附元件群組經過妥善分組,且能輕鬆找出,就能更輕鬆地閱讀。
fi-0026:在文件註解區塊中加註
註解不應置於文件註解區塊中:
library test.bad.fi0026;
/// start
// middle
/// end
type Empty = struct {};
請改為將註解放在文件註解區塊的前後:
library test.good.fi0026;
// some comments above,
// maybe about the doc comment
/// A
/// multiline
/// comment!
// another comment, maybe about the struct
type Empty = struct {};
一般來說,文件註解區塊前方的註解為最佳效果 以供文件註解本身的註解
fi-0027:文件註解區塊中的空白行
文件註解區塊中不得空白行:
library test.bad.fi0027;
/// start
/// end
type Empty = struct {};
空白行應該只在文件註解的前面或後面 區塊:
library test.good.fi0027a;
/// A doc comment
type Empty = struct {};
您也可以考慮完全省略空白行:
library test.good.fi0027b;
/// A doc comment
type Empty = struct {};
fi-0028:文件註解後面必須加上宣告
文件註解不可任意浮動,如同一般註解:
library test.bad.fi0028;
type Empty = struct {};
/// bad
無論何種情況,文件註解都必須列在 FIDL 宣告之前:
library test.good.fi0028a;
/// A doc comment
type Empty = struct {};
FIDL「lowers」文件在編譯期間對 @doc
屬性的註解。事實上
您可以視需要直接以這種方式撰寫註解:
library test.good.fi0028b;
@doc("An attribute doc comment")
type Empty = struct {};
從技術面來看,獨立的文件註解無法完整呈現, 「文件」的意思也可能在語意上造成混淆什麼東西?取消喜歡 一般註解、文件註解會處理成結構化文件,以及 因此,請務必清楚說明這些 FIDL 結構所連結的是哪個 FIDL 結構。
fi-0029:資源定義必須至少有一項屬性
不允許沒有指定屬性的資源定義:
library test.bad.resourcedefinitionnoproperties;
resource_definition SomeResource : uint32 {
properties {};
};
請指定至少一個屬性:
library test.good.fi0029;
resource_definition SomeResource : uint32 {
properties {
subtype strict enum : uint32 {
NONE = 0;
};
};
};
這是與 FIDL 內部實作相關的錯誤,因此應該僅 。使用者 絕不顯示這個錯誤
其所參照的 resource_definition
宣告是 FIDL 的內部方式
定義諸如帳號代碼等資源,而且日後可能會
處理一般化工作。
fi-0030:修飾符無效
每個 FIDL 修飾符都有一組特定可用的宣告。 禁止在禁止的宣告中使用修飾符:
library test.bad.fi0030;
type MyStruct = strict struct {
i int32;
};
最佳做法是移除違規的修飾符:
library test.good.fi0030;
type MyStruct = struct {
i int32;
};
fi-0031:只有位元和列舉可以有子類型
並非所有 FIDL 版面配置都可以包含子類型:
library test.bad.fi0031;
type Foo = flexible union : uint32 {};
只有 bits
和 enum
版面配置是在基礎類型上定義。
library test.good.fi0031;
type Foo = flexible enum : uint32 {};
bits
和 enum
的版面配置不太獨特,因為他們只是
完整性 FIDL 基元的子類型受限因此
因此可以指定做為這個子類型的基礎類型。
相反地,struct
、table
和 union
版面配置可以任意大型
可能包含許多成員,因此全版面配置的子類型無法使
感覺。
fi-0032:不允許使用重複的修飾符
禁止在單一宣告中指定同一個修飾符:
library test.bad.fi0032;
type MyUnion = strict resource strict union {
1: foo bool;
};
移除重複的修飾符:
library test.good.fi0032;
type MyUnion = resource strict union {
1: foo bool;
};
fi-0033:衝突的修飾符
某些修飾符彼此互斥,且無法同時修改 相同的宣告:
library test.bad.conflictingmodifiers;
type StrictFlexibleFoo = strict flexible union {
1: b bool;
};
type FlexibleStrictBar = flexible strict union {
1: b bool;
};
您只能在一張 strict
或 flexible
修飾符中擇一使用
逐一宣告
library test.good.fi0033;
type FlexibleFoo = flexible union {
1: i int32;
};
type StrictBar = strict union {
1: i int32;
};
目前,只有 strict
和 flexible
修飾符彼此互斥
resource
修飾符沒有對等修飾符,因此具有
未套用這類限制
fi-0034:名稱衝突
兩個宣告的名稱不得重複:
library test.bad.fi0034;
const COLOR string = "red";
const COLOR string = "blue";
而是為每個宣告提供專屬的名稱:
library test.good.fi0034b;
const COLOR string = "red";
const OTHER_COLOR string = "blue";
或者,如果您不小心新增了一個宣告,請移除以下任一宣告:
library test.good.fi0034a;
const COLOR string = "red";
如要進一步瞭解如何選擇名稱,請參閱 FIDL 樣式指南。
fi-0035:正式名稱衝突
兩個宣告不得使用相同的正規名稱:
library test.bad.fi0035;
const COLOR string = "red";
protocol Color {};
雖然 COLOR
和 Color
的外觀不同,但兩者都會以
正規名稱 color
。如要取得正規名稱,只需將
將名稱原始為 snake_case
。
如要修正錯誤,請為每項宣告命名,且名稱後方應不得重複 標準化:
library test.good.fi0035;
const COLOR string = "red";
protocol ColorMixer {};
遵循 FIDL 樣式指南的命名規範,即可 降低發生錯誤的機率正規名稱衝突 在使用相同大小寫樣式的宣告之間不會發生,且 與其他樣式不同的廣告之間很少會發生 要求 (例如通訊協定名稱通常應為以「-er」結尾的名詞片語)。
FIDL 會強制執行這項規則,因為繫結產生器會將名稱轉換為 目標語言的慣用命名樣式。確保專屬標準 命名,我們保證繫結作業不會產生名稱衝突。 詳情請參閱 RFC-0040:識別碼不重複性。
fi-0036:名稱重疊
具有相同名稱的宣告不能有重疊的播映資訊:
@available(added=1)
library test.bad.fi0036;
type Color = strict enum {
RED = 1;
};
@available(added=2)
type Color = flexible enum {
RED = 1;
};
請改用 @available
屬性,確定只有
宣告的任何版本:
@available(added=1)
library test.good.fi0036;
@available(replaced=2)
type Color = strict enum {
RED = 1;
};
@available(added=2)
type Color = flexible enum {
RED = 1;
};
您也可以重新命名或移除其中一項宣告,如下所示: fi-0034。
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0037:正式名稱重疊
具有相同標準名稱的宣告不得重疊 播映時間:
@available(added=1)
library test.bad.fi0037;
const COLOR string = "red";
@available(added=2)
protocol Color {};
雖然 COLOR
和 Color
的外觀不同,但兩者都會以
正規名稱 color
。如要取得正規名稱,只需將
將名稱原始為 snake_case
。
如要修正錯誤,請為每項宣告命名,且名稱後方應不得重複 以及標準化
@available(added=1)
library test.good.fi0037;
const COLOR string = "red";
@available(added=2)
protocol ColorMixer {};
或者,您也可以變更下列任一聲明的播映資訊: fi-0036 或移除宣告。
如要進一步瞭解 FIDL 要求宣告權限的原因,請參閱 fi-0035 且沒有不重複的正規名稱
fi-0038:名稱與匯入衝突
宣告的名稱不得與使用 using
匯入的程式庫名稱相同:
library dependency;
const VALUE uint32 = 1;
library test.bad.fi0038b;
using dependency;
type dependency = struct {};
// Without this, we'd get fi-0178 instead.
const USE_VALUE uint32 = dependency.VALUE;
請改為使用 using
以不同名稱匯入程式庫 ... as
語法:
library test.good.fi0038b;
using dependency as dep;
type dependency = struct {};
const USE_VALUE uint32 = dep.VALUE;
或者,您也可以重新命名宣告以避免衝突:
library test.good.fi0038c;
using dependency;
type OtherName = struct {};
const USE_VALUE uint32 = dependency.VALUE;
如要避免這個問題,您可以在程式庫名稱中使用多個元件。適用對象
例如 Fuchsia SDK 中的 FIDL 程式庫以 fuchsia.
開頭,因此
至少兩個元件,而且不得與宣告名稱衝突。
這項錯誤是為了防止混淆。舉例來說,如果 dependency
是
加入名為 VALUE
的成員列舉,系統會不明確
dependency.VALUE
會參照該列舉成員,或參照
匯入的程式庫
fi-0039:正規名稱與匯入衝突
宣告的標準名稱不得與匯入的程式庫時相同
using
:
library dependency;
const VALUE uint32 = 1;
library test.bad.fi0039b;
using dependency;
type Dependency = struct {};
// Without this, we'd get fi-0178 instead.
const USE_VALUE uint32 = dependency.VALUE;
雖然 dependency
和 Dependency
的外觀不同,但兩者
由「標準」名稱 dependency
表示。這樣您就能取得正規名稱
做法是將原始名稱轉換為 snake_case
。
如要修正錯誤,請使用 using
以不同名稱匯入程式庫 ...
as
語法:
library test.good.fi0039b;
using dependency as dep;
type Dependency = struct {};
const USE_VALUE uint32 = dep.VALUE;
或者,您也可以重新命名宣告以避免衝突:
library test.good.fi0039c;
using dependency;
type OtherName = struct {};
const USE_VALUE uint32 = dependency.VALUE;
請參閱 fi-0038,瞭解出現這個錯誤的原因,以及如何避免。
如要進一步瞭解 FIDL 要求宣告權限的原因,請參閱 fi-0035 且沒有不重複的正規名稱
fi-0040:檔案對資料庫名稱有異議
程式庫可由多個檔案組成,但每個檔案都必須相同 名稱:
library test.bad.fi0040a;
library test.bad.fi0040b;
確認程式庫使用的所有檔案都使用相同的名稱:
library test.good.fi0040;
library test.good.fi0040;
多檔案程式庫的建議慣例是另外建立
空白的 overview.fidl 檔案,以做為主要的「項目」
點」到程式庫中overview.fidl
檔案也是合適的位置
加入以程式庫限定範圍的 @available
平台
規格。
fi-0041:多個名稱相同的程式庫
傳遞至 fidlc
的每個程式庫都必須具有不重複的名稱:
library test.bad.fi0041;
library test.bad.fi0041;
確認所有程式庫的名稱皆不重複:
library test.good.fi0041a;
library test.good.fi0041b;
這項錯誤通常是因為在fidlc
不正確。構成每個必要程式庫所需的程式庫的組成檔案
編譯 (即編譯的程式庫以及其所有轉換程序)
依附元件) 必須以單一空格分隔的檔案清單的形式提供
透過 --files
引數指定這類旗標。常見錯誤是
嘗試傳遞單一 --files
清單中所有程式庫的檔案。
fi-0042:重複的程式庫匯入作業
無法多次匯入依附元件:
library test.bad.fi0042a;
type Bar = struct {};
library test.bad.fi0042b;
using test.bad.fi0042a;
using test.bad.fi0042a; // duplicated
type Foo = struct {
bar test.bad.fi0042a.Bar;
};
確認每個依附元件僅匯入一次:
library test.good.fi0042a;
type Bar = struct {};
library test.good.fi0042b;
using test.good.fi0042a;
type Foo = struct {
bar test.good.fi0042a.Bar;
};
值得注意的是,FIDL 不支援匯入不同版本的
同一個程式庫系統會為整個 fidlc
解析 @available
版本
透過 --available
旗標進行編譯,這表示兩個程式庫
所有已編譯的依附元件及其所有依附元件都必須使用相同的版本
程式碼編譯作業
fi-0043:衝突的程式庫匯入
匯入的程式庫不得建立別名 與其他匯入程式庫的非別名名稱衝突:
library test.bad.fi0043a;
type Bar = struct {};
// This library has a one component name to demonstrate the error.
library fi0043b;
type Baz = struct {};
library test.bad.fi0043c;
using test.bad.fi0043a as fi0043b; // conflict
using fi0043b;
type Foo = struct {
a fi0043b.Bar;
b fi0043b.Baz;
};
選擇其他別名,即可解決名稱衝突問題:
library test.good.fi0043a;
type Bar = struct {};
library fi0043b;
type Baz = struct {};
library test.good.fi0043c;
using test.good.fi0043a as dep;
using fi0043b;
type Foo = struct {
a dep.Bar;
b fi0043b.Baz;
};
fi-0044:衝突的程式庫匯入別名
匯入的程式庫不得建立別名 與指派給其他匯入程式庫的別名相衝突:
library test.bad.fi0044a;
type Bar = struct {};
library test.bad.fi0044b;
type Baz = struct {};
library test.bad.fi0044c;
using test.bad.fi0044a as dep;
using test.bad.fi0044b as dep; // conflict
type Foo = struct {
a dep.Bar;
b dep.Baz;
};
選擇不含衝突的別名,解決名稱衝突問題:
library test.good.fi0044a;
type Bar = struct {};
library test.good.fi0044b;
type Baz = struct {};
library test.good.fi0044c;
using test.good.fi0044a as dep1;
using test.good.fi0044b as dep2;
type Foo = struct {
a dep1.Bar;
b dep2.Baz;
};
fi-0045:使用宣告的屬性遭到禁止
屬性無法附加至 using
宣告:
library test.bad.fi0045a;
type Bar = struct {};
library test.bad.fi0045b;
/// not allowed
@also_not_allowed
using test.bad.fi0045a;
type Foo = struct {
bar test.bad.fi0045a.Bar;
};
請移除這項屬性來修正錯誤:
library test.good.fi0045a;
type Bar = struct {};
library test.good.fi0045b;
using test.good.fi0045a;
type Foo = struct {
bar test.good.fi0045a.Bar;
};
這項限制也適用於 /// ...
份文件註解,而且這些內容僅限於
@doc("...")
屬性的語法糖。
fi-0046:不明的程式庫
在大多數情況下,這是因為依附元件有錯字,或建構系統沒有提供。 如果相關依附元件是刻意未使用,則必須移除相關的使用行:
library test.bad.fi0046;
using dependent; // unknown using.
type Foo = struct {
dep dependent.Bar;
};
請確認所有匯入內容都會透過建構系統新增為程式庫的依附元件。
library test.good.fi0046;
type Foo = struct {
dep int64;
};
fi-0047
fi-0048:選擇性的資料表成員
資料表成員類型不得為 optional
:
library test.bad.fi0048;
type Foo = table {
// Strings can be optional in general, but not in table member position.
1: t string:optional;
};
從所有成員中移除 optional
限制:
library test.good.fi0048;
type Foo = table {
1: t string;
};
資料表成員一律為選用項目,因此針對成員的基礎類型指定此資訊是多餘的。
表格成員一律可以選用,因為線路上,每個資料表成員都以一個向量項目表示。 這個向量一定代表表格中所有已知的欄位,因此每個省略的資料表成員都會 視為空值信封 - 與省略的選擇性類型表示法完全相同。
fi-0049:非必要的工會成員
無法選擇是否加入聯合成員:
library test.bad.fi0049;
type Foo = strict union {
// Strings can be optional in general, but not in unions.
1: bar string:optional;
};
移除 optional
限制:
library test.good.fi0049;
type Foo = strict union {
1: bar string;
};
FIDL 不允許將聯集成員設為選用,因為這可能會導致透過許多方式表示相同值。
例如,包含三個選用成員的聯集會有 6 個狀態 (每個成員 2 個)。而應該使用
第四個成員類型為 struct {}
,或將整體聯集設為選用 Foo:optional
。
fi-0050:禁止使用已淘汰的結構體預設語法
先前允許為 struct
成員設定預設值 FIDL:
library test.bad.fi0050;
type MyStruct = struct {
field int64 = 20;
};
自 RFC-0160:移除 FIDL 結構體預設值的支援,這個行為將 不允許:
library test.good.fi0050;
type MyStruct = struct {
field int64;
};
無法使用 struct
成員的預設值。使用者應進行設定
應用程式邏輯的預設值
少數舊版使用者已可使用這個語法 列在編譯器內建的許可清單中,但沒有任何新的例外狀況 您想加入這份清單中遷移這些使用者後,這項功能 將從 FIDL 中永久移除。
fi-0051:不明的相依程式庫
使用不明資料庫的符號時,就會發生這個錯誤。
library test.bad.fi0051;
type Company = table {
1: employees vector<unknown.dependent.library.Person>;
2: name string;
};
如要修正這個問題,請使用宣告來匯入缺少的相依程式庫。
library known.dependent.library;
type Person = table {
1: age uint8;
2: name string;
};
library test.good.fi0051;
using known.dependent.library;
type Company = table {
1: employees vector<known.dependent.library.Person>;
2: name string;
};
這個錯誤通常發生於 fidlc
指令列叫用
格式錯誤。如果您確定存在未知資料庫,且
必須能解決;請務必傳送相依程式庫的檔案
會透過傳遞至 --files
旗標的空格分隔清單,正確定義這些元素。
fi-0052:找不到名稱
如果您使用 FIDL 編譯器找不到的名稱,就會發生這個錯誤。
library test.bad.fi0052;
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
}) error ParsingError; // ParsingError doesn't exist.
};
如要修正這個問題,請移除找不到的名稱:
library test.good.fi0052a;
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
或定義找不到的名稱:
library test.good.fi0052b;
type ParsingError = flexible enum {
UNEXPECTED_EOF = 0;
};
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
}) error ParsingError;
};
fi-0053:無法參照成員
當您參照的成員不是 bits
或 enum
項目時,就會發生這個錯誤。
library test.bad.fi0053a;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
const JOHNS_NAME Person.name = "John Johnson"; // Cannot refer to member of struct 'Person'.
library test.bad.fi0053b;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
type Cat = struct {
name string;
age Person.birthday; // Cannot refer to member of struct 'Person'.
};
如要修正這項錯誤,請變更為已命名的類型:
library test.good.fi0053a;
type Person = struct {
name string;
birthday struct {
year uint16;
month uint8;
day uint8;
};
};
const JOHNS_NAME string = "John Johnson";
或擷取成員的類型:
library test.good.fi0053b;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday Date;
};
type Cat = struct {
name string;
age Date;
};
fi-0054:位元/列舉成員無效
如果在未事先定義的情況下參照 enum
或 bits
成員,就會發生這項錯誤。
library test.bad.fi0054;
type Enum = enum {
foo_bar = 1;
};
const EXAMPLE Enum = Enum.FOO_BAR;
為避免發生這個錯誤,請確認您先前已宣告參照 成員的值。這些值有大小寫之分。
library test.good.fi0054;
type Enum = enum {
foo_bar = 1;
};
const EXAMPLE Enum = Enum.foo_bar;
fi-0055:已淘汰的參考檔案無效
當使用帶有 type
或 const
的參照
@available
屬性不相容。這通常是因為您在
淘汰了較新版本的 types
或 consts
。
@available(added=1)
library test.bad.fi0055;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(added=3)
type Config = table {
// RGB is deprecated in version 2.
1: color RGB;
};
如要修正這個錯誤,請使用未淘汰的 type
或 const
:
@available(added=1)
library test.good.fi0055;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(added=3)
type Config = table {
// Using a non-deprecated type.
1: color Color;
};
fi-0056:參照給已淘汰的其他平台無效
當您使用其他 type
或 const
的參照時,就會發生這個錯誤
@available
屬性不相容的平台。這種情況通常發生在
使用已淘汰的 types
或 consts
@available(platform="foo", added=1)
library test.bad.fi0056a;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(platform="bar", added=2)
library test.bad.fi0056b;
using test.bad.fi0056a;
@available(added=3)
type Config = table {
// RGB is deprecated in version 2.
1: color test.bad.fi0056a.RGB;
};
如要修正這個錯誤,請使用未淘汰的 type
或 const
:
@available(platform="foo", added=1)
library test.good.fi0056a;
@available(added=1, deprecated=2, note="use Color instead")
alias RGB = array<uint8, 3>;
@available(added=2)
type Color = struct {
r uint8;
g uint8;
b uint8;
a uint8;
};
@available(platform="bar", added=2)
library test.good.fi0056b;
using test.good.fi0056a;
@available(added=2)
type Config = table {
// Change to use a non-deprecated type.
1: color test.good.fi0056a.Color;
};
fi-0057:包含月經週期
有許多情況可能導致這個問題發生,但所有內容 基本上,這些變數基本上會對應到 無法解決這項錯誤最簡單的形式,就是當型別或通訊協定 在自己的定義中直接提及本身:
library test.bad.fi0057c;
type MySelf = struct {
me MySelf;
};
發生更複雜的故障情況時,可能會轉換類型或通訊協定 至少透過一種間接層級提及自己:
library test.bad.fi0057a;
type Yin = struct {
yang Yang;
};
type Yang = struct {
yin Yin;
};
library test.bad.fi0057b;
protocol Yin {
compose Yang;
};
protocol Yang {
compose Yin;
};
只要在 循環,因為這樣可讓循環「中斷」編碼/解碼 時間:
library test.good.fi0057;
type MySelf = struct {
me box<MySelf>;
};
library test.bad.fi0057d;
type MySelf = table {
1: me MySelf;
};
系統不得使用信封無法解鎖的遞迴類型,因為
就無法編碼在上方第一個範例中,編碼 MySelf
會
需要對 MySelf
的執行個體進行初次編碼,而這反過來又需要
編碼是 MySelf
的例項、廣告啟動。這個問題的解決方法是
新增「休息時間」在這個鏈中,使用者可選擇自己
為另一個 MySelf
的巢狀例項編碼,或對空值信封進行編碼
若是沒有進一步資料
fi-0058:參照編譯器產生的酬載名稱
匿名方法酬載具有由 FIDL 自動產生的名稱 ,讓產生的後端程式碼使用者能夠參照他們所建構的類型 物件圖示請在 *.fidl 檔案本身參照這些類型 不過,我們也禁止:
library test.bad.fi0058;
protocol MyProtocol {
strict MyInfallible(struct {
in uint8;
}) -> (struct {
out int8;
});
strict MyFallible(struct {
in uint8;
}) -> (struct {
out int8;
}) error flexible enum {};
strict -> MyEvent(struct {
out int8;
});
};
type MyAnonymousReferences = struct {
a MyProtocolMyInfallibleRequest;
b MyProtocolMyInfallibleResponse;
c MyProtocolMyFallibleRequest;
d MyProtocol_MyFallible_Result;
e MyProtocol_MyFallible_Response;
f MyProtocol_MyFallible_Error;
g MyProtocolMyEventRequest;
};
如要直接參照酬載類型,則應擷取 酬載轉換為自身的具名類型宣告:
library test.good.fi0058;
type MyRequest = struct {
in uint8;
};
type MyResponse = struct {
out int8;
};
type MyError = flexible enum {};
protocol MyProtocol {
strict MyInfallible(MyRequest) -> (MyResponse);
strict MyFallible(MyRequest) -> (MyResponse) error MyError;
strict -> MyEvent(MyResponse);
};
type MyAnonymousReferences = struct {
a MyRequest;
b MyResponse;
c MyRequest;
// There is no way to explicitly name the error result union.
// d MyProtocol_MyFallible_Result;
e MyResponse;
f MyError;
g MyResponse;
};
所有 FIDL 方法和事件都會保留 [PROTOCOL_NAME][METHOD_NAME]Request
匿名要求酬載的名稱成效卓越、難以理解的雙向方法
此外,請額外預訂 [PROTOCOL_NAME][METHOD_NAME]Response
。雙向方法
可彈性或可捨棄預訂的廣告:
[PROTOCOL_NAME]_[METHOD_NAME]_Result
[PROTOCOL_NAME]_[METHOD_NAME]_Response
[PROTOCOL_NAME]_[METHOD_NAME]_Error
基於歷史原因,這些名稱使用底線與其他名稱不同。
fi-0059:常數類型無效
並非所有類型都可用於 const
宣告:
library test.bad.fi0059;
const MY_CONST string:optional = "foo";
請盡可能轉換為允許的類型:
library test.good.fi0059;
const MY_CONST string = "foo";
只有 FIDL 原始物件 (bool
、int8
、int16
、int32
、int64
、uint8
、
uint16
、uint32
、uint64
、float32
、float64
,以及非選用 string
類型可用於 const
宣告的左側。
fi-0060:無法解析常數值
常數值必須可解析為已知值:
library test.bad.fi0060;
const MY_CONST bool = optional;
請確認使用的常數是有效值:
library test.good.fi0060;
const MY_CONST bool = true;
這個錯誤通常發生了其他錯誤, 無法解析預期常數的特性
fi-0061:或者在非原始值的運算子
二元或運算子只能用於基本:
library test.bad.fi0061;
const HI string = "hi";
const THERE string = "there";
const OR_OP string = HI | THERE;
建議將目前操作的資料顯示為 bits
列舉:
library test.good.fi0061;
type MyBits = flexible bits {
HI = 0x1;
THERE = 0x10;
};
const OR_OP MyBits = MyBits.HI | MyBits.THERE;
fi-0062:不允許使用新類型
來自 RFC-0052:類型別名和新類型的新類型 已完全實作,目前無法使用:
library test.bad.fi0062;
type Matrix = array<float64, 9>;
同時,您可以透過使用 單一元素:
library test.good.fi0062a;
type Matrix = struct {
elements array<float64, 9>;
};
或者,您也可以定義別名,但請注意,此類型與新類型 且沒有任何型別安全,也就是說,可與叢集的 基礎類型):
library test.good.fi0062b;
alias Matrix = array<float64, 9>;
fi-0063:預期值但已取得類型
const
宣告的右側必須解析為常數值。
非類型:
library test.bad.fi0063;
type MyType = struct {};
const MY_CONST uint32 = MyType;
確認右側為值:
library test.good.fi0063;
const MY_VALUE uint32 = 8;
const MY_CONST uint32 = MY_VALUE;
fi-0064:位元或列舉值類型不正確
使用 bits
或 enum
變數做為 const
宣告中的值時,
bits
/enum
值的類型必須與
const 宣告:
library test.bad.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST MyEnum = OtherEnum.VALUE;
其中一種解決方法是變更 const
宣告的類型,以符合
儲存的值:
library test.good.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST OtherEnum = OtherEnum.VALUE;
或者,您也可以選取其他與 const
相符的值
宣告的類型:
library test.good.fi0064;
type MyEnum = enum : int32 {
VALUE = 1;
};
type OtherEnum = enum : int32 {
VALUE = 5;
};
const MY_CONST MyEnum = MyEnum.VALUE;
fi-0065:無法將值轉換為預期的類型
常數值必須是適用於該位置的類型
發生這項錯誤最常見的原因是 const
宣告的值
不符合敘述類型:
library test.bad.fi0065a;
const MY_CONST bool = "foo";
如果使用正確定義的 const
值,仍會發生問題
基礎類型的無效位置:
library test.bad.fi0065b;
const ONE uint8 = 0x0001;
const TWO_FIFTY_SIX uint16 = 0x0100;
const TWO_FIFTY_SEVEN uint8 = ONE | TWO_FIFTY_SIX;
此外,FIDL 的「官方」會檢查其引數 比對結構定義這些引數本身都是常數值,因此 可能會出現類似類型不符的情況
library test.bad.fi0065c;
protocol MyProtocol {
@selector(3840912312901827381273)
MyMethod();
};
在這些情況下,解決辦法是只使用預期的值
輸入可接受 const
值的位置。上述案例變為
分別為:
library test.good.fi0065a;
const MY_CONST string = "foo";
library test.good.fi0065b;
const ONE uint8 = 0x0001;
const TWO_FIFTY_SIX uint16 = 0x0100;
const TWO_FIFTY_SEVEN uint16 = ONE | TWO_FIFTY_SIX;
library test.good.fi0065c;
protocol MyProtocol {
@selector("MyOldMethod")
MyMethod();
};
fi-0066:常數溢位類型
常數值不能落在其基礎範圍外 類型:
library test.bad.fi0066;
const NUM uint64 = -42;
變更值以符合類型 範圍:
library test.good.fi0066a;
const NUM uint64 = 42;
或者,變更為 以配合目前溢位的 值:
library test.good.fi0066b;
const NUM int64 = -42;
這個錯誤只與 FIDL 的數字類型有關,且所有
範圍來自 C++ std::numeric_limits
介面,而且如下:
類型 | 最小值 | 上限 |
---|---|---|
int8 |
-128 | 127 |
int16 |
32768 | 32767 |
int32 |
2147483648 | 2147483647 |
int64 |
9223372036854775808 | 9223372036854775807 |
uint8 |
0 | 255 |
uint16 |
0 | 65536 |
uint32 |
0 | 4294967295 |
uint64 |
0 | 18446744073709551615 |
float32 |
-3.40282e+38 | 3.40282e+38 |
float64 |
-1.79769e+308 | 1.79769e+308 |
fi-0067:位元成員必須是兩個方的權力
bits
宣告中所有成員的值,不得為
不等於兩個的次方:
library test.bad.fi0067;
type NonPowerOfTwo = bits : uint64 {
THREE = 3;
};
成員值應一律為兩個次方:
library test.good.fi0067a;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
要避免因這項限製而耗費過多心力,最簡單的方法就是只使用位元 使用遮罩,而非十進位數字:
library test.good.fi0067b;
type Life = bits {
A = 0b000010;
B = 0b001000;
C = 0b100000;
};
bits
結構代表「位元陣列」。這份
節省記憶體,表示一系列布林標記序列。由於每個
bits
宣告的成員會對應至其基礎部分
該對應的值就必須清楚識別
指派的無正負號整數。
fi-0068:彈性列舉有保留的未知值
當您定義列舉成員,且其值與 保留的未知值。
彈性列舉可能會保留 FIDL 結構定義無法辨識的值。此外,
彈性列舉一律會保留一個視為不明的值。
根據預設,此值是
該列舉的基本整數類型 (例如 uint8
為 255
)。
library test.bad.fi0068;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
MAX = 255;
};
如要修正錯誤,你可以移除成員或變更其值:
library test.good.fi0068a;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
};
library test.good.fi0068b;
type Foo = flexible enum : uint8 {
ZERO = 0;
ONE = 1;
MAX = 254;
};
最後,如果您在將 strict
列舉轉換為
flexible
列舉,您可以使用 @unknown
屬性來指定數值
值做為未知的值。請見 @unknown
。
fi-0069:位元必須使用無正負號的積分子類型
使用帶正負號的數值做為 bits
宣告的基礎類型為
禁止:
library test.bad.fi0069;
type Fruit = bits : int64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
請改用下列任一種:uint8
、uint16
、uint32
或 uint64
:
library test.good.fi0069;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
與 enum
宣告不同,後者同時允許帶正負號與無正負號整數 (請參閱:
fi-0070),bits
宣告則僅允許後者。這是
因為每位 bits
成員都代表
位元陣列 (這就是 fi-0067 的原因
存在)。最明確表示這是一個無正負號整數。
無正負號整數的二進位表示法會直接對應至單一位元 (2 到
索引的次方),而正負號整數中的負數則幾乎
必須根據雙重補給的機制,選擇多個位元
表示法。
fi-0070:列舉必須使用積分子類型
使用非積分數字 float32
或 float64
做為基礎類型
禁止使用 enum
宣告:
library test.bad.fi0070;
type MyEnum = enum : float64 {
ONE_POINT_FIVE = 1.5;
};
請改用下列任一種格式:int8
、int16
、int32
、int64
、uint8
、
uint16
、uint32
或 uint64
:
library test.good.fi0070;
type MyEnum = enum : uint64 {
ONE = 1;
};
fi-0071:嚴格列舉成員不允許使用未知屬性
strict enum
不得包含使用 @unknown
加註的任何成員
屬性:
library test.bad.fi0071;
type MyEnum = strict enum : int8 {
@unknown
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
如要繼續使用 @unknown
屬性,請變更為 flexible enum
:
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
否則,請直接移除該屬性,以維持 strict enum
:
library test.good.fi0071;
type MyEnum = strict enum : int8 {
UNKNOWN = 0;
FOO = 1;
MAX = 127;
};
@unknown
屬性的用途是
順利轉換 strict enum
時,有使用者定義的未知值。
轉換為 flexible enum
,且其中已知及處理的值不明
FIDL。在上述範例中,我應該使用另一個
正確用法
fi-0072:只有列舉成員可以攜帶未知的屬性
禁止利用 @unknown
屬性容納多位 enum
成員:
library test.bad.fi0072;
type MyEnum = flexible enum : uint8 {
@unknown
UNKNOWN = 0;
@unknown
OTHER = 1;
};
只選擇特定網域為「未知」的成員,並加上註解 值:
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
OTHER = 1;
};
@unknown
屬性的用途是
順利轉換 strict enum
時,有使用者定義的未知值。
轉換為 flexible enum
,且其中已知及處理的值不明
FIDL:
library test.good.fi0072;
type MyEnum = strict enum : int8 {
UNKNOWN = 0;
OTHER = 1;
};
library test.good.fi0071a;
type MyEnum = flexible enum : int8 {
@unknown
UNKNOWN = 0;
OTHER = 1;
};
fi-0073:撰寫非通訊協定
只有通訊協定可用於 compose
陳述式:
library test.bad.fi0073;
type MyStruct = struct {};
protocol MyProtocol {
compose MyStruct;
};
請確定您所參照的名稱指向通訊協定:
library test.good.fi0073;
protocol MyOtherProtocol {};
protocol MyProtocol {
compose MyOtherProtocol;
};
fi-0074:方法酬載所使用的版面配置無效
您只能使用 struct
、table
或 union
版面配置來描述方法
酬載:
library test.bad.fi0074;
protocol MyProtocol {
MyMethod(enum {
FOO = 1;
});
};
請改用下列其中一種版面配置:
library test.good.fi0074;
protocol MyProtocol {
MyMethod(struct {
foo bool;
});
};
fi-0075:用於方法酬載的原始無效
基元無法用作方法酬載:
library test.bad.fi0075;
protocol MyProtocol {
MyMethod(uint32);
};
請改用 struct
、table
或 union
版面配置類型:
library test.good.fi0075;
protocol MyProtocol {
MyMethod(struct {
wrapped_in_struct uint32;
});
};
所需的酬載真的只是基本值的情況,
演變時不是問題,只要將值納入 struct
版面配置中,
所產生的酬載,大小會與預期值相同。
fi-0076
fi-0077:互動酬載不得為空白結構體
方法或事件中的酬載不得為空白的結構體:
library test.bad.fi0077a;
protocol Test {
MyMethod(struct {}) -> (struct {});
};
library test.bad.fi0077b;
protocol Test {
-> MyEvent(struct {});
};
可表示特定的要求/回應並未
刪除任何資訊,刪除空白的結構,將 ()
留在該位置:
library test.good.fi0077a;
protocol Test {
MyMethod() -> ();
};
library test.good.fi0077b;
protocol Test {
-> MyEvent();
};
空白結構體無法擴充,且線上線需要 1 位元組。自 FIDL 支援不含有效負載的互動,以這種方式使用空白結構, 大幅提高效率因此不接受。
fi-0078
fi-0079
fi-0080:產生的零序數
這個錯誤不應發生。如果你能做到這點 恭喜,您可能已經破損 SHA-256!
但開玩笑,如果 fidlc 編譯器產生序數值,就會發生這個錯誤 值。由於這個錯誤一定不會發生,因此如果確實顯示,您可能已在 FIDL 編譯器。如果發生這種情況,請向 Issue Tracker 回報問題。
fi-0081:重複的方法序數
這個錯誤通常會在使用 @selector
屬性時發生
使兩個方法名稱產生相同序數
library test.bad.fi0081;
protocol Parser {
ParseLine();
// Multiple methods with the same ordinal...
@selector("ParseLine")
ParseOneLine();
};
如要修正這個問題,請更新方法名稱或選取器,確保不會衝突。
library test.good.fi0081;
protocol Parser {
ParseLine();
@selector("Parse1Line")
ParseOneLine();
};
如果出現 SHA-256 衝突,也可能會發生這個錯誤,但 基本上為零如果你確信選取器沒有錯, 仍然遇到這個錯誤,您可能在 FIDL 編譯器中發現了錯誤。 如果發生這種情況,請向 Issue Tracker 回報問題。
fi-0082:選取器值無效
如果 @selector 使用的值無效,就會發生這個錯誤。 最常見的原因是錯字。選取器必須是獨立項目 方法名稱或完整方法名稱
library test.bad.fi0082;
protocol Parser {
@selector("test.old.fi0082.Parser.Parse")
Parse();
};
如要修正這個問題,請將選取器改成有效的獨立或完全 合格方法名稱:
library test.good.fi0082;
protocol Parser {
@selector("test.old.fi0082/Parser.Parse")
Parse();
};
fi-0083:fuchsia.io
必須使用明確的序數
用於自動將 fuchsia.io
序重新命名的 FIDL 編譯器
fuchsia.io1
。這項魔法設計是為了讓您更輕鬆地
fuchsia.io2
,方法是讓方法的 io2
版本具有「normal」
序數然而,這個系統的反應有點太神奇了,因此現在
即可手動提供 fuchsia.io
的序數。
library fuchsia.io;
protocol SomeProtocol {
SomeMethod();
};
如要修正這個問題,請使用 fuchsia.io1
手動提供選取器,
程式庫名稱,讓 fuchsia.io
名稱用於 io2。
library fuchsia.io;
protocol SomeProtocol {
@selector("fuchsia.io1/SomeProtocol.SomeMethod")
SomeMethod();
};
fi-0084:方法酬載結構中不允許有預設成員
做為付款方式付款的結構體無法指定預設成員:
library test.bad.fi0084;
type MyStruct = struct {
@allow_deprecated_struct_defaults
a bool = false;
};
protocol MyProtocol {
MyMethod(MyStruct) -> (MyStruct);
};
從相關的 struct
宣告中移除預設成員:
library test.good.fi0084;
type MyStruct = struct {
a bool;
};
protocol MyProtocol {
MyMethod(MyStruct) -> (MyStruct);
};
fi-0085
fi-0086
fi-0087
fi-0088:服務成員不可為選用
如果您將服務成員標示為 optional
,就會發生這個錯誤。標示
服務成員一律是「optional
」,因此無法允許服務成員
選用。
library test.bad.fi0088;
protocol Sorter {
Sort(struct {
input vector<int32>;
}) -> (struct {
output vector<int32>;
});
};
service SortService {
quicksort client_end:<Sorter, optional>;
mergesort client_end:<Sorter, optional>;
};
如要修正這個問題,請移除選用子句:
library test.good.fi0088;
protocol Sorter {
Sort(struct {
input vector<int32>;
}) -> (struct {
output vector<int32>;
});
};
service SortService {
quicksort client_end:Sorter;
mergesort client_end:Sorter;
};
fi-0089
fi-0090
fi-0091:結構體成員類型無效
當您嘗試為不支援的設定方式設定預設結構值時,就會發生這個錯誤 類型。僅允許數字和布林值類型設定預設結構值。
library test.bad.fi0091;
type Person = struct {
@allow_deprecated_struct_defaults
name string:optional = "";
};
如要修正這個問題,請移除預設值:
library test.good.fi0091;
type Person = struct {
@allow_deprecated_struct_defaults
name string:optional;
};
fi-0092:表格序數過大
FIDL 資料表序數不得大於 64:
library test.bad.fi0092;
type Table64thField = table {
1: x int64;
};
type Example = table {
1: v1 int64;
2: v2 int64;
3: v3 int64;
4: v4 int64;
5: v5 int64;
6: v6 int64;
7: v7 int64;
8: v8 int64;
9: v9 int64;
10: v10 int64;
11: v11 int64;
12: v12 int64;
13: v13 int64;
14: v14 int64;
15: v15 int64;
16: v16 int64;
17: v17 int64;
18: v18 int64;
19: v19 int64;
20: v20 int64;
21: v21 int64;
22: v22 int64;
23: v23 int64;
24: v24 int64;
25: v25 int64;
26: v26 int64;
27: v27 int64;
28: v28 int64;
29: v29 int64;
30: v30 int64;
31: v31 int64;
32: v32 int64;
33: v33 int64;
34: v34 int64;
35: v35 int64;
36: v36 int64;
37: v37 int64;
38: v38 int64;
39: v39 int64;
40: v40 int64;
41: v41 int64;
42: v42 int64;
43: v43 int64;
44: v44 int64;
45: v45 int64;
46: v46 int64;
47: v47 int64;
48: v48 int64;
49: v49 int64;
50: v50 int64;
51: v51 int64;
52: v52 int64;
53: v53 int64;
54: v54 int64;
55: v55 int64;
56: v56 int64;
57: v57 int64;
58: v58 int64;
59: v59 int64;
60: v60 int64;
61: v61 int64;
62: v62 int64;
63: v63 int64;
// The 64th field of a table must be another table, otherwise it will cause
// fi-0093: Max Ordinal In Table Must Be Table.
64: v64 Table64thField;
65: v65 int64;
};
為了支援超過 64 種一般的成長,FIDL 需要 做為另一個資料表凡是超過 64 個表格欄位,都必須放在巢狀結構中 表格。
library test.good.fi0092;
type Table64thField = table {
1: x int64;
// Any fields beyond 64 of table Example must be move to the nested table in
// ordinal 64 of Example.
2: v65 int64;
};
type Example = table {
1: v1 int64;
2: v2 int64;
3: v3 int64;
4: v4 int64;
5: v5 int64;
6: v6 int64;
7: v7 int64;
8: v8 int64;
9: v9 int64;
10: v10 int64;
11: v11 int64;
12: v12 int64;
13: v13 int64;
14: v14 int64;
15: v15 int64;
16: v16 int64;
17: v17 int64;
18: v18 int64;
19: v19 int64;
20: v20 int64;
21: v21 int64;
22: v22 int64;
23: v23 int64;
24: v24 int64;
25: v25 int64;
26: v26 int64;
27: v27 int64;
28: v28 int64;
29: v29 int64;
30: v30 int64;
31: v31 int64;
32: v32 int64;
33: v33 int64;
34: v34 int64;
35: v35 int64;
36: v36 int64;
37: v37 int64;
38: v38 int64;
39: v39 int64;
40: v40 int64;
41: v41 int64;
42: v42 int64;
43: v43 int64;
44: v44 int64;
45: v45 int64;
46: v46 int64;
47: v47 int64;
48: v48 int64;
49: v49 int64;
50: v50 int64;
51: v51 int64;
52: v52 int64;
53: v53 int64;
54: v54 int64;
55: v55 int64;
56: v56 int64;
57: v57 int64;
58: v58 int64;
59: v59 int64;
60: v60 int64;
61: v61 int64;
62: v62 int64;
63: v63 int64;
64: v64 Table64thField;
};
資料表中的每個欄位都會產生 FIDL 信封的負擔,以便 選填欄位如此一來,資料表的每個欄位都會顯示 並支援增減資料表來改良資料表,但 比結構體耗用更多記憶體負擔
一般來說,您可以藉由避免 精細的小型欄位您可以改將一些元素分組 您預期需要同時在結構體中新增或移除 這些欄位可做為資料表欄位使用這麼做可以減輕負擔,並避免 因此往往難以追求變革。
這成為 RFC-0132: FIDL 資料表大小的錯誤 上限, 目的是避免使用者不小心將 大型資料表。在結構定義中,這所需的額外費用並不明顯,尤其是有 只有幾個欄位 (和通常較大),或有許多欄位 一次只能使用幾個範本
fi-0093:表格的序數上限必須為表格
FIDL 資料表中的第 64 個成員類型必須是資料表:
library test.bad.fi0093;
type Example = table {
1: v1 int64;
2: v2 int64;
3: v3 int64;
4: v4 int64;
5: v5 int64;
6: v6 int64;
7: v7 int64;
8: v8 int64;
9: v9 int64;
10: v10 int64;
11: v11 int64;
12: v12 int64;
13: v13 int64;
14: v14 int64;
15: v15 int64;
16: v16 int64;
17: v17 int64;
18: v18 int64;
19: v19 int64;
20: v20 int64;
21: v21 int64;
22: v22 int64;
23: v23 int64;
24: v24 int64;
25: v25 int64;
26: v26 int64;
27: v27 int64;
28: v28 int64;
29: v29 int64;
30: v30 int64;
31: v31 int64;
32: v32 int64;
33: v33 int64;
34: v34 int64;
35: v35 int64;
36: v36 int64;
37: v37 int64;
38: v38 int64;
39: v39 int64;
40: v40 int64;
41: v41 int64;
42: v42 int64;
43: v43 int64;
44: v44 int64;
45: v45 int64;
46: v46 int64;
47: v47 int64;
48: v48 int64;
49: v49 int64;
50: v50 int64;
51: v51 int64;
52: v52 int64;
53: v53 int64;
54: v54 int64;
55: v55 int64;
56: v56 int64;
57: v57 int64;
58: v58 int64;
59: v59 int64;
60: v60 int64;
61: v61 int64;
62: v62 int64;
63: v63 int64;
64: v64 int64;
};
如果發現自己需要新增第 64 位成員,應建立個別 表格來保留 64 以上成員,並改為將該成員加入表格中:
library test.good.fi0093;
type Table64thField = table {
1: x int64;
};
type Example = table {
1: v1 int64;
2: v2 int64;
3: v3 int64;
4: v4 int64;
5: v5 int64;
6: v6 int64;
7: v7 int64;
8: v8 int64;
9: v9 int64;
10: v10 int64;
11: v11 int64;
12: v12 int64;
13: v13 int64;
14: v14 int64;
15: v15 int64;
16: v16 int64;
17: v17 int64;
18: v18 int64;
19: v19 int64;
20: v20 int64;
21: v21 int64;
22: v22 int64;
23: v23 int64;
24: v24 int64;
25: v25 int64;
26: v26 int64;
27: v27 int64;
28: v28 int64;
29: v29 int64;
30: v30 int64;
31: v31 int64;
32: v32 int64;
33: v33 int64;
34: v34 int64;
35: v35 int64;
36: v36 int64;
37: v37 int64;
38: v38 int64;
39: v39 int64;
40: v40 int64;
41: v41 int64;
42: v42 int64;
43: v43 int64;
44: v44 int64;
45: v45 int64;
46: v46 int64;
47: v47 int64;
48: v48 int64;
49: v49 int64;
50: v50 int64;
51: v51 int64;
52: v52 int64;
53: v53 int64;
54: v54 int64;
55: v55 int64;
56: v56 int64;
57: v57 int64;
58: v58 int64;
59: v59 int64;
60: v60 int64;
61: v61 int64;
62: v62 int64;
63: v63 int64;
64: v64 Table64thField;
};
我們完整詳述這個要求背後的推理和動機 RFC-0132:FIDL 資料表大小限制。簡單來說,FIDL 資料表必須 允許的欄位數量較為有限,否則 若是一次只使用幾個欄位的資料表,編碼形式就會有 空間非常大 (每省略成員 16 個位元組)。
如要為使用者提供表格中超過 64 個欄位的解決方法,FIDL 強制為「接續表」保留最後一個序數包含 就能輸入這些欄位在這個位置使用任何其他類型會導致表格 無法實現目標
fi-0094:重複的資料表成員一般
在 table
宣告中,成員使用的常態無法重複:
library test.bad.fi0094;
type MyTable = table {
1: my_field string;
1: my_other_field uint32;
};
視需要遞增序數,以確保宣告的獨特性 所有成員的序數:
library test.good.fi0094a;
type MyTable = table {
1: my_field string;
2: my_other_field uint32;
};
或者,您也可以移除其中一位名稱重複的成員:
library test.good.fi0094b;
type MyTable = table {
1: my_field string;
};
序數是用來識別電線欄位。如果兩名成員共用一個 單數式,就無法有可靠的方式 判斷該參照哪個欄位 解碼器會解碼 FIDL 訊息
fi-0095
fi-0096
fi-0097:重複的聯集成員一般
在 union
宣告中,成員使用的常態無法重複:
library test.bad.fi0097;
type MyUnion = strict union {
1: my_variant string;
1: my_other_variant int32;
};
視需要遞增序數,以確保宣告的獨特性 所有成員的序數:
library test.good.fi0097a;
type MyUnion = strict union {
1: my_variant string;
2: my_other_variant int32;
};
或者,您也可以移除其中一位名稱重複的成員:
library test.good.fi0097b;
type MyUnion = strict union {
1: my_variant string;
};
序數是用於識別電線的變體。如果兩名成員共用一個 單數,沒有可靠的方式可以判斷系統參照哪個變化版本 標示出 FIDL 訊息的情況
fi-0098
fi-0099
fi-0100
fi-0101:無法解析的大小限制
套用至 vector
或 string
類型定義的大小限制必須是
uint32
類型的有效值:
library test.bad.fi0101a;
alias MyBoundedOptionalVector = vector<uint32>:<"255", optional>;
library test.bad.fi0101b;
alias MyBoundedOptionalVector = vector<uint32>:<uint8, optional>;
確認情況如下:
library test.good.fi0101;
alias MyBoundedOptionalVector = vector<uint32>:<255, optional>;
fi-0102:會員價值無法解析
bits
和 enum
宣告的成員必須是
指定的子類型:
library test.bad.fi0102;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = -4;
};
確保所有值都與宣告的基礎類型相符:
library test.good.fi0102;
type Fruit = bits : uint64 {
ORANGE = 1;
APPLE = 2;
BANANA = 4;
};
fi-0103:無法解析結構體預設值
struct
宣告成員的預設值必須與
個別成員的指定類型:
library test.bad.fi0103;
type MyEnum = enum : int32 {
A = 1;
};
type MyStruct = struct {
@allow_deprecated_struct_defaults
field MyEnum = 1;
};
確認該值與宣告的類型相符:
library test.good.fi0103;
type MyEnum = enum : int32 {
A = 1;
};
type MyStruct = struct {
@allow_deprecated_struct_defaults
field MyEnum = MyEnum.A;
};
fi-0104:無法解析的屬性引數
官方的引數值 根據預期屬性結構定義,FIDL 屬性無效 引數:
library test.bad.fi0104;
type MyStruct = struct {
my_field @generated_name(true) struct {};
};
確認做為屬性引數使用的值類型正確無誤:
library test.good.fi0104;
type MyStruct = struct {
my_field @generated_name("my_inner_type") struct {};
};
fi-0105
fi-0106
fi-0107:成員值重複
bits
和 enum
宣告中都不得包含相同值的成員:
library test.bad.fi0107;
type Fruit = flexible enum {
ORANGE = 1;
APPLE = 1;
};
將成員值變更為不得重複:
library test.good.fi0107a;
type Fruit = flexible enum {
ORANGE = 1;
APPLE = 2;
};
或者,您也可以移除其中一名重複的成員:
library test.good.fi0107b;
type Fruit = flexible enum {
ORANGE = 1;
};
fi-0108
fi-0109
fi-0110:包含類型的資源必須標示為資源
包含帳號代碼的類型 (直接或透過遞移性)
加入另一個包含的處理常式類型,如果沒有該類型,則無法宣告
指定類型為 resource
:
library test.bad.fi0110;
using zx;
type Foo = struct {
handle zx.Handle;
};
有兩種可能的解決方案。其一是針對 使用資源修飾符進行宣告:
library test.good.fi0110a;
using zx;
type Foo = resource struct {
handle zx.Handle;
};
或者,您也可以選擇徹底移除包含 resource
的類型,
因此,無需在自有宣告中使用修飾符:
library test.good.fi0110b;
type Foo = struct {
value uint32;
};
新增 resource
修飾符和其動機的推理和動機
「傳染性」如要瞭解這項錯誤強制執行的使用模式的性質,請查看
。RFC-0057:預設 no 帳號。
fi-0111:內嵌大小超過上限
不允許內嵌大小為 64 KiB 以上的 FIDL 類型:
library test.bad.fi0111;
type MyStruct = struct {
numbers array<uint8, 65536>;
};
而是確定該類型的內嵌大小小於 64 KiB。在本例中 就可以調整陣列繫結:
library test.good.fi0111;
type MyStruct = struct {
numbers array<uint8, 65535>;
};
這項限制是基於效能考量的考量。也就是編碼器和解碼器 可以假設大小和偏移量符合無正負號的 16 位元整數。
除非您使用大型陣列或 具備深度巢狀結構的結構體大多數的 FIDL 結構 (例如字串、向量、資料表、 和聯集) 會使用離線儲存空間,不會計入 個別內嵌大小
fi-0112:服務成員並非 client_end
服務成員只能做為用戶端結束,不得加入任何其他類型:
library test.bad.fi0112;
protocol Calculator {};
service Service {
calculator server_end:Calculator;
};
如要修正錯誤,請確認成員的格式是某些通訊協定 P
的 client_end:P
格式:
library test.good.fi0112;
protocol Calculator {};
service Service {
calculator client_end:Calculator;
};
服務是通訊協定執行個體的集合,而非一般用途資料 所以對於允許任意型別並不合理。
fi-0113:服務中的傳輸方式不符
FIDL 服務禁止包含使用不同傳輸的通訊協定:
library test.bad.fi0113;
protocol ChannelProtocol {};
@transport("Driver")
protocol DriverProtocol {};
service SomeService {
a client_end:ChannelProtocol;
b client_end:DriverProtocol;
};
請改為針對各種交通運輸使用不同服務:
library test.good.fi0113;
protocol ChannelProtocol {};
@transport("Driver")
protocol DriverProtocol {};
service ChannelService {
protocol client_end:ChannelProtocol;
};
service DriverService {
protocol client_end:DriverProtocol;
};
請注意,服務是 FIDL 中尚未完成的功能。其原本是 於 RFC-0041 中設計:支援統合 Serviceas,以及 裝置。 詳情請參閱 https://fxbug.dev/42160684 。
fi-0114:Composed 通訊協定太開放
通訊協定無法組合更開放的其他通訊協定:
library test.bad.fi0114;
open protocol Composed {};
ajar protocol Composing {
compose Composed;
};
如要修正此問題,請提高撰寫通訊協定的開放程度,即
從 closed
改為 ajar
或從 ajar
變更為 open
:
library test.good.fi0114a;
open protocol Composed {};
open protocol Composing {
compose Composed;
};
或者,您也可以降低組成通訊協定的開放程度,即
從 open
到 ajar
或從 ajar
到 closed
:
library test.good.fi0114b;
ajar protocol Composed {};
ajar protocol Composing {
compose Composed;
};
之所以有這項規則,是因為通訊協定的開放性會限制 可以包含的方法。例如,ajar 通訊協定不得含有 彈性的雙向方法,但開放式通訊協定可以執行,因此對 Ajar 而言並不安全 都屬於開放式通訊協定
請參閱 RFC-0138:處理不明問題 互動 ,進一步瞭解通訊協定修飾符。
fi-0115:彈性的雙向方法須使用開放通訊協定
封閉式和 ajar 通訊協定不得包含彈性的雙向方法:
library test.bad.fi0115;
ajar protocol Protocol {
flexible Method() -> ();
};
請改為標示雙向方法 strict
,而非 flexible
:
library test.good.fi0115a;
ajar protocol Protocol {
strict Method() -> ();
};
或者,您也可以標記通訊協定 open
,而不是 closed
或 ajar
:
library test.good.fi0115b;
open protocol Protocol {
flexible Method() -> ();
};
之所以發生這個錯誤,是因為 closed
(或 ajar
) 修飾符的用途:
確定方法不含任何彈性 (雙向) 方法。第一次
建立通訊協定後,請審慎考慮
關閉、關閉、Ajar 或開放式,按照需要的可進性屬性進行設計。
請參閱 RFC-0138:處理不明問題 互動 ,進一步瞭解通訊協定修飾符。
fi-0116:彈性的單向方法,需要 ajar 或開放式通訊協定
封閉式通訊協定不允許包含彈性的單向方法:
library test.bad.fi0116;
closed protocol Protocol {
flexible Method();
};
請改為標記單向方法 strict
,而非 flexible
:
library test.good.fi0116;
closed protocol Protocol {
strict Method();
};
您也可以標示通訊協定 ajar
或 open
,而不是 closed
:
library test.good.fi0116;
ajar protocol Protocol {
flexible Method();
};
之所以發生這個錯誤,是因為 closed
修飾符的用途是確保
方法不含任何彈性方法,首次建立通訊協定時
請務必仔細考慮該產品是關閉、罐子或打開
視需求彈性調整資源配置
請參閱 RFC-0138:處理不明問題 互動 ,進一步瞭解通訊協定修飾符。
fi-0117:使用不相容的傳輸帳號
通訊協定只能指與其傳輸相容的帳號代碼。 例如,Zircon 管道傳輸上的通訊協定不得參照 Fuchsia 驅動程式架構可處理:
library test.bad.fi0117;
using fdf;
protocol Protocol {
Method(resource struct {
h fdf.handle;
});
};
請改用與通訊協定傳輸相容的帳號代碼:
library test.good.fi0117a;
using zx;
protocol Protocol {
Method(resource struct {
h zx.Handle;
});
};
或者,變更通訊協定的傳輸方式,使其符合控制代碼:
library test.good.fi0117b;
using fdf;
@transport("Driver")
protocol Protocol {
Method(resource struct {
h fdf.handle;
});
};
fi-0118:使用不相容的傳輸端點
通訊協定只能指涉傳輸的結尾 (client_end
和 server_end
)
支援相同的通訊協定例如使用 Syscall 的通訊協定
傳輸功能不能使用驅動程式傳輸參照通訊協定的用戶端端:
library test.bad.fi0118;
@transport("Driver")
protocol DriverProtocol {};
@transport("Syscall")
protocol P {
M(resource struct {
s client_end:DriverProtocol;
});
};
如要修正錯誤,請移除傳輸端成員:
library test.good.fi0118;
@transport("Driver")
protocol DriverProtocol {};
@transport("Syscall")
protocol Protocol {
M();
};
fi-0119
fi-0120:屬性位置無效
部分官方屬性僅
是否允許特定位置例如,@selector
屬性
用於:
library test.bad.fi0120a;
@selector("Nonsense")
type MyUnion = union {
1: hello uint8;
};
如要修正錯誤,請移除這項屬性:
library test.good.fi0120a;
type MyUnion = union {
1: hello uint8;
};
您打算以特定方式使用屬性時,也可能會發生這個錯誤
但放在錯誤的位置舉例來說,
@generated_name
屬性無法直接位於成員上:
library test.bad.fi0120a;
@selector("Nonsense")
type MyUnion = union {
1: hello uint8;
};
而是放在成員的匿名版面配置之前:
library test.good.fi0120a;
type MyUnion = union {
1: hello uint8;
};
fi-0121:屬性已淘汰
部分官方屬性為 已淘汰,且應不再使用:
library test.bad.fi0121;
@example_deprecated_attribute
type MyStruct = struct {};
修正方式取決於該屬性遭到淘汰的原因。舉例來說,錯誤訊息可能指出需改用其他屬性。在此情況下,我們可以移除屬性:
library test.good.fi0121;
type MyStruct = struct {};
fi-0122:屬性名稱重複
元素中不能有多個名稱相同的屬性:
library test.bad.fi0122;
@custom_attribute("first")
@custom_attribute("second")
type Foo = struct {};
每項屬性只需指定一次即可:
library test.good.fi0122;
@custom_attribute("first")
type Foo = struct {};
fi-0123:標準屬性名稱重複
元素中不得含有多個有相同標準名稱的屬性:
library test.bad.fi0123;
@custom_attribute("first")
@CustomAttribute("second")
type Foo = struct {};
雖然 custom_attribute
和 CustomAttribute
的外觀不同
兩者皆以「標準」名稱 custom_attribute
表示。您就能取得
正規名稱,方法是將原始名稱轉換為 snake_case
。
如要修正錯誤,請為每項屬性命名,且名稱之間不得重複 以及標準化
library test.good.fi0123;
@custom_attribute("first")
@AnotherCustomAttribute("first")
type Foo = struct {};
如要進一步瞭解 FIDL 要求宣告權限的原因,請參閱 fi-0035 且沒有不重複的正規名稱
fi-0124:自訂屬性引數必須為字串或布林值
使用者定義 FIDL 屬性上的引數僅能使用字串或 布林值類型:
library test.bad.fi0124;
@my_custom_attr(foo=1, bar=2.3)
type MyStruct = struct {};
library test.good.fi0124;
@my_custom_attr(foo=true, bar="baz")
type MyStruct = struct {};
不同於官方屬性,使用者定義的結構定義
編譯器不知道這些屬性。因此不可能
導致任何指定數字引數的型別。為 2
「int8
」、「uint64
」或「float32
」?編譯器無法知道。
如要解決這個問題,可能的做法是實作第一級
numeric
類型 (適用於這類模稜兩可的情況)。但由於
自訂屬性引數是目前已知的用途
還沒有優先處理的功能
fi-0125:屬性引數不得命名
使用官方屬性時 需要使用單一引數,您就無法為該引數命名:
library test.bad.fi0125;
@transport(value="Driver")
protocol Foo {};
請改為傳送引數,而不為其命名:
library test.good.fi0125;
@discoverable(name="example.Bar")
protocol Foo {};
FIDL 會強制執行此要求,讓屬性更簡潔一致。在
系統推論引數名稱是 value
(顯示在
JSON IR),因為這是屬性接收的唯一引數。
fi-0126:屬性引數必須命名
使用官方屬性時 需要多個引數,則無法傳遞未命名的引數:
@available(1)
library test.bad.fi0126;
請改為指定引數的名稱:
@available(added=1)
library test.good.fi0126;
發生這個錯誤,因為無法得知您想要的引數 在屬性接受多個引數時設定。
fi-0127:缺少必要屬性引數
使用官方屬性時 您不可省略該引數:
library test.bad.fi0127;
@has_required_arg
type Foo = struct {};
請改為提供必要引數:
library test.good.fi0127;
@has_required_arg(required="something")
type Foo = struct {};
fi-0128:缺少單一屬性引數
使用官方屬性時 需要一個引數,您就無法省略該引數:
library test.bad.fi0128;
@transport
protocol Protocol {};
而是改為提供引數:
library test.good.fi0128;
@transport("Driver")
protocol Protocol {};
fi-0129:不明的屬性引數
使用官方屬性時 您無法提供結構定義中沒有的引數:
@available(added=1, discontinued=2)
library test.bad.fi0129;
如果您要傳遞不同的引數但名稱有誤,請將其變更為正確的名稱:
@available(added=1, deprecated=2)
library test.good.fi0129a;
您也可以移除引數:
@available(added=1)
library test.good.fi0129b;
發生這個錯誤,是因為系統依據結構定義驗證官方屬性。如果 FIDL 允許使用任意引數,因此不會有任何作用,而且可能會造成 並遮蓋錯字
fi-0130:屬性引數重複
一個屬性不能有兩個名稱相同的引數:
library test.bad.fi0130;
@custom_attribute(custom_arg=true, custom_arg=true)
type Foo = struct {};
請改為只提供一個具有該名稱的引數:
library test.good.fi0130;
@custom_attribute(custom_arg=true)
type Foo = struct {};
fi-0131:重複的標準屬性引數
一個屬性不能有兩個使用相同正規名稱的引數:
library test.bad.fi0131;
@custom_attribute(custom_arg=true, CustomArg=true)
type Foo = struct {};
雖然 custom_arg
和 CustomArg
的外觀不同,但兩者
由「標準」名稱 custom_arg
表示。這樣您就能取得正規名稱
做法是將原始名稱轉換為 snake_case
。
如要修正錯誤,請為每個引數提供一個不重複的名稱 標準化:
library test.good.fi0131a;
@custom_attribute(custom_arg=true, AnotherCustomArg=true)
type Foo = struct {};
您也可以移除其中一個引數:
library test.good.fi0131b;
@custom_attribute(custom_arg=true)
type Foo = struct {};
如要進一步瞭解 FIDL 要求宣告權限的原因,請參閱 fi-0035 且沒有不重複的正規名稱
fi-0132:非預期的屬性引數
使用官方屬性時 沒有引數,您就無法提供引數:
library test.bad.fi0132;
type Foo = flexible enum : uint8 {
@unknown("hello")
BAR = 1;
};
而是應該移除引數:
library test.good.fi0132;
type Foo = flexible enum : uint8 {
@unknown
BAR = 1;
};
fi-0133:屬性引數須為常值
特定的官方屬性 不允許參照常數的引數:
library test.bad.fi0133;
const NAME string = "MyTable";
type Foo = struct {
bar @generated_name(NAME) table {};
};
請改為傳送常值做為引數:
library test.good.fi0133;
type Foo = struct {
bar @generated_name("MyTable") table {};
};
這些屬性需要常值引數,因為其值會影響 編譯。支援非常值的論述並不容易 有時則會造成牴觸
fi-0134
fi-0135:可搜尋名稱無效
如果 @discoverable
屬性使用的名稱無效,就會發生這個錯誤。
@discoverable
屬性應為程式庫名稱,後面接有 .
和
通訊協定名稱
library test.bad.fi0135;
@discoverable(name="test.bad.fi0135/Parser")
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
如要修正這項錯誤,請使用有效的可搜尋名稱:
library test.good.fi0135;
@discoverable(name="test.good.fi0135.Parser")
protocol Parser {
Tokenize() -> (struct {
tokens vector<string>;
});
};
fi-0136
fi-0137
fi-0138
fi-0139
fi-0140
fi-0141:無效的錯誤類型
方法回應酬載上的 error
類型必須是 int32
、uint32
或
enum
數量:
library test.bad.fi0141;
protocol MyProtocol {
MyMethod() -> () error float32;
};
如要修正這項錯誤,請將 error
類型變更為其中一個有效選項:
library test.good.fi0141;
protocol MyProtocol {
MyMethod() -> () error int32;
};
詳情請參閱 RFC-0060:錯誤處理。
fi-0142:通訊協定傳輸類型無效
protocol
宣告中的 @transport(...)
屬性不得指定
無效的傳輸方式:
library test.bad.fi0142;
@transport("Invalid")
protocol MyProtocol {
MyMethod();
};
請改用系統支援的其中一種傳輸方式:
library test.good.fi0142;
@transport("Syscall")
protocol MyProtocol {
MyMethod();
};
支援的傳輸元件仍在開發階段,請參閱 FIDL 屬性」一文,掌握最新資訊。
fi-0143
fi-0144
fi-0145:屬性錯字
屬性名稱的拼寫與 FIDL 官方機構其中一個官方名稱過於相似 屬性會導致編譯器警告:
library test.bad.fi0145;
@duc("should be doc")
protocol Example {
Method();
};
在上述範例中,@duc
屬性的拼寫方式與
官方 FIDL 屬性 @doc
。在這類情況中,屬性的命名方式
而不是不小心打錯官方 FIDL 屬性
應加以修改,使其更加獨特:
library test.good.fi0145;
@duck("quack")
protocol Example {
Method();
};
除了拼字檢查以外,這項警告的目的在於不建議 與官方 FIDL 屬性太過相似的名稱。
錯字偵測演算法的運作方式是計算 屬性名稱與每個官方 FIDL 的距離 屬性。名稱太過相似,定義為編輯內容太小 並觸發錯字偵測器
fi-0146:無效的生成名稱
如果使用 @generated_name
屬性指定無效的屬性,就會發生這個錯誤
名稱。產生的名稱必須符合所有 FIDL ID 的規則。
library test.bad.fi0146;
type Device = table {
1: kind flexible enum {
DESKTOP = 1;
PHONE = 2;
};
};
type Input = table {
1: kind @generated_name("_kind") flexible enum {
KEYBOARD = 1;
MOUSE = 2;
};
};
如要修正這個問題,請將 @generated_name
值變更為有效的 ID。
library test.good.fi0146;
type Device = table {
1: kind flexible enum {
DESKTOP = 1;
PHONE = 2;
};
};
type Input = table {
1: kind @generated_name("input_kind") flexible enum {
KEYBOARD = 1;
MOUSE = 2;
};
};
fi-0147:@available
個缺少引數
這個錯誤會在您使用 @available
屬性且未提供
必要的引數@available
需要至少一個added
。
deprecated
或 removed
。
@available(added=1)
library test.bad.fi0147;
@available
type Foo = struct {};
如要修正這個問題,請新增下列其中一個必要引數:
@available(added=1)
library test.good.fi0147;
@available(added=2)
type Foo = struct {};
詳情請參閱 FIDL 版本管理。
fi-0148:淘汰通知
為 @available
使用 note
引數時,就會發生這個錯誤
,而且未加入 deprecated
引數。note
僅支援
以及淘汰的那一環
@available(added=1, note="My note")
library test.bad.fi0148;
如要修正這個錯誤,請移除附註:
@available(added=1)
library test.good.fi0148a;
或是新增必要的 deprecated
通知:
@available(added=1, deprecated=2, note="Removed in 2; use X instead.")
library test.good.fi0148b;
詳情請參閱 FIDL 版本管理。
fi-0149:平台不在程式庫中
嘗試使用 @available
屬性的 platform
時,就會發生這個錯誤
引數。platform
引數僅在 library
層級有效。
@available(added=1)
library test.bad.fi0149;
@available(platform="foo")
type Person = struct {
name string;
};
如要修正這個問題,請將 platform
引數移至程式庫 @available
屬性:
@available(added=1, platform="foo")
library test.good.fi0149a;
type Person = struct {
name string;
};
或是完全移除 platform
引數:
@available(added=1)
library test.good.fi0149b;
type Person = struct {
name string;
};
fi-0150:新增缺少的程式庫供應情形
在沒有在程式庫中新增 @available
屬性至程式庫時,就會發生這個錯誤
並傳回 added
引數必須為 library
指定 added
引數
@available
屬性。
@available(removed=2)
library test.bad.fi0150a;
@available(platform="foo")
library test.bad.fi0150b;
如要修正這個問題,請將 added
引數新增至程式庫的 @available
屬性:
@available(added=1, removed=2)
library test.good.fi0150a;
@available(added=1, platform="foo")
library test.good.fi0150b;
fi-0151:缺少程式庫可用性
如果在非 library
中新增 @available
屬性,就會發生這個錯誤
宣告,但 library
宣告中沒有 @available
屬性。
library test.bad.fi0151;
@available(added=1)
type Person = struct {
name string;
};
如要修正這個錯誤,請在 library
宣告中加入 @available
屬性:
@available(added=1)
library test.good.fi0151a;
@available(added=1)
type Person = struct {
name string;
};
或是從非 library
宣告中移除 @available
屬性:
library test.good.fi0151b;
type Person = struct {
name string;
};
fi-0152:平台無效
如果 platform
引數的 platform
引數使用無效字元,就會發生這個錯誤。
@available
屬性。platform
引數必須是有效的 FIDL 程式庫
ID:
@available(added=1, platform="Spaces are not allowed")
library test.bad.fi0152;
如要修正這個錯誤,請移除不允許的字元:
@available(added=1, platform="foo")
library test.good.fi0152;
fi-0153:版本無效
如果 added
或 removed
使用的版本無效,就會發生這個錯誤
@available
屬性的引數。added
和 removed
引數必須
介於 1 和 2^63-1 之間的正整數,或特殊常數 HEAD
。
@available(added=0)
library test.bad.fi0153;
如要解決這個問題,請將版本變更為有效值:
@available(added=1)
library test.good.fi0153;
fi-0154:供應順序無效
如果 added
、deprecated
與
@available
屬性的 remove
引數。您必須使用下列限制
尊重:
added
必須小於或等於deprecated
deprecated
必須小於removed
added
必須小於removed
@available(added=2, removed=2)
library test.bad.fi0154a;
@available(added=2, deprecated=3, removed=3)
library test.bad.fi0154b;
如要解決這個問題,請將 added
、deprecated
和 removed
引數更新為
所需排序:
@available(added=1, removed=2)
library test.good.fi0154a;
@available(added=2, deprecated=2, removed=3)
library test.good.fi0154b;
fi-0155:供應情形與父項衝突
當你將 @availability
屬性新增至非 library
時,就會發生這個錯誤
宣告與 library
的宣告發生衝突。
@available(added=2, deprecated=3, removed=4)
library test.bad.fi0155a;
@available(added=1)
type Person = struct {
name string;
};
@available(added=2, deprecated=3, removed=4)
library test.bad.fi0155b;
@available(added=4)
type Person = struct {
name string;
};
如要修正這個錯誤,請將 @availability
屬性更新為必填屬性
限制:
@available(added=2, deprecated=3, removed=4)
library test.good.fi0155;
@available(added=2)
type Person = struct {
name string;
};
fi-0156:不能為選用
當您嘗試將類型標示為「選用」無法選取時,就會發生這個錯誤 選用。
library test.bad.fi0156;
type Person = struct {
name string;
age int16:optional;
};
如要修正這個錯誤,請移除選用限制:
library test.good.fi0156;
type Person = struct {
name string;
age int16;
};
只有能選用且不會變更電線形狀的 FIDL 類型才是
可以使用 optional
限制。詳情請參閱
非必要指南或下方的可展開內容。
FIDL 方案:選填性
某些 FIDL 類型可設為選用,不要變更
但該訊息包含訊息,且加上 :optional
限制。
此外,table
版面配置一律為選用性質,struct
版面配置則一律不會
不過,無論內部 IP 位址為何
DNS 名稱始終會指向特定的執行個體如要將 struct
設為選用,其必須納入 box<T>
中,因此
而不是包含訊息的線形
基礎類型 | 選用版本 | 選用性是否會改變電線配置? |
---|---|---|
struct {...} |
box<struct {...}> |
是 |
table {...} |
table {...} |
否 |
union {...} |
union {...}:optional |
不可以 |
vector<T> |
vector<T>:optional |
不可以 |
string |
string:optional |
不可以 |
zx.Handle |
zx.Handle:optional |
不可以 |
client_end:P |
client_end:<P, optional> |
不可以 |
server_end:P |
server_end:<P, optional> |
否 |
所有其他類型 (bits
、enum
、array<T, N>
和原始類型) 都無法
並可視需要設為
在這個變化版本中,我們允許鍵/值儲存庫將其他鍵/值儲存庫視為
成員。簡單來說,我們將這層變成樹做法是替換掉原始的
value
的定義,以及使用雙成員 union
的定義:一種變體
使用與之前相同的 vector<byte>
類型儲存分葉節點,而另一個
會以其他巢狀儲存庫的形式儲存分支版本節點。
原因
這裡說明瞭「選用」的幾個用法,因此我們可以宣告 不一定存在。FIDL 有三種選用方式:
- 一律儲存的類型
中斷狀態
還可直接在線路上說明「缺口」透過
空值 。啟用中
這些類型的選擇性設定不會影響郵件的傳播形狀
,只會變更特定項目中有效的值
類型。
union
、vector<T>
、client_end
、server_end
和zx.Handle
透過新增:optional
限制,即可選擇所有型別。 將value
union
設為選用值,我們就能 「null」項目,格式為缺少value
。這表示bytes
沒有任何內容 和缺少/空白的store
屬性都是無效值。 - 與前述類型不同,
struct
版面配置沒有額外空間, 可以儲存空值的標題因此,這必須包裝在 信封,變更郵件包含的郵件的傳輸形狀 。為確保此線路修改效果清晰易讀,Item
struct
類型必須納入box<T>
類型範本中。 - 最後,
table
版面配置一律為選用項目。缺失的table
只是單一個 而不設定任何成員
樹狀結構是自然的自我參照資料結構:樹狀結構中的任何節點
包含純資料 (在本範例中為字串) 或含有更多資料的子樹狀結構
節點。這需要遞迴:Item
的定義現在轉為遞移性
只靠它!在 FIDL 中表示遞迴類型可能有點難度,
尤其是因為支援服務目前稍微
受限。我們可以支援這些類型
由自我參照建立的循環中至少一種選用類型。適用對象
例如,這裡會將 items
struct
成員定義為 box<Item>
進而破壞納入循環。
這些變更也大量使用匿名類型或
宣告只會內嵌在其使用點上,而不是命名。
自己的頂層 type
宣告。系統預設會以匿名方式
所產生語言繫結中的型別擷取自其本機環境。適用對象
執行個體,新導入的 flexible union
會使用其本身的成員
名稱為 Value
,新引入的 struct
會變成 Store
,依此類推。
這種經驗法則有時會導致衝突,因此 FIDL 會提供逸出字元
方法是允許作者手動覆寫系統產生的匿名類型
name。這項操作是透過 @generated_name
屬性來完成
並變更後端產生的名稱我們可以使用這個方法
Store
類型已重新命名為 NestedStore
,以免與
使用相同的名稱的 protocol
宣告。
實作
FIDL、CML 和領域介面定義的修改如下:
FIDL
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. library examples.keyvaluestore.supporttrees; /// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That /// is, it must start with a letter, end with a letter or number, contain only letters, numbers, /// periods, and slashes, and be between 4 and 64 characters long. type Item = struct { key string:128; value strict union { // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are // disallowed. 1: bytes vector<byte>:64000; // Allows a store within a store, thereby turning our flat key-value store into a tree // thereof. Note the use of `@generated_name` to prevent a type-name collision with the // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the // chain of recursion, thereby allowing `Item` to include itself in its own definition. // // This is a table so that added fields, like for example a `hash`, can be easily added in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
用戶端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
伺服器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
領域
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
用戶端和伺服器實作項目現在可以使用任何支援的語言編寫:
Rust
用戶端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
伺服器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++ (自然)
用戶端
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
伺服器
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++ (有線)
用戶端
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
伺服器
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
用戶端
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
伺服器
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0157:用戶端/伺服器結束限制必須是通訊協定
套用至 client_end
或 server_end
的第一個限制必須指向
轉換為 protocol
定義:
library test.bad.fi0157;
type MyStruct = struct {};
alias ServerEnd = server_end:MyStruct;
將限制變更為指向通訊協定:
library test.good.fi0157;
protocol MyProtocol {};
alias ServerEnd = server_end:MyProtocol;
fi-0158:無法繫結兩次
alias
宣告無法變更已設限制的值
別名的類型:
library test.bad.fi0158;
alias ByteVec256 = vector<uint8>:256;
alias ByteVec512 = ByteVec256:512;
未受限的定義應收到專屬的 alias
宣告,
而每個進一步受限的別名,都應繼承該別名:
library test.good.fi0158;
alias AliasOfVectorOfString = vector<string>;
alias AliasOfVectorOfStringSmall = AliasOfVectorOfString:8;
alias AliasOfVectorOfStringLarge = AliasOfVectorOfString:16;
這是不允許的,避免混淆和編譯器實作複雜。
fi-0159:結構體不可為選用
結構體不得包含 optional
限制:
library test.bad.fi0159;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday Date:optional;
};
將 T:optional
變更為 box<T>
即可修正問題:
library test.good.fi0159;
type Date = struct {
year uint16;
month uint8;
day uint8;
};
type Person = struct {
name string;
birthday box<Date>;
};
只有能選用且不會變更電線形狀的 FIDL 類型才是
可以使用 optional
限制。詳情請參閱
Optionality 指南。
fi-0160:類型不能標示為「非必要」兩次
如果選擇兩次類型,就會發生這項錯誤。這些廣告活動 當類型在兩種都標示為「選用」時 和宣告網站
library test.bad.fi0160;
alias MyAlias = vector<string>:optional;
type MyStruct = struct {
my_member MyAlias:optional;
};
如要修正這項錯誤,只需將類型設為選填一次即可。
舉例來說,您可以從使用網站中移除 :optional
。
library test.good.fi0160a;
alias MyAlias = vector<string>:optional;
type MyStruct = struct {
my_member MyAlias;
};
您也可以從別名宣告中移除 :optional
。
library test.good.fi0160b;
alias MyAlias = vector<string>;
type MyStruct = struct {
my_member MyAlias:optional;
};
fi-0161:大小不得為 0
當您嘗試將陣列大小限制設為 0 時,就會發生這個錯誤。陣列 大小不得為 0
library test.bad.fi0161;
type Person = struct {
name string;
nicknames array<string, 0>;
};
如要修正這項錯誤,請將大小限制變更為正整數。
library test.good.fi0161;
type Person = struct {
name string;
nicknames array<string, 5>;
};
fi-0162:版面配置參數數量錯誤
特定 FIDL 版面配置 (例如 vector
和 array
) 會採用參數。這個錯誤
表示醒目顯示類型的參數數量不正確
指定:
library test.bad.fi0162a;
type Foo = struct {
bar array<8>;
};
在無法參數化的類型出現錯誤的情況下,也可能會顯示 附加的參數:
library test.bad.fi0162b;
type Foo = struct {
bar uint8<8>;
};
修正方法一律會在 問題:
library test.good.fi0162;
type Foo = struct {
bar array<uint8, 8>;
};
FIDL 中唯一的參數化類型為 array<T, N>
、box<T>
和
vector<T>
。要在當中參數化的 client_end
和 server_end
類型
舊版的 FIDL 語法,但現在非如此
但這是很常出現這個錯誤的執行作業來源。現在這兩種類型
請將通訊協定規格視為 (必要) 限制。
一律列於角括號 <...>
中的參數包含
與限制相似,後者會顯示在結尾的 :...
字元後方
和類型比方說,首次模糊處理時,文字可能會有些許奇怪,array<T, N>
將大小指定為參數,vector<T>:N
則指定其大小為
限制。不同之處在於參數一定會影響電線配置
而限制只會變更值的類型
編碼/解碼時間視為可接受的值,
對線路配置的影響
如需深入瞭解,請參閱 RFC-0050:FIDL 語法修訂 以及這兩個概念之間的區別
fi-0163:多項限制定義
當您嘗試使用
多個冒號 (:
)。多項限制定義必須採用
角括號語法 type:<constraint1, constraint2, etc>
。
library test.bad.fi0163;
type Person = struct {
name string;
favorite_color string:30:optional;
};
如要修正這個錯誤,請針對限制使用角括號語法:
library test.good.fi0163;
type Person = struct {
name string;
favorite_color string:<30, optional>;
};
fi-0164:限制過多
如果您嘗試對類型新增的限制數量超過
支援。例如,string
最多支援兩個限制。
library test.bad.fi0164;
type Person = struct {
name string:<0, optional, 20>;
};
如要修正這個問題,請移除額外的限制條件:
library test.good.fi0164;
type Person = struct {
name string:<20, optional>;
};
fi-0165:預期類型
在 FIDL 使用常數或通訊協定 ID 時,發生這個錯誤。 而不是只預測類型
library test.bad.fi0165;
type Person = struct {
name string;
nicknames vector<5>;
};
如要修正這項錯誤,請更新程式碼並使用有效的類型:
library test.good.fi0165;
type Person = struct {
name string;
nicknames vector<string>:5;
};
通訊協定不會視為 FIDL 類型,也無法在類型處使用 。
fi-0166:非預期的限制
當您嘗試使用非預期範圍限制時,就會發生這個錯誤。
通常是因為名為 const
的位置有誤。
library test.bad.fi0166;
const MIN_SIZE uint8 = 1;
const MAX_SIZE uint8 = 5;
type Person = struct {
name string;
nicknames vector<string>:<MIN_SIZE, MAX_SIZE>;
};
如要修正這個錯誤,請移除限制:
library test.good.fi0166;
const MAX_SIZE uint8 = 5;
type Person = struct {
name string;
nicknames vector<string>:<MAX_SIZE>;
};
fi-0167:不能限制兩次
為 client_end
或 server_end
已透過 alias
宣告定義傳輸界限
禁止:
library test.bad.fi0167;
protocol MyOtherProtocol {};
alias ClientEnd = client_end:MyProtocol;
alias ServerEnd = server_end:MyProtocol;
protocol MyProtocol {
MyMethod(resource struct {
my_client ClientEnd:MyOtherProtocol;
}) -> (resource struct {
my_server ServerEnd:MyOtherProtocol;
});
};
請改用 client_end
和 server_end
類型的別名
完全:
library test.good.fi0167;
protocol MyProtocol {
MyMethod(resource struct {
my_client client_end:MyProtocol;
}) -> (resource struct {
my_server server_end:MyProtocol;
});
};
這是不允許的,避免混淆和編譯器實作複雜。
fi-0168:用戶端/伺服器端必須有通訊協定限制
套用至 client_end
或 server_end
的第一個限制必須指向
轉換為 protocol
定義:
library test.bad.fi0168;
protocol MyProtocol {
MyMethod(resource struct {
server server_end;
});
};
新增限制指向所需通訊協定:
library test.good.fi0168;
protocol MyProtocol {
MyMethod(resource struct {
server server_end:MyProtocol;
});
};
fi-0169:盒裝類型不能為選填
box<T>
形式的類型無法套用 optional
限制:
library test.bad.fi0169;
type Color = struct {
red byte;
green byte;
blue byte;
};
type MyStruct = struct {
maybe_color box<Color>:optional;
};
盒子類型為定義選用,因此加上額外限制 非必要且多餘的:
library test.good.fi0169;
type Color = struct {
red byte;
green byte;
blue byte;
};
type MyStruct = struct {
maybe_color box<Color>;
};
fi-0170
fi-0171:盒裝類型應改用選用限制
只有使用 struct
版面配置的類型可以裝箱;union
、vector
、string
、
client_end
、server_end
和 zx.Handle
必須使用 optional
限制
請改採以下做法:
library test.bad.fi0171;
using zx;
type MyStruct = resource struct {
my_resource_member box<zx.Handle>;
};
如要修正這個問題,請將 box<T>
轉換為 T:optional
:
library test.good.fi0171;
using zx;
type MyStruct = resource struct {
my_resource_member zx.Handle:optional;
};
只有能選用且不會變更電線形狀的 FIDL 類型才是
可以使用 optional
限制。詳情請參閱
非必要指南或下方的可展開內容
可能不準確或不適當
FIDL 方案:選填性
某些 FIDL 類型可設為選用,不要變更
但該訊息包含訊息,且加上 :optional
限制。
此外,table
版面配置一律為選用性質,struct
版面配置則一律不會
不過,無論內部 IP 位址為何
DNS 名稱始終會指向特定的執行個體如要將 struct
設為選用,其必須納入 box<T>
中,因此
而不是包含訊息的線形
基礎類型 | 選用版本 | 選用性是否會改變電線配置? |
---|---|---|
struct {...} |
box<struct {...}> |
是 |
table {...} |
table {...} |
否 |
union {...} |
union {...}:optional |
不可以 |
vector<T> |
vector<T>:optional |
不可以 |
string |
string:optional |
不可以 |
zx.Handle |
zx.Handle:optional |
不可以 |
client_end:P |
client_end:<P, optional> |
不可以 |
server_end:P |
server_end:<P, optional> |
否 |
所有其他類型 (bits
、enum
、array<T, N>
和原始類型) 都無法
並可視需要設為
在這個變化版本中,我們允許鍵/值儲存庫將其他鍵/值儲存庫視為
成員。簡單來說,我們將這層變成樹做法是替換掉原始的
value
的定義,以及使用雙成員 union
的定義:一種變體
使用與之前相同的 vector<byte>
類型儲存分葉節點,而另一個
會以其他巢狀儲存庫的形式儲存分支版本節點。
原因
這裡說明瞭「選用」的幾個用法,因此我們可以宣告 不一定存在。FIDL 有三種選用方式:
- 一律儲存的類型
中斷狀態
還可直接在線路上說明「缺口」透過
空值 。啟用中
這些類型的選擇性設定不會影響郵件的傳播形狀
,只會變更特定項目中有效的值
類型。
union
、vector<T>
、client_end
、server_end
和zx.Handle
透過新增:optional
限制,即可選擇所有型別。 將value
union
設為選用值,我們就能 「null」項目,格式為缺少value
。這表示bytes
沒有任何內容 和缺少/空白的store
屬性都是無效值。 - 與前述類型不同,
struct
版面配置沒有額外空間, 可以儲存空值的標題因此,這必須包裝在 信封,變更郵件包含的郵件的傳輸形狀 。為確保此線路修改效果清晰易讀,Item
struct
類型必須納入box<T>
類型範本中。 - 最後,
table
版面配置一律為選用項目。缺失的table
只是單一個 而不設定任何成員
樹狀結構是自然的自我參照資料結構:樹狀結構中的任何節點
包含純資料 (在本範例中為字串) 或含有更多資料的子樹狀結構
節點。這需要遞迴:Item
的定義現在轉為遞移性
只靠它!在 FIDL 中表示遞迴類型可能有點難度,
尤其是因為支援服務目前稍微
受限。我們可以支援這些類型
由自我參照建立的循環中至少一種選用類型。適用對象
例如,這裡會將 items
struct
成員定義為 box<Item>
進而破壞納入循環。
這些變更也大量使用匿名類型或
宣告只會內嵌在其使用點上,而不是命名。
自己的頂層 type
宣告。系統預設會以匿名方式
所產生語言繫結中的型別擷取自其本機環境。適用對象
執行個體,新導入的 flexible union
會使用其本身的成員
名稱為 Value
,新引入的 struct
會變成 Store
,依此類推。
這種經驗法則有時會導致衝突,因此 FIDL 會提供逸出字元
方法是允許作者手動覆寫系統產生的匿名類型
name。這項操作是透過 @generated_name
屬性來完成
並變更後端產生的名稱我們可以使用這個方法
Store
類型已重新命名為 NestedStore
,以免與
使用相同的名稱的 protocol
宣告。
實作
FIDL、CML 和領域介面定義的修改如下:
FIDL
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. library examples.keyvaluestore.supporttrees; /// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That /// is, it must start with a letter, end with a letter or number, contain only letters, numbers, /// periods, and slashes, and be between 4 and 64 characters long. type Item = struct { key string:128; value strict union { // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are // disallowed. 1: bytes vector<byte>:64000; // Allows a store within a store, thereby turning our flat key-value store into a tree // thereof. Note the use of `@generated_name` to prevent a type-name collision with the // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the // chain of recursion, thereby allowing `Item` to include itself in its own definition. // // This is a table so that added fields, like for example a `hash`, can be easily added in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
用戶端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
伺服器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
領域
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
用戶端和伺服器實作項目現在可以使用任何支援的語言編寫:
Rust
用戶端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
伺服器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++ (自然)
用戶端
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
伺服器
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++ (有線)
用戶端
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
伺服器
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
用戶端
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
伺服器
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0172:資源定義必須使用 uint32 子類型
resource_definition
宣告的子類型必須是 uint32
:
library test.bad.fi0172;
type MySubtype = strict enum : uint32 {
NONE = 0;
};
resource_definition MyResource : uint8 {
properties {
subtype MySubtype;
};
};
將子類型變更為 uint32
即可修正這項錯誤:
library test.good.fi0172;
type MySubtype = strict enum : uint32 {
NONE = 0;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
};
};
這是與 FIDL 內部實作相關的錯誤,因此應該僅 。使用者 絕不顯示這個錯誤
其所參照的 resource_definition
宣告是 FIDL 的內部方式
定義諸如帳號代碼等資源,而且日後可能會
處理一般化工作。
fi-0173:資源定義必須指定子類型
resource_definition
宣告不得省略 subtype
成員:
library test.bad.fi0173;
resource_definition MyResource : uint32 {
properties {
rights uint32;
};
};
請將這位成員指向有效的 enum : uint32
宣告:
library test.good.fi0173;
resource_definition MyResource : uint32 {
properties {
subtype flexible enum : uint32 {};
rights uint32;
};
};
這是與 FIDL 內部實作相關的錯誤,因此應該僅 。使用者 絕不顯示這個錯誤
其所參照的 resource_definition
宣告是 FIDL 的內部方式
定義諸如帳號代碼等資源,而且日後可能會
處理一般化工作。
fi-0174
fi-0175:資源定義子類型屬性必須參照列舉
resource_definition
宣告無法使用非 enum
做為 subtype
成員:
library test.bad.fi0175;
resource_definition MyResource : uint32 {
properties {
subtype struct {};
};
};
請將這位成員指向有效的 enum : uint32
宣告:
library test.good.fi0175;
resource_definition MyResource : uint32 {
properties {
subtype flexible enum : uint32 {};
};
};
這是與 FIDL 內部實作相關的錯誤,因此應該僅 。使用者 絕不顯示這個錯誤
其所參照的 resource_definition
宣告是 FIDL 的內部方式
定義諸如帳號代碼等資源,而且日後可能會
處理一般化工作。
fi-0176
fi-0177:資源定義權利屬性必須參照位元
resource_definition
宣告無法使用非 bits
做為 rights
成員:
library test.bad.fi0177;
type MySubtype = enum : uint32 {
NONE = 0;
VMO = 3;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
rights string;
};
};
請將這位成員指向有效的 bits : uint32
宣告:
library test.good.fi0177;
type MySubtype = enum : uint32 {
NONE = 0;
VMO = 3;
};
resource_definition MyResource : uint32 {
properties {
subtype MySubtype;
rights uint32;
};
};
這是與 FIDL 內部實作相關的錯誤,因此應該僅 。使用者 絕不顯示這個錯誤
其所參照的 resource_definition
宣告是 FIDL 的內部方式
定義諸如帳號代碼等資源,而且日後可能會
處理一般化工作。
fi-0178:未使用的匯入
參照透過 using
宣告匯入的依附元件會是錯誤:
library test.bad.fi0178;
using dependent;
type Foo = struct {
does_not int64;
use_dependent int32;
};
確認所有這類匯入內容都會在程式庫匯入作業中使用 參考匯入的內容,或移除未使用的依附元件:
library test.good.fi0178;
using dependent;
type Foo = struct {
dep dependent.Bar;
};
fi-0179:無限制的新類型
來自 RFC-0052:類型別名和新類型的新類型
可受到限制舉例來說,新類型的 string
不得
受限於:optional
:
library test.bad.fi0179;
type Name = string;
type Info = struct {
name Name:optional;
};
在這種情況下,我們可以將 name
欄位放在
而非結構體:
library test.good.fi0179;
type Name = string;
type Info = table {
1: name Name;
};
這項限制可簡化新型別的設計。不清楚 API 是什麼 ABI 通常看起來應該是受限的新類型 (例如 限制會套用到新類型本身,或套用至基礎 類型?)。
fi-0180:Zircon C 類型是實驗性
usize
、uintptr
、uchar
和 experimental_pointer
是內建的類型
Zither 專案所開發的。無法用於一般 FIDL
程式庫:
library test.bad.fi0180;
type Data = struct {
size usize64;
};
請改用其他類型,例如 uint64
,而非 usize
:
library test.good.fi0180;
type Data = struct {
size uint64;
};
fi-0181:程式庫屬性引數參照常數
程式庫宣告中的屬性引數不得參照 常數:
@custom_attribute(VALUE)
library test.bad.fi0181a;
const VALUE string = "hello";
而是提供常值引數:
@custom_attribute("hello")
library test.good.fi0181a;
之所以有這項限制,是因為很少需要,且支援該功能 並未強制規定的複雜性
fi-0182
fi-0183
fi-0184:非預期的控製字元
字串常值不允許包含原始控製字元 (ASCII)
0x00
到 0x1f
的字元):
library test.bad.fi0184;
const TAB string = " "; // literal tab character
請改用逸出序列。在本例中,\t
是正確的:
library test.good.fi0184a;
const TAB string = "\t";
或者,您也可以使用 Unicode 逸出序列。適用於任何萬國碼 (Unicode) 碼點:
library test.good.fi0184b;
const TAB string = "\u{9}";
字串常值中不得使用原始控製字元,因為這些字元都是 空白字元或無法列印 因此就會造成混淆 請注意,是否直接內嵌在 FIDL 來源檔案中。
fi-0185:Unicode 逸出序列缺少大括號
字串常值中的萬國碼 (Unicode) 逸出序列必須透過大括號指定碼點:
library test.bad.fi0185;
const SMILE string = "\u";
如要修正錯誤,請在大括號中指定碼點:
library test.good.fi0185;
const SMILE string = "\u{1F600}";
fi-0186:無結尾的萬國碼 (Unicode) 逸出序列
字串常值中的萬國碼 (Unicode) 逸出序列必須終止:
library test.bad.fi0186;
const SMILE string = "\u{1F600";
如要終止逸出序列,請新增右大括號 }
:
library test.good.fi0186;
const SMILE string = "\u{1F600}";
fi-0187:萬國碼 (Unicode) 逸出序列空白
字串常值中的萬國碼 (Unicode) 逸出序列必須包含至少一個十六進位數字:
library test.bad.fi0187;
const SMILE string = "\u{}";
如要修正錯誤,請加入十六進位數字來指定萬國碼 (Unicode) 碼點:
library test.good.fi0187;
const SMILE string = "\u{1F600}";
fi-0188:Unicode 逸出序列中的數字太多
字串常值中的萬國碼 (Unicode) 逸出序列不得包含超過 6 個十六進位數字:
library test.bad.fi0188;
const SMILE string = "\u{001F600}";
如要修正錯誤,請指定最多 6 個十六進位數字。在本例中 可移除的 0:
library test.good.fi0188;
const SMILE string = "\u{1F600}";
有此限制,因為所有有效的 Unicode 碼點都符合 6 位數的十六進位代碼 所以沒有理由超過這個上限。
fi-0189:Unicode 碼點過大
字串常值中的萬國碼 (Unicode) 逸出序列無法指定萬國碼 (Unicode) 碼點
大於 0x10ffff
的上限:
library test.bad.fi0189;
const TOO_LARGE string = "\u{110000}";
而是確認碼點是否有效:
library test.good.fi0189;
const MAX_CODEPOINT string = "\u{10ffff}";
fi-0190
fi-0191:方法必須指定嚴格程度
這個錯誤表示 FIDL 方法沒有 strict
或 flexible
修飾符
library test.bad.fi0191;
open protocol Example {
OneWay();
};
如要修正這個問題,請在方法中加入 strict
或 flexible
。如果這是
現有方法,您必須使用 strict
,且應該會看到相容性
指南,瞭解如何變更為 flexible
。如果
這是一種新方法,所有 API 的 API 評分量表
指引方向
library test.good.fi0191;
open protocol Example {
flexible OneWay();
};
為支援處理不明問題,我們正在遷移 FIDL
RFC-0138 中定義的互動。這項新功能可讓您
適用於 FIDL 方法和事件的 strict
和 flexible
修飾符。
以往,所有方法的行為都會像 strict
一樣,但
這項遷移作業結束時,預設值將為 flexible
。避免混淆
以及因變更方法預設修飾符而造成的
strict
至 flexible
,這段過渡期間需要使用方法修飾符
。遷移作業完成後,系統會將錯誤從錯誤變更為
Linter 建議。
如需進一步瞭解不明互動,請參閱 FIDL 語言 參考資料。
fi-0192:通訊協定必須指定開放性
這個錯誤表示 FIDL 通訊協定沒有 open
、ajar
或
closed
修飾符。
library test.bad.fi0192;
protocol ImplicitOpenness {};
如要修正這個問題,請在通訊協定中加入 open
、ajar
或 closed
。如果這是
現有的通訊協定,您必須使用 closed
,且應該會看到相容性
指南,瞭解如何變更為 open
或
ajar
。如果是新方法,您應該會看到 API
參考評分量表,瞭解選擇哪種做法。
library test.good.fi0192;
open protocol ImplicitOpenness {};
為支援處理不明問題,我們正在遷移 FIDL
RFC-0138 中定義的互動。這項新功能會新增三個
新的修飾符、open
、ajar
和 closed
,適用於 FIDL 通訊協定。
以往所有通訊協定的行為都像 closed
一樣,但
這項遷移作業結束時,預設值將為 open
。為避免混淆
變更通訊協定預設修飾符可能會引發的
closed
到 open
,轉換期間必須使用通訊協定修飾符
。遷移作業完成後,系統會將錯誤從錯誤變更為
Linter 建議。
如需進一步瞭解不明互動,請參閱 FIDL 語言 參考資料。
fi-0193:無法裝箱類型
結構體以外的類型不得裝箱。例如,基本類型不能裝箱:
library test.bad.fi0193;
type MyStruct = struct {
my_member box<bool>;
};
如要封裝原始成員,請改為將其置於單一成員 struct
中:
library test.good.fi0193;
type MyStruct = struct {
my_member box<struct {
my_bool bool;
}>;
};
請注意,部分類型可以使用 optional
設為選用
限制。請參閱選擇性指南或可展開式
下文。
FIDL 方案:選填性
某些 FIDL 類型可設為選用,不要變更
但該訊息包含訊息,且加上 :optional
限制。
此外,table
版面配置一律為選用性質,struct
版面配置則一律不會
不過,無論內部 IP 位址為何
DNS 名稱始終會指向特定的執行個體如要將 struct
設為選用,其必須納入 box<T>
中,因此
而不是包含訊息的線形
基礎類型 | 選用版本 | 選用性是否會改變電線配置? |
---|---|---|
struct {...} |
box<struct {...}> |
是 |
table {...} |
table {...} |
否 |
union {...} |
union {...}:optional |
不可以 |
vector<T> |
vector<T>:optional |
不可以 |
string |
string:optional |
不可以 |
zx.Handle |
zx.Handle:optional |
不可以 |
client_end:P |
client_end:<P, optional> |
不可以 |
server_end:P |
server_end:<P, optional> |
否 |
所有其他類型 (bits
、enum
、array<T, N>
和原始類型) 都無法
並可視需要設為
在這個變化版本中,我們允許鍵/值儲存庫將其他鍵/值儲存庫視為
成員。簡單來說,我們將這層變成樹做法是替換掉原始的
value
的定義,以及使用雙成員 union
的定義:一種變體
使用與之前相同的 vector<byte>
類型儲存分葉節點,而另一個
會以其他巢狀儲存庫的形式儲存分支版本節點。
原因
這裡說明瞭「選用」的幾個用法,因此我們可以宣告 不一定存在。FIDL 有三種選用方式:
- 一律儲存的類型
中斷狀態
還可直接在線路上說明「缺口」透過
空值 。啟用中
這些類型的選擇性設定不會影響郵件的傳播形狀
,只會變更特定項目中有效的值
類型。
union
、vector<T>
、client_end
、server_end
和zx.Handle
透過新增:optional
限制,即可選擇所有型別。 將value
union
設為選用值,我們就能 「null」項目,格式為缺少value
。這表示bytes
沒有任何內容 和缺少/空白的store
屬性都是無效值。 - 與前述類型不同,
struct
版面配置沒有額外空間, 可以儲存空值的標題因此,這必須包裝在 信封,變更郵件包含的郵件的傳輸形狀 。為確保此線路修改效果清晰易讀,Item
struct
類型必須納入box<T>
類型範本中。 - 最後,
table
版面配置一律為選用項目。缺失的table
只是單一個 而不設定任何成員
樹狀結構是自然的自我參照資料結構:樹狀結構中的任何節點
包含純資料 (在本範例中為字串) 或含有更多資料的子樹狀結構
節點。這需要遞迴:Item
的定義現在轉為遞移性
只靠它!在 FIDL 中表示遞迴類型可能有點難度,
尤其是因為支援服務目前稍微
受限。我們可以支援這些類型
由自我參照建立的循環中至少一種選用類型。適用對象
例如,這裡會將 items
struct
成員定義為 box<Item>
進而破壞納入循環。
這些變更也大量使用匿名類型或
宣告只會內嵌在其使用點上,而不是命名。
自己的頂層 type
宣告。系統預設會以匿名方式
所產生語言繫結中的型別擷取自其本機環境。適用對象
執行個體,新導入的 flexible union
會使用其本身的成員
名稱為 Value
,新引入的 struct
會變成 Store
,依此類推。
這種經驗法則有時會導致衝突,因此 FIDL 會提供逸出字元
方法是允許作者手動覆寫系統產生的匿名類型
name。這項操作是透過 @generated_name
屬性來完成
並變更後端產生的名稱我們可以使用這個方法
Store
類型已重新命名為 NestedStore
,以免與
使用相同的名稱的 protocol
宣告。
實作
FIDL、CML 和領域介面定義的修改如下:
FIDL
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. library examples.keyvaluestore.supporttrees; /// An item in the store. The key must match the regex `^[A-z][A-z0-9_\.\/]{2,62}[A-z0-9]$`. That /// is, it must start with a letter, end with a letter or number, contain only letters, numbers, /// periods, and slashes, and be between 4 and 64 characters long. type Item = struct { key string:128; value strict union { // Keep the original `bytes` as one of the options in the new union. All leaf nodes in the // tree must be `bytes`, or absent unions (representing empty). Empty byte arrays are // disallowed. 1: bytes vector<byte>:64000; // Allows a store within a store, thereby turning our flat key-value store into a tree // thereof. Note the use of `@generated_name` to prevent a type-name collision with the // `Store` protocol below, and the use of `box<T>` to ensure that there is a break in the // chain of recursion, thereby allowing `Item` to include itself in its own definition. // // This is a table so that added fields, like for example a `hash`, can be easily added in // the future. 2: store @generated_name("nested_store") table { 1: items vector<box<Item>>; }; }:optional; }; /// An enumeration of things that may go wrong when trying to write a value to our store. type WriteError = flexible enum { UNKNOWN = 0; INVALID_KEY = 1; INVALID_VALUE = 2; ALREADY_EXISTS = 3; }; /// A very basic key-value store. @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; };
CML
用戶端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/client_bin", }, use: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A newline separated list nested entries. The first line should be the key // for the nested store, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. write_nested: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, // A list of keys, all of which will be populated as null entries. write_null: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, }, }
伺服器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { include: [ "syslog/client.shard.cml" ], program: { runner: "elf", binary: "bin/server_bin", }, capabilities: [ { protocol: "examples.keyvaluestore.supporttrees.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.supporttrees.Store", from: "self", }, ], }
領域
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. { children: [ { name: "client", url: "#meta/client.cm", }, { name: "server", url: "#meta/server.cm", }, ], offer: [ // Route the protocol under test from the server to the client. { protocol: "examples.keyvaluestore.supporttrees.Store", from: "#server", to: "#client", }, // Route diagnostics support to all children. { protocol: [ "fuchsia.inspect.InspectSink", "fuchsia.logger.LogSink", ], from: "parent", to: [ "#client", "#server", ], }, ], }
用戶端和伺服器實作項目現在可以使用任何支援的語言編寫:
Rust
用戶端
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use { anyhow::{Context as _, Error}, config::Config, fidl_examples_keyvaluestore_supporttrees::{Item, NestedStore, StoreMarker, Value}, fuchsia_component::client::connect_to_protocol, std::{thread, time}, }; #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Load the structured config values passed to this component at startup. let config = Config::take_from_startup_handle(); // Use the Component Framework runtime to connect to the newly spun up server component. We wrap // our retained client end in a proxy object that lets us asynchronously send `Store` requests // across the channel. let store = connect_to_protocol::<StoreMarker>()?; println!("Outgoing connection enabled"); // This client's structured config has one parameter, a vector of strings. Each string is the // path to a resource file whose filename is a key and whose contents are a value. We iterate // over them and try to write each key-value pair to the remote store. for key in config.write_items.into_iter() { let path = format!("/pkg/data/{}.txt", key); let value = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; let res = store .write_item(&Item { key: key.clone(), value: Some(Box::new(Value::Bytes(value.into_bytes()))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Add nested entries to the key-value store as well. The entries are strings, where the first // line is the key of the entry, and each subsequent entry should be a pointer to a text file // containing the string value. The name of that text file (without the `.txt` suffix) will // serve as the entries key. for spec in config.write_nested.into_iter() { let mut items = vec![]; let mut nested_store = NestedStore::default(); let mut lines = spec.split("\n"); let key = lines.next().unwrap(); // For each entry, make a new entry in the `NestedStore` being built. for entry in lines { let path = format!("/pkg/data/{}.txt", entry); let contents = std::fs::read_to_string(path.clone()) .with_context(|| format!("Failed to load {path}"))?; items.push(Some(Box::new(Item { key: entry.to_string(), value: Some(Box::new(Value::Bytes(contents.into()))), }))); } nested_store.items = Some(items); // Send the `NestedStore`, represented as a vector of values. let res = store .write_item(&Item { key: key.to_string(), value: Some(Box::new(Value::Store(nested_store))), }) .await; match res? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // Each entry in this list is a null value in the store. for key in config.write_null.into_iter() { match store.write_item(&Item { key: key.to_string(), value: None }).await? { Ok(_) => println!("WriteItem Success at key: {}", key), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // TODO(https://fxbug.dev/42156498): We need to sleep here to make sure all logs get drained. Once the // referenced bug has been resolved, we can remove the sleep. thread::sleep(time::Duration::from_secs(2)); Ok(()) }
伺服器
// Copyright 2022 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Note: For the clarity of this example, allow code to be unused. #![allow(dead_code)] use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_supporttrees::{ Item, StoreRequest, StoreRequestStream, Value, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, std::str::from_utf8, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z]\w+[A-Za-z0-9]$").expect("Key validation regex failed to compile"); } // A representation of a key-value store that can contain an arbitrarily deep nesting of other // key-value stores. enum StoreNode { Leaf(Option<Vec<u8>>), Branch(Box<HashMap<String, StoreNode>>), } /// Recursive item writer, which takes a `StoreNode` that may not necessarily be the root node, and /// writes an entry to it. fn write_item( store: &mut HashMap<String, StoreNode>, attempt: Item, path: &str, ) -> Result<(), WriteError> { // Validate the key. if !KEY_VALIDATION_REGEX.is_match(attempt.key.as_str()) { println!("Write error: INVALID_KEY, For key: {}", attempt.key); return Err(WriteError::InvalidKey); } // Write to the store, validating that the key did not already exist. match store.entry(attempt.key) { Entry::Occupied(entry) => { println!("Write error: ALREADY_EXISTS, For key: {}", entry.key()); Err(WriteError::AlreadyExists) } Entry::Vacant(entry) => { let key = format!("{}{}", &path, entry.key()); match attempt.value { // Null entries are allowed. None => { println!("Wrote value: NONE at key: {}", key); entry.insert(StoreNode::Leaf(None)); } Some(value) => match *value { // If this is a nested store, recursively make a new store to insert at this // position. Value::Store(entry_list) => { // Validate the value - absent stores, items lists with no children, or any // of the elements within that list being empty boxes, are all not allowed. if entry_list.items.is_some() { let items = entry_list.items.unwrap(); if !items.is_empty() && items.iter().all(|i| i.is_some()) { let nested_path = format!("{}/", key); let mut nested_store = HashMap::<String, StoreNode>::new(); for item in items.into_iter() { write_item(&mut nested_store, *item.unwrap(), &nested_path)?; } println!("Created branch at key: {}", key); entry.insert(StoreNode::Branch(Box::new(nested_store))); return Ok(()); } } println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } // This is a simple leaf node on this branch. Value::Bytes(value) => { // Validate the value. if value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", key); return Err(WriteError::InvalidValue); } println!("Wrote key: {}, value: {:?}", key, from_utf8(&value).unwrap()); entry.insert(StoreNode::Leaf(Some(value))); } }, } Ok(()) } } } /// Creates a new instance of the server. Each server has its own bespoke, per-connection instance /// of the key-value store. async fn run_server(stream: StoreRequestStream) -> Result<(), Error> { // Create a new in-memory key-value store. The store will live for the lifetime of the // connection between the server and this particular client. let store = RefCell::new(HashMap::<String, StoreNode>::new()); // Serve all requests on the protocol sequentially - a new request is not handled until its // predecessor has been processed. stream .map(|result| result.context("failed request")) .try_for_each(|request| async { // Match based on the method being invoked. match request { StoreRequest::WriteItem { attempt, responder } => { println!("WriteItem request received"); // The `responder` parameter is a special struct that manages the outgoing reply // to this method call. Calling `send` on the responder exactly once will send // the reply. responder .send(write_item(&mut store.borrow_mut(), attempt, "")) .context("error sending reply")?; println!("WriteItem response sent"); } StoreRequest::_UnknownMethod { ordinal, .. } => { println!("Received an unknown method with ordinal {ordinal}"); } } Ok(()) }) .await } // A helper enum that allows us to treat a `Store` service instance as a value. enum IncomingService { Store(StoreRequestStream), } #[fuchsia::main] async fn main() -> Result<(), Error> { println!("Started"); // Add a discoverable instance of our `Store` protocol - this will allow the client to see the // server and connect to it. let mut fs = ServiceFs::new_local(); fs.dir("svc").add_fidl_service(IncomingService::Store); fs.take_and_serve_directory_handle()?; println!("Listening for incoming connections"); // The maximum number of concurrent clients that may be served by this process. const MAX_CONCURRENT: usize = 10; // Serve each connection simultaneously, up to the `MAX_CONCURRENT` limit. fs.for_each_concurrent(MAX_CONCURRENT, |IncomingService::Store(stream)| { run_server(stream).unwrap_or_else(|e| println!("{:?}", e)) }) .await; Ok(()) }
C++ (自然)
用戶端
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
伺服器
// TODO(https://fxbug.dev/42060656): C++ (Natural) implementation.
C++ (有線)
用戶端
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
伺服器
// TODO(https://fxbug.dev/42060656): C++ (Wire) implementation.
HLCPP
用戶端
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
伺服器
// TODO(https://fxbug.dev/42060656): HLCPP implementation.
fi-0194
fi-0195
fi-0196
fi-0200
fi-0201:未選取平台版本
如果您在編譯版本化 FIDL 程式庫的情況下未選擇 版本:
// fidlc --files test.fidl --out test.json
@available(platform="foo", added=1)
library test.bad.fi0201;
如要修正問題,請選擇具有 --available
指令列旗標的版本:
// fidlc --files test.fidl --out test.json --available foo:1
@available(platform="foo", added=1)
library test.good.fi0201;
版本必須是大於或等於 1 的數字,或為
版本 NEXT
和 HEAD
。詳情請參閱
FIDL 版本管理。
fi-0202
fi-0203:移除並取代為互斥鎖
@available
屬性支援 removed
和 replaced
引數,但
無法同時使用:
@available(added=1)
library test.bad.fi0203;
protocol Foo {
@available(removed=2, replaced=2)
Foo();
};
如要修正錯誤,請刪除其中一個引數。如果您有意移除
,但保留 removed
並刪除 replaced
:
@available(added=1)
library test.good.fi0203a;
open protocol Foo {
@available(removed=2)
strict Foo();
};
此外,如果您要以新的定義「替換」元素,請保留
replaced
並刪除 removed
:
@available(added=1)
library test.good.fi0203b;
open protocol Foo {
@available(replaced=2)
strict Foo();
@available(added=2)
flexible Foo();
};
同時使用 removed
和 replaced
並不合理,因為兩者結合
相反地當元素標示為 removed
時,fidlc 會驗證
但「不會」在同一個版本中加入取代元素。如果
元素標示為 replaced
,fidlc 會驗證該元素是否由「IS」取代
加入同一個版本的其他元素
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0204:無法取代程式庫
@available
屬性的 replaced
引數無法用於程式庫
宣告:
@available(added=1, replaced=2)
library test.bad.fi0204;
請改用 removed
引數:
@available(added=1, removed=2)
library test.good.fi0204;
replaced
引數表示元素已由新的
定義這種做法不適用於整個程式庫,因為我們假設每個程式庫都採用該方法
程式庫中只有一個檔案定義了這個容器。
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0205:@available(removed=N)
無效
元素標示為 @available(removed=N)
時,表示該元素不可
不符合使用的版本 N
您無法重複使用這個名稱:
@available(added=1)
library test.bad.fi0204;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible Bar();
};
如要以新定義取代元素 (相同的 API 和 ABI),請使用
replaced
引數,而不是 removed
引數:
@available(added=1)
library test.good.fi0204a;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
如果您想移除元素並定義一個不相關的新元素 (使用 API 和 ABI),請為新元素選擇其他名稱:
@available(added=1)
library test.good.fi0204b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible NewBar();
};
如果您希望重複使用相同的名稱 (相同的 API、不同的 ABI),請使用
在移除後重新命名舊元素,並釋出 renamed
引數
原始名稱:
@available(added=1)
library test.good.fi0204c;
open protocol Foo {
@available(removed=2, renamed="DeprecatedBar")
strict Bar();
@available(added=2)
@selector("NewBar")
flexible Bar();
};
請注意,在此情況下,您必須使用 @selector
,確保新方法擁有
不同的 ABI
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0206:@available(replaced=N)
無效
元素標示為 @available(replaced=N)
時,表示該元素
已由標示 @available(added=N)
的新定義取代。FIDL 編譯器
找不到這樣的定義時,就會回報錯誤:
@available(added=1)
library test.bad.fi0205;
open protocol Foo {
@available(replaced=2)
strict Bar();
};
如果您不想取代元素,請改用 removed
引數
replaced
引數中傳回的值:
@available(added=1)
library test.good.fi0205a;
open protocol Foo {
@available(removed=2)
strict Bar();
};
如果您想替換元素,請新增替換定義:
@available(added=1)
library test.good.fi0205b;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0207:類型形狀整數溢位
FIDL 類型不得太大,因此大小會溢位 uint32
:
library test.bad.fi0207;
type Foo = struct {
bytes array<uint64, 536870912>;
};
如要修正錯誤,請使用較小的陣列大小:
library test.good.fi0207;
type Foo = struct {
bytes array<uint64, 100>;
};
實務中的 FIDL 類型應該小於 232 個位元組,因為 通常是透過 Zircon 頻道傳送,每台上限為 64 KiB 訊息。
fi-0208:保留平台
某些平台名稱由 FIDL 保留。例如「反版本」 平台是保留的程式庫,用於代表未使用版本管理的程式庫:
@available(platform="unversioned", added=1)
library test.bad.fi0208;
請改為選擇其他平台名稱:
@available(platform="foo", added=1)
library test.good.fi0208;
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0209:不允許使用保留欄位
FIDL 不再支援 reserved
資料表或聯集欄位:
library test.bad.fi0209;
type User = table {
1: reserved;
2: email string;
};
保留欄位的主要用途是避免意外重複使用一般項目。
FIDL 版本管理
這種狀況您可以在 @available(removed=N)
舊欄位中加上註解
將這些內容 (及其標準) 在來源檔案中保留:
@available(added=1)
library test.good.fi0209a;
type User = table {
@available(removed=2)
1: name string;
2: email string;
};
reserved
的另一個使用方式是記錄序數未來預定的使用方式。
在這種情況下,請考慮在不穩定的 API 級別 HEAD
中定義欄位:
@available(added=1)
library test.good.fi0209b;
type User = table {
@available(added=HEAD)
1: name string;
2: email string;
};
如果是其他 reserved
用途,建議改為留言:
library test.good.fi0209c;
type User = table {
// We skip ordinal 1 because...
2: email string;
};
fi-0210:@discoverable 用戶端或伺服器引數中的位置無效
可搜尋通訊協定的 client
或 server
位置必須是
(可能空白) 以半形逗號分隔的 platform
和 external
清單。也就是說:
- ""
- "platform"
- "external"
- "platform,external"
- "external,platform"
library test.bad.fi0210;
@discoverable(server="platform,canada")
protocol Example {};
請確認下列引數正確無誤:
library test.good.fi0210;
@discoverable(server="platform")
protocol Example {};
fi-0211:元素無法重新命名
@available
屬性的 renamed
引數只能用於
而非宣告本身:
@available(added=1)
library test.bad.fi0211;
@available(replaced=2, renamed="Bar")
type Foo = struct {};
@available(added=2)
type Bar = struct {};
與其重新命名宣告,不如移除舊聲明然後新增宣告:
@available(added=1)
library test.good.fi0211;
@available(removed=2)
type Foo = struct {};
@available(added=2)
type Bar = struct {};
只有成員才能重新命名,因為 FIDL 編譯器可比較 並要求其 ABI 身分 (例如資料表序數) 確保工作正確執行。
下列元素也不允許使用 renamed
引數:
library
:您無法從內部重新命名程式庫,因為 FIDL 工具鍊 假設每個程式庫都有一個名稱相反地 並使用所需名稱遷移使用者。compose
:您無法重新命名通訊協定組合,因為組成具有 開頭沒有名稱
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0212:重新命名,但未取代或移除
系統不允許單獨使用 @available
引數 renamed
。必須使用
與 replaced
或 removed
引數一起顯示:
@available(added=1)
library test.bad.fi0212;
protocol Foo {
@available(renamed="New")
Old();
};
如果只想重新命名 N
版本的元素,請使用 replaced=N
並
請使用標示為 added=N
的新名稱定義替代項目:
@available(added=1)
library test.good.fi0212a;
protocol Foo {
@available(replaced=2, renamed="New")
Old();
@available(added=2)
@selector("Old")
New();
};
在這種情況下,替代方法必須覆寫 ABI 的 @selector
相容性。
或者,如果您要移除版本 N
的元素並參照該元素
以其他名稱取代,請使用 removed=N
:
@available(added=1)
library test.good.fi0212b;
protocol Foo {
@available(removed=2, renamed="DeprecatedOld")
Old();
};
在此情況下,只有在指定多個版本時,才會使用新名稱
(例如 --available test:1,2
),因為這是加入元素的唯一方法
同時指定超過移除內容的版本
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0213:已重新命名為同名名稱
使用 @available
引數 renamed
重新命名元素時,新名稱
不得與元素的原始名稱相同:
@available(added=1)
library test.bad.fi0213;
type Foo = table {
@available(replaced=2, renamed="bar")
1: bar string;
@available(added=2)
1: bar string:10;
};
如要修正錯誤,請移除 renamed
引數:
@available(added=1)
library test.good.fi0213a;
type Foo = table {
@available(replaced=2)
1: bar string;
@available(added=2)
1: bar string:10;
};
或者,您也可以保留 renamed
引數,但選擇其他名稱:
@available(added=1)
library test.good.fi0213b;
type Foo = table {
@available(replaced=2, renamed="baz")
1: bar string;
@available(added=2)
1: baz string:10;
};
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0214:@available(removed=N, renamed="NewName")
無效
這就像 fi-0205:@available(removed=N)
無效,但適用於
renamed
引數牽涉到。
當元素標示為 @available(removed=N, renamed="NewName")
時,
該元素已無法在 N
版中使用,並重新命名為「NewName」
移除後的版本您無法使用「NewName」其他:
@available(added=1)
library test.bad.fi0214;
open protocol Foo {
@available(removed=2, renamed="NewName")
strict OldName();
@available(added=2)
flexible NewName();
};
如要重新命名元素,同時保留 ABI,請使用 replaced
引數而非 removed
引數:
@available(added=1)
library test.good.fi0214a;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
@available(added=2)
@selector("OldName")
flexible NewName();
};
請注意,在此情況下,您必須使用 @selector
確保重新命名的方法
也具備相同的 ABI
如要讓新元素使用不同的 ABI,請保留 removed
和
請確認 renamed
引數和新元素的名稱不同:
@available(added=1)
library test.good.fi0214b;
open protocol Foo {
@available(removed=2, renamed="NewName")
strict OldName();
@available(added=2)
strict DifferentName();
};
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0215:@available(replaced=N, renamed="NewName")
無效
這就像 fi-0206:@available(replaced=N)
無效,但適用於
renamed
引數牽涉到。
當元素標示為 @available(replaced=N, renamed="NewName")
時,
該元素會由名為「NewName」的新定義所取代並加上
@available(added=N)
。如果 FIDL 編譯器找不到,則會回報錯誤
稱為:
@available(added=1)
library test.bad.fi0215;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
};
如要修正錯誤,請使用新名稱定義元素:
@available(added=1)
library test.good.fi0215;
open protocol Foo {
@available(replaced=2, renamed="NewName")
strict OldName();
@available(added=2)
@selector("OldName")
strict NewName();
};
請注意,您必須使用 @selector
確保重新命名的方法相同
ABI。
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0216:@available(removed=N)
無效 (ABI)
這就像 fi-0205:@available(removed=N)
無效,但是
,那麼其 ABI 為:
@available(added=1)
library test.bad.fi0216;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
@selector("Bar")
flexible Qux();
};
如要刻意取代元素的 ABI,請使用
replaced
和 renamed
引數,而不是 removed
:
@available(added=1)
library test.good.fi0216a;
open protocol Foo {
@available(replaced=2, renamed="Qux")
strict Bar();
@available(added=2)
@selector("Bar")
flexible Qux();
};
如果您並不想重複使用 ABI,請選擇其他 ABI。在本例中
可以移除 @selector
屬性,並使用該方法的預設選取器
以名稱表示:
@available(added=1)
library test.good.fi0216b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible Qux();
};
其他成員 (非只有方法) 可能會發生這個錯誤。適用於位元和列舉 成員,ABI 為整數值。對於 struct 成員,ABI 是位元組 偏移量。對於資料表和聯集成員,ABI 是序數。
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0217:@available(replaced=N)
無效 (ABI)
這就像 fi-0206:@available(replaced=N)
無效,但適用於
取代項目只會與元素名稱相符,而非其 ABI:
@available(added=1)
library test.bad.fi0217;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
@selector("NotBar")
flexible Bar();
};
如果您想替換元素,請確認其 ABI 相符。在本例中
因為這兩種方法具有相同的 @selector
屬性,所以我們可以移除
名稱:
@available(added=1)
library test.good.fi0217a;
open protocol Foo {
@available(replaced=2)
strict Bar();
@available(added=2)
flexible Bar();
};
如果您不想取代 ABI,請使用 removed
,而非 replaced
。
在此案例中,我們也必須選擇其他名稱,以免與
舊型:
@available(added=1)
library test.good.fi0217b;
open protocol Foo {
@available(removed=2)
strict Bar();
@available(added=2)
flexible NotBar();
};
如果您想重複使用名稱,而非 ABI,請使用 removed
,而不要使用 ABI
replaced
,也是使用 renamed
在移除後重新命名舊元素。
釋放原始名稱:
@available(added=1)
library test.good.fi0217c;
open protocol Foo {
@available(removed=2, renamed="DeprecatedBar")
strict Bar();
@available(added=2)
@selector("NotBar")
flexible Bar();
};
其他成員 (非只有方法) 可能會發生這個錯誤。適用於位元和列舉 成員,ABI 為整數值。對於 struct 成員,ABI 是位元組 偏移量。對於資料表和聯集成員,ABI 是序數。
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0218:修飾符可用性引數無效
在 FIDL 版本管理修飾符語法中,您只能使用 added
引數
和 removed
。不得使用其他 @available
引數 (例如 deprecated
):
@available(added=1)
library test.bad.fi0218;
type Foo = resource(deprecated=2) struct {};
如要修正錯誤,請移除不支援的引數:
@available(added=1)
library test.good.fi0218;
type Foo = resource struct {};
與宣告和成員不同,修飾符沒有專屬的生命週期。 因此淘汰、替換和重新命名等概念 具體做法是指示 Kubernetes 建立並維護 一或多個代表這些 Pod 的物件修飾符只能新增及移除。
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0219:無法變更方法的嚴格程度
FIDL 版本管理修飾符語法可讓您新增或移除 strict
flexible
修飾符。但不適用於雙向方式
不使用 error
語法,因為這類變更會導致 ABI 破壞:
@available(added=1)
library test.bad.fi0219;
open protocol Foo {
strict(removed=2) flexible(added=2) Method() -> ();
};
請改為移除嚴格方法,並新增具有其他彈性方法的彈性方法 取代該圖片:
@available(added=1)
library test.good.fi0219a;
open protocol Foo {
@available(removed=2)
strict Method() -> ();
@available(added=2)
flexible NewMethod() -> ();
};
或者,您也可以使用 renamed
引數和 @selector
屬性,如要為新 ABI 重複使用方法名稱,則建議使用這個屬性:
@available(added=1)
library test.good.fi0219b;
open protocol Foo {
@available(removed=2, renamed="StrictMethod")
strict Method() -> ();
@available(added=2)
@selector("FlexibleMethod")
flexible Method() -> ();
};
不允許在沒有錯誤語法的情況下變更雙向方法的嚴格程度 因為這會改變回應形狀如果雙向解決方式 是靈活彈性或使用錯誤語法,FIDL 會自動產生結果聯集, 則會納入回應因此,限制嚴格程度只有雙向性 傳回錯誤語法的方法
請參閱 FIDL 版本管理,進一步瞭解版本管理。
fi-0220:版本範圍中找不到名稱
系統在找不到特定版本的名稱時,就會發生這個錯誤
。舉例來說,在第 1 版新增的元素 Foo
不能
參照在第 2 版新增的其他元素 Bar
,因為 Bar
不存在
版本 1:
@available(added=1)
library test.bad.fi0220;
alias Foo = Bar;
@available(added=2)
type Bar = struct {};
在本例中,我們可以修正錯誤,新增 @available
屬性來使 Foo
還會加入下列版本 2:
@available(added=1)
library test.good.fi0220;
@available(added=2)
alias Foo = Bar;
@available(added=2)
type Bar = struct {};
請參閱 FIDL 版本管理,進一步瞭解版本管理。