本文件是 Fuchsia 介面定義語言的規格 (FIDL)。
如需進一步瞭解 FIDL 的整體用途、目標與需求, 請參閱「總覽」。
另請參閱 FIDL 文法的 EBNF 說明。
語法
FIDL 提供宣告資料類型和通訊協定的語法。這些 宣告方式被收集到程式庫以進行發布。
FIDL 宣告是以 UTF-8 文字檔案儲存。每個檔案都包含一個序列 。FIDL 中的宣告順序 檔案或程式庫中的 FIDL 檔案無關。
留言
FIDL 註解以兩個正斜線 (//
) 開頭,持續到結尾
即可呼叫以三個正斜線 (///
) 開頭的註解
說明文件註解,並在產生的繫結中以註解形式傳送。
// this is a comment
/// and this one is too, but it also ends up in the generated code
type MyStruct = struct { // plain comment
f int32; // as is this one
}; // and this is the last one!
關鍵字
下列字詞在 FIDL 中有特殊意義:
ajar, alias, as, bits, closed, compose, const, enum, error, false, flexible,
library, open, optional, protocol, resource, service, strict, struct, table,
true, type, union, using.
但 FIDL 沒有保留的關鍵字。例如:
// Declaring a struct named "struct" is allowed, though confusing.
type struct = struct {};
// Declaring a table field named "strict" is a more reasonable example.
type Options = table {
1: strict bool;
};
ID
程式庫名稱
FIDL「程式庫名稱」標籤為「FIDL 程式庫」。其中包含
更多以點 (.
) 分隔的元件每個元件都必須與規則運算式相符
[a-z][a-z0-9]*
。簡言之,程式庫名稱元件的開頭必須為小寫
可以使用小寫英文字母和數字,
// A library named "foo".
library foo;
ID
FIDL 標籤宣告及其成員。這些項目必須符合
規則運算式 [a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])?
。換句話說:ID 的開頭必須是
可以使用英文字母,可以包含英文字母、數字和底線,但不得使用結尾
這裡會顯示一個底線
// A struct named "Foo".
type Foo = struct {};
FIDL ID 會區分大小寫。不過,ID 不得重複
標準形式,否則 FIDL 編譯器會失敗,並顯示 fi-0035:
正規名稱衝突。
可從 ID 轉換為 snake_case
來取得 ID 的標準格式。
有效 ID
FIDL 一律會在目前的範圍內尋找不合格的符號 資源庫。如要參照其他程式庫中的符號,您必須使用 程式庫名稱或別名
objects.fidl:
library objects;
using textures as tex;
protocol Frob {
// "Thing" refers to "Thing" in the "objects" library
// "tex.Color" refers to "Color" in the "textures" library
Paint(struct { thing Thing; color tex.Color; });
};
type Thing = struct {
name string;
};
textures.fidl:
library textures;
type Color = struct {
rgba uint32;
};
解析度演算法
FIDL 採用以下演算法來解析 ID。當「嘗試解決」時 遷移失敗,請繼續下一個步驟。「解決」狀態該步驟失敗, 編譯器會產生錯誤。
- 如果代碼不符合資格:
- 請嘗試在目前程式庫中將宣告視為宣告。
- 請嘗試以內建宣告的形式重新修改,例如:
bool
是指fidl.bool
。 - 解析為結構定義位元/列舉成員,例如
CHANNEL
的zx.handle:CHANNEL
是指zx.ObjType.Channel
。
- 如果不符合
X.Y
的資格:- 請嘗試在目前程式庫中將
X
解析為宣告:- 以
X
的成員身分解決Y
。
- 以
- 將
X
解析為程式庫名稱或別名。- 將
Y
視為X
中的宣告。
- 將
- 請嘗試在目前程式庫中將
- 如果資格為
x.Y.Z
,其中x
代表一或多個元件:- 請嘗試將
x.Y
解析為程式庫名稱或別名:- 將
Z
視為x.Y
中的宣告。
- 將
- 將
x
解析為程式庫名稱或別名:- 將
Y
視為x
中的宣告方式:- 以
x.Y
的成員身分解決Z
。
- 以
- 將
- 請嘗試將
完整名稱
FIDL 使用完整名稱 (簡稱「FQN」) 明確稱呼
宣告和成員程式庫名稱的 FQN 圖示,斜線 /
、
宣告 ID,也可以視需要加入點 .
和成員 ID。例如:
fuchsia.io/MAX_BUF
是指程式庫fuchsia.io
中的MAX_BUF
常數。fuchsia.process/Launcher.Launch
是指Launch
fuchsia.process
程式庫的Launcher
通訊協定。
FQN 用在錯誤訊息、FIDL JSON 中繼表示法 方法選取器和說明文件註解交叉參照。
常值
FIDL 支援下列種類的文字:
- 布林值:
true
、false
- 整數:十進位 (
123
)、十六進位 (0xA1B2
)、八進位 (0755
)、二進位 (0b101
)。- 只有小數常值可以為負數。
- 浮點值:
1.23
、-0.01
、1e5
、2.0e-3
- 系統只允許使用
e
和e-
,但不允許e+
。
- 系統只允許使用
- 字串:
"hello"
、"\\ \" \n \r \t \u{1f642}"
數字常值中的所有字母 (例如十六進位數字) 不區分大小寫。
常數
FIDL 允許為所有支援常值 (布林值、 整數、浮點數和字串) 以及位元和列舉。例如:
const ENABLED_FLAG bool = true;
const OFFSET int8 = -33;
const ANSWER uint16 = 42;
const ANSWER_IN_BINARY uint16 = 0b101010;
const POPULATION_USA_2018 uint32 = 330000000;
const DIAMOND uint64 = 0x183c7effff7e3c18;
const FUCHSIA uint64 = 4054509061583223046;
const USERNAME string = "squeenze";
const MIN_TEMP float32 = -273.15;
const CONVERSION_FACTOR float64 = 1.41421358;
const MY_DRINK Beverage = Beverage.WATER;
const FEATURES InfoFeatures = InfoFeatures.WLAN | InfoFeatures.SYNTH;
常數運算式可以是常值、參照其他常數的參照
參照位元或列舉成員,或兩組分隔的位元成員
除以直立線字元 (|
)。FIDL 不支援其他任何算術
運算式,例如 1 + 2
。
宣告分隔符
FIDL 使用分號 (;
) 來分隔檔案內相鄰的宣告內容,
檔案,很像 C
程式庫
程式庫是 FIDL 宣告的已命名容器。
// library identifier separated by dots
library fuchsia.composition;
// "using" to import library "fuchsia.mem"
using fuchsia.mem;
// "using" to import library "fuchsia.geometry" under the alias "geo"
using fuchsia.geometry as geo;
程式庫會透過 using
宣告匯入其他程式庫。您可以參考
為匯入的程式庫中的符號進行限定,例如:
fuchsia.mem.Range
。使用 using ... as
語法時,您必須限定符號
將金鑰替換為別名,例如 geo.Rect
(不支援 fuchsia.geometry.Rect
)。
library
和 using
宣告的範圍僅限於單一檔案。
FIDL 程式庫中的每個檔案都必須重述 library
宣告和任何
using
會宣告檔案需要。
FIDL 編譯器不需要任何特定的目錄結構,但每個
FIDL 程式庫通常存放在以程式庫命名的專屬目錄中。
包含多個檔案的程式庫通常會有 Overview.fidl
檔案,當中只包含 library
宣告,以及
以及說明文件註解
程式庫的名稱可在語言繫結中使用做為命名空間。例如:
C++ 繫結產生器會為 FIDL 程式庫 fuchsia.ui
提出宣告
在 C++ 命名空間 fuchsia_ui
中同樣地,Rust 繫結產生器
會產生名為 fidl_fuchsia_ui
的 Crate。
類型和類型宣告
FIDL 支援多種內建類型和新型別宣告 (例如結構體、聯集、型別別名) 和通訊協定。
基本
- 簡易值類型。
- 不得為選用項目。
FIDL 支援下列原始類型:
- 布林值
bool
- 帶號整數
int8 int16 int32 int64
- 非帶號整數
uint8 uint16 uint32 uint64
- IEEE 754 浮點值
float32 float64
數字後面會加上其大小 (以位元為單位)。舉例來說,int8
為 1 個位元組。
使用
// A record which contains fields of a few primitive types.
type Sprite = struct {
x float32;
y float32;
index uint32;
color uint32;
visible bool;
};
點數
- 已命名位元類型。
- 從基礎整數類型選擇的個別位元值子集。
- 不得為選用項目。
- 位元可以是
strict
或flexible
。 - 位元預設為
flexible
。 strict
位元至少要有一位成員,flexible
位元可以留空。
運算子
|
是位元的位元 OR 運算子。
使用
type InfoFeatures = strict bits : uint8 {
/// If present, this device represents WLAN hardware
WLAN = 0x01;
/// If present, this device is synthetic (not backed by h/w)
SYNTH = 0x02;
/// If present, this device receives all messages it sends
LOOPBACK = 0x04;
};
// Underlying type is assumed to be uint32.
type AllowableSegments = flexible bits {
TOLL_ROADS = 0b001;
HIGHWAYS = 0b010;
BIKE_PATHS = 0b100;
};
const ROADS AllowableSegments = AllowableSegments.TOLL_ROADS | AllowableSegments.HIGHWAYS;
列舉
- 正確的列舉類型。
- 從基礎整數類型選擇的具名值子集。
- 不得為選用項目。
- 列舉可以是
strict
或flexible
。 - 列舉預設值為
flexible
。 strict
列舉至少要有一個成員,flexible
列舉可以空白。
宣告
序數是每個列舉元素必要項目。基本類型 列舉必須是 int8, uint8, int16, uint16, int32, uint32, int64, uint64。如果省略,基礎類型會預設為 uint32。
type Beverage = flexible enum : uint8 {
WATER = 0;
COFFEE = 1;
TEA = 2;
WHISKEY = 3;
};
// Underlying type is assumed to be uint32.
type Vessel = strict enum {
CUP = 0;
BOWL = 1;
TUREEN = 2;
JUG = 3;
};
使用
列舉類型會以 ID 表示,必要時或許符合資格。
// A record which contains two enum fields.
type Order = struct {
beverage Beverage;
vessel Vessel;
};
FIDL 方案:Enum
列舉是一種 FIDL 資料類型,代表一組固定清單 常數,例如紙牌上的西裝或使用者的汽車品牌 可以從下拉式選單中選取接著,這個清單中的值會對應到 基本整數類型,其中每個值都會與其中一個 名單成員
以下範例會在列舉為 FIDL 列舉的情況下新增 FIDL 列舉
最適合:列舉可能由
方法呼叫失敗ReadError
列舉有兩名成員:NOT_FOUND
用於
表示系統在讀取嘗試期間無法比對到搜尋值,
如果發生無法明確指出的情況,UNKNOWN
會列為隨身行李錯誤
情境。請注意,這個列舉標示為 flexible
,方便您輕鬆使用
需隨著日後新會員進化。
原因
原始的唯寫鍵/值儲存庫現已使用 使用者可以將商品讀回商店外
實作
套用至 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.addreaditem; // Aliases for the key and value. Using aliases helps increase the readability of FIDL files and // reduces likelihood of errors due to differing constraints. alias Key = string:128; alias Value = vector<byte>:64000; /// 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 Key; value Value; }; /// 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; }; /// An enumeration of things that may go wrong when trying to read a value out of our store. type ReadError = flexible enum { UNKNOWN = 0; NOT_FOUND = 1; }; /// A very basic key-value store - so basic, in fact, that one may only write to it, never read! @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; /// Reads an item from the store. flexible ReadItem(struct { key Key; }) -> (Item) error ReadError; };
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.addreaditem.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, read_items: { 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.addreaditem.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.addreaditem.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.addreaditem.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}; use config::Config; use fidl_examples_keyvaluestore_addreaditem::{Item, StoreMarker}; use fuchsia_component::client::connect_to_protocol; use std::{str, 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}"))?; match store.write_item(&Item { key: key, value: value.into_bytes() }).await? { Ok(_) => println!("WriteItem Success"), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // The structured config for this client contains `read_items`, a vector of strings, each of // which is meant to be read from the key-value store. We iterate over these keys, attempting to // read them in turn. for key in config.read_items.into_iter() { let res = store.read_item(key.as_str()).await; match res.unwrap() { Ok(val) => { println!("ReadItem Success: key: {}, value: {}", key, str::from_utf8(&val.1)?) } Err(err) => println!("ReadItem 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. use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_addreaditem::{ Item, ReadError, StoreRequest, StoreRequestStream, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z][A-Za-z0-9_\./]{2,62}[A-Za-z0-9]$") .expect("Key validation regex failed to compile"); } /// Handler for the `WriteItem` method. fn write_item(store: &mut HashMap<String, Vec<u8>>, attempt: Item) -> 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); } // Validate the value. if attempt.value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", attempt.key); return Err(WriteError::InvalidValue); } // 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) => { println!("Wrote value at key: {}", entry.key()); entry.insert(attempt.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, Vec<u8>>::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::ReadItem { key, responder } => { println!("ReadItem request received"); // Read the item from the store, returning the appropriate error if it could not be found. responder .send(match store.borrow().get(&key) { Some(found) => { println!("Read value at key: {}", key); Ok((&key, found)) } None => { println!("Read error: NOT_FOUND, For key: {}", key); Err(ReadError::NotFound) } }) .context("error sending reply")?; println!("ReadItem 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.
陣列
- 同質元素的固定長度序列。
- 元素可以是任何類型。
- 不得為選用本身;可以包含選用類型。
使用
陣列會表示 array<T, N>
,其中 T 可以是任何 FIDL 類型 (包括
陣列),且「N」是正整數常數運算式,會指定
陣列中的元素數量。
// A record which contains some arrays.
type Arrays = struct {
// array of exactly 16 floating point numbers
matrix array<float32, 16>;
// array of exactly 10 arrays of 4 strings each
form array<array<string, 4>, 10>;
};
請注意,N 會顯示為版面配置參數,這表示該參數會影響 ABI
和類型換句話說,將 _N_
參數變更為
ABI 突破變更。
字串
- 變數長度序列,代表 UTF-8 編碼的文字。
- 可以是選用項目;沒有字串和空字串是不同的值
- 可指定大小上限,例如最大 40 個位元組的
string:40
字串。根據預設,string
表示string:MAX
,表示不受限。 - 字串常值支援逸出序列
\\
、\"
、\n
、\r
、\t
、 和\u{X}
,其中X
為 1 到 6 個十六進位代碼,代表萬國碼 (Unicode) 碼點。 - 可能包含嵌入式
NUL
個位元組,與傳統 C 字串不同。
使用
字串表示方式如下:
string
:必要字串 (驗證錯誤) 則會發生)string:optional
:選用字串string:N, string:<N, optional>
:分別為字串和選用字串。 長度上限為 N 個位元組
// A record which contains some strings.
type Document = struct {
// title string, maximum of 40 bytes long
title string:40;
// description string, may be null, no upper bound on size
description string:optional;
};
請注意,N 會顯示為限制 (出現在 :
之後),也就是說,
但不會影響型別的 ABI。也就是將
參數 _N_
不是 ABI 破壞性變更。
繫結會強制執行,因此不應使用字串傳遞任意二進位資料 有效的 UTF-8 編碼。如果是小型資料,請考慮改用
bytes
fuchsia.mem.Buffer
適用於 blob。詳情請見 我該使用字串還是向量? 。
向量
- 同質元素的可變長度序列。
- 可以是選用項目;但不含向量和空向量
- 可指定大小上限,例如
vector<T>:40
,最多可包含 40 個元素 向量。根據預設,vector<T>
表示vector<T>:MAX
,表示不受限。 - 布林值向量並沒有特殊案例。每個布林值元素 像系統一樣
使用
向量的表示方式如下:
vector<T>
:元素類型的必要向量 T (如果不存在,會發生驗證錯誤)vector<T>:optional
:可選用的元素向量 四vector<T>:N
和vector<T>:<N, optional>
:向量以及選用的向量 ,每個元素的長度上限為 N 個
T 可以是任何 FIDL 類型。
// A record which contains some vectors.
type Vectors = struct {
// a vector of up to 10 integers
params vector<int32>:10;
// a vector of bytes, no upper bound on size
blob vector<uint8>;
// a nullable vector of up to 24 strings
nullable_vector_of_strings vector<string>:<24, optional>;
// a vector of nullable strings, no upper bound on size
vector_of_nullable_strings vector<string:optional>;
// a vector of vectors of 16-element arrays of floating point numbers
complex vector<vector<array<float32, 16>>>;
};
帳號代碼
- 透過處理常式值傳輸 Zircon 能力。
- 儲存為 32 位元無正負號整數。
- 可以是選用項目;系統會將缺少的控點編碼為零值控制代碼。
- 帳號代碼可選擇與類型和一組必要的 Zircon 建立關聯 權利。
使用
帳號代碼說明:
zx.Handle
:未指定類型的必要 Zircon 處理常式zx.Handle:optional
:未指定類型的選用 Zircon 處理常式zx.Handle:H
:類型為 H 的必要 Zircon 控制代碼zx.Handle:<H, optional>
:選用的 H 類型的 Zircon 控制代碼zx.Handle:<H, R>
:需要 H 類型的必要 Zircon 控制代碼 (具備相關權限 Rzx.Handle:<H, R, optional>
:選用的 H 類型的 Zircon 控制代碼, 權利 R
H 可以是任何支援的物件
Zircon,例如:channel
,thread
,vmo
。詳情請參閱
grammar 來查看完整清單。
R 可以是 Zircon 支援的任何右側。
權利是位元類型的值,定義於 zx
FIDL 程式庫,例如zx.Rights.READ
。無論是傳入或傳出
路線、控制代碼已經過驗證,皆能取得正確的 Zircon 物件型別,並位於
或至少在 FIDL 中指定的權利。如果帳號代碼擁有其他權利
,只要呼叫
zx_handle_replace
。請參閱帳號代碼生命週期範例和RFC-0028:帳號代碼
權利,進一步
詳細資料。
結構體、表格和包含控點的聯集必須標有
resource
修飾符。
// A record which contains some handles.
type Handles = resource struct {
// a handle of unspecified type
h zx.Handle;
// an optional channel
c zx.Handle:<CHANNEL, optional>;
};
Structs
- 記錄類型為一系列的欄位的記錄類型。
- 新增或移除欄位或變更欄位類型通常是 與 ABI 不相容
- 宣告可以包含
resource
修飾符。 - 參考檔案可支援
box
。 - 結構包含零或多個成員。
宣告
type CirclePoint = struct {
x float32;
y float32;
};
type Color = struct {
r float32;
g float32;
b float32;
};
使用
結構體會以宣告的名稱 (例如圓形) 表示:
Circle
:必要的圓形box<Circle>
:選擇性的圓形,已儲存離線。
type Circle = struct {
filled bool;
center CirclePoint; // CirclePoint will be stored in-line
radius float32;
color box<Color>; // Color will be stored out-of-line
dashed bool;
};
表格
- 記錄類型,由一系列型別欄位和序數組成。
- 宣告是用來在出現結構定義變更時,用於前向和回溯相容性。
- 宣告可以包含
resource
修飾符。 - 資料表不得為選用項目。「缺少值」的語意是以空白資料表表示 亦即所有成員都缺席,以免重複交替選擇。
- 資料表含有零或多個成員。
宣告
type Profile = table {
1: locales vector<string>;
2: calendars vector<string>;
3: time_zones vector<string>;
};
使用
資料表會以宣告的名稱 (例如「設定檔」) 表示:
Profile
:必要的設定檔
以下展示 Profile
如何隨之變化,同時攜帶溫度單位。
客戶瞭解先前的「Profile
」定義 (不含溫度單位)
仍可將其設定檔傳送至已更新,以處理
欄位組合。
type TemperatureUnit = enum {
CELSIUS = 1;
FAHRENHEIT = 2;
};
type Profile = table {
1: locales vector<string>;
2: calendars vector<string>;
3: time_zones vector<string>;
4: temperature_unit TemperatureUnit;
};
聯合工會
- 包含序數和信封的記錄類型。
- 序數表示成員選取,信封包含內容。
- 部署完成後仍可修改宣告,同時維持 ABI 相容性。請參閱相容性指南,瞭解 原始碼相容性注意事項
- 宣告可以包含
resource
修飾符。 - 參考檔案可以選擇性使用。
- 聯集可為
strict
或flexible
。 - 聯集預設為
flexible
。 strict
聯集必須包含一或多位成員。沒有成員的聯集 不會有居民,所以使用傳輸方式比較合理。 但是,允許無成員的flexible
聯集,因為仍可 則會解碼無成員聯集 (包含的資料一律為「未知」)。
宣告
/// The result of an operation. A result is either a single number or an
/// [Error] value.
type Result = union {
1: number float64;
2: reserved;
3: error Error;
};
使用
聯集會以宣告的名稱 (例如 Result) 和選填性表示:
Result
:必要結果Result:optional
:選用結果
type Either = strict union {
1: left Left;
2: right Right;
};
嚴格與彈性
FIDL 類型宣告可以有嚴格或彈性行為:
- 除非使用
strict
宣告,否則位元、列舉和聯集可保持彈性 修飾符 - 結構體一律採用嚴格的行為。
- 資料表始終有彈性的行為。
僅適用於嚴格型別,對含有資料的值進行序列化或還原序列化作業 請參閱驗證錯誤。
在這個例子中:
type FlexibleEither = flexible union {
1: left Left;
2: right Right;
};
由於靈活性很高,因此 FlexibleEither
可簡化進化為
則採用第三個變化版本客戶知道先前定義
沒有第三個變化版本的 FlexibleEither
仍可從
加入更多變數。如果
聯集是未知的變化版本,繫結可能會將其公開為不明資料 (即
原始位元組和控點),並允許重新編碼未知的聯集 (例如
以便支援類似 Proxy 的用途提供的互動式方法
如要進一步瞭解彈性類型的不明資料,請參閱繫結
參考資料。
如需更多詳細資料,請參閱 RFC-0033:處理不明欄位和嚴格程度。
價值與資源
每個 FIDL 類型都是「值類型」或「資源類型」。資源 類型包括:
所有其他類型都是值類型。
值類型不得包含資源類型。舉例來說,這不正確:
type Foo = struct { // ERROR: must be "resource struct Foo"
h zx.Handle;
};
即使類型不包含,還是可以使用 resource
修飾符標示
控制代碼如要在
Future,因為要新增或移除 resource
修飾符,
來源相容性注意事項。例如:
// No handles now, but we will add some in the future.
type Record = resource table {
1: str string;
};
// "Foo" must be a resource because it contains "Record", which is a resource.
type Foo = resource struct {
record Record;
};
詳情請參閱「RFC-0057:預設無帳號」。
通訊協定
- 說明可透過管道傳送訊息來叫用的方法。
- 方法以序數識別。編譯器的計算方式如下:
- 取得方法完整名稱的 SHA-256 雜湊。
- 擷取雜湊摘要的前 8 個位元組
- 將這些位元組解譯為小端整數
- 將該值的上位元 (也就是最後一位元) 設為 0。
- 如要覆寫序數,方法可以有
@selector
屬性。如果 屬性是有效的 FQN,將用來取代 FQN 。否則,必須是有效 ID 並用於替換 方法是在建構 FQN 時選取方法名稱。
每個方法宣告都會狀態其引數和結果。
- 如果未宣告任何結果,則方法為單向:沒有回應 但由伺服器產生
- 如果宣告結果 (即使為空白),方法會是雙向的方法: 每次叫用方法都會從伺服器產生回應。
- 如果只宣告結果,此方法稱為 event 的值。然後再定義來自伺服器的垃圾郵件。
- 雙向方法可能會宣告伺服器可傳送的錯誤類型
而不是回應。這個類型必須是
int32
、uint32
或 有enum
個。
通訊協定的伺服器即將關閉管道時 可以選擇傳送 epitaph 訊息給用戶端,表示 連線處理。Epitaph 必須是最後一則訊息 透過管道提交的內容Epitaph 訊息包含 32 位元的 int 類型為 zx_status_t。負值保留給系統使用 錯誤代碼。
ZX_OK
值 (0) 表示作業成功。 應用程式定義的錯誤代碼 (先前定義為所有陽性代碼)zx_status_t
值) 已淘汰。如需更多有關 Epitaph 的詳細資訊,請參見 拒絕 RFC-0031:Typed Epitaphs。如要進一步瞭解zx_status_t
請參閱 RFC-0085:減少 zx_status_t 空間。
宣告
type DivisionError = strict enum : uint32 {
DIVIDE_BY_ZERO = 1;
};
protocol Calculator {
Add(struct {
a int32;
b int32;
}) -> (struct {
sum int32;
});
Divide(struct {
dividend int32;
divisor int32;
}) -> (struct {
quotient int32;
remainder int32;
}) error DivisionError;
Clear();
-> OnError(struct {
status_code uint32;
});
};
使用
通訊協定是以名稱、管道的定向性和 自由選擇:
client_end:Protocol
:透過 FIDL 通訊協定進行通訊的管道用戶端端點client_end:<Protocol, optional>
:上述內容的選用版本server_end:Protocol
:透過 FIDL 通訊協定通訊的管道端點server_end:<Protocol, optional>
:上述內容的選用版本
// A record which contains protocol-bound channels.
type Record = resource struct {
// client endpoint of a channel bound to the Calculator protocol
c client_end:Calculator;
// server endpoint of a channel bound to the Science protocol
s server_end:Science;
// optional client endpoint of a channel bound to the
// RealCalculator protocol
r client_end:<RealCalculator, optional>;
};
通訊協定組合
通訊協定可包含其他通訊協定的方法。 這稱為組合:您從其他通訊協定建立一個通訊協定。
組合的用途如下:
- 貴公司有多個通訊協定,這些共通行為都很常見
- 假設您希望針對不同的目標對象 公開不同層級的功能
常見行為
在第一個情況下,可能有跨多個通訊協定共用的行為。 例如,在圖形系統中,多個不同的通訊協定可能都共用 使用者通常必須設定背景和前景顏色。 您不會讓每種通訊協定都定義各自的色彩設定方法, 您可以指定的通訊協定
protocol SceneryController {
SetBackground(struct {
color Color;
});
SetForeground(struct {
color Color;
});
};
然後可供其他通訊協定共用:
protocol Drawer {
compose SceneryController;
Circle(struct {
x int32;
y int32;
radius int32;
});
Square(struct {
x int32;
y int32;
diagonal int32;
});
};
protocol Writer {
compose SceneryController;
Text(struct {
x int32;
y int32;
message string;
});
};
上述有三種通訊協定:SceneryController
、Drawer
和 Writer
。
Drawer
是用來繪製圖形物件,例如特定位置的圓形和正方形
指定的尺寸
它構成 SetBackground() 和 SetForeground() 方法,
SceneryController
通訊協定,因為其中包含 SceneryController
通訊協定
(透過 compose
關鍵字)。
用於在螢幕上寫入文字的 Writer
通訊協定包括 SceneryController
透過相同方式使用這個通訊協定
現在 Drawer
和 Writer
都包含 SetBackground() 和 SetForeground()。
相較於指定 Drawer
和 Writer
自行指定顏色,這有幾項好處
設定方法:
- 背景和前景色彩的設定方式都相同,無論是否設為使用 在螢幕上繪製圓形、正方形或輸入文字。
- 可在
Drawer
和Writer
中新增新方法,而無須變更 只要在SceneryController
通訊協定中加入這些定義即可
最後一點格外重要,因為這能讓我們新增
與既有通訊協定相比
比方說,我們可能會在
我們的圖形系統
擴充 SceneryController
通訊協定來進行處理,如下所示:
protocol SceneryController {
SetBackground(struct { color Color; });
SetForeground(struct { color Color; });
SetAlphaChannel(struct { a int; });
};
我們現在擴充了 Drawer
和 Writer
,以便支援 Alpha 混合。
多首樂曲
樂曲並非一對一關係,我們可以納入多項樂曲 指定為特定通訊協定,不必讓所有通訊協定都由 包括通訊協定在內
例如,我們或許可以設定字型特性。
字型不符合我們的 Drawer
通訊協定,但其對我們的 Writer
而言是合理的
通訊協定等等
因此,我們會定義 FontController
通訊協定:
protocol FontController {
SetPointSize(struct {
points int32;
});
SetFontName(struct {
fontname string;
});
Italic(struct {
onoff bool;
});
Bold(struct {
onoff bool;
});
Underscore(struct {
onoff bool;
});
Strikethrough(struct {
onoff bool;
});
};
然後使用 compose
關鍵字邀請 Writer
加入這個名單:
protocol Writer {
compose SceneryController;
compose FontController;
Text(struct { x int; y int; message string; });
};
這裡,我們已使用 FontController
通訊協定的方法擴充 Writer
通訊協定,
而不會幹擾 Drawer
通訊協定 (無須瞭解字型的任何知識)。
通訊協定組合類似 mixin。 詳情請參閱「RFC-0023:組合模型」一文。
分層
我們在本節一開始就曾提過第二個用於組合的用途,也就是 向不同目標對象提供不同等級的功能。
在這個範例中,我們有兩個獨立的通訊協定,也就是 Clock
通訊協定。
取得目前時間和時區:
protocol Clock {
Now() -> (struct {
time Time;
});
CurrentTimeZone() -> (struct {
timezone string;
});
};
以及設定時間和時區的 Horologist
通訊協定:
protocol Horologist {
SetTime(struct {
time Time;
});
SetCurrentTimeZone(struct {
timezone string;
});
};
我們不一定希望將更有特殊權限的 Horologist
通訊協定公開給
但得將它公開給系統時鐘元件。
因此,我們建立了一個通訊協定 (SystemClock
),組成兩者:
protocol SystemClock {
compose Clock;
compose Horologist;
};
不明互動
通訊協定可以定義它們在接收方法呼叫或事件時的回應方式 並無法識別其序數出現無法辨識的常態 用戶端和伺服器是使用不同的 且通訊協定可能包含較多或較少的方法,不過,如果 用戶端和伺服器在同一管道上誤用不同通訊協定。
為了控制發生這些未知互動時的通訊協定行為,
方法可標示為 strict
或 flexible
,且通訊協定可
標示為 closed
、ajar
或 open
。
方法嚴格度修飾符 strict
和 flexible
可指定
傳送端,希望接收端如果收到回應
無法辨識序數如果是單向或雙向方式,傳送端為
用戶端,而傳送端的事件則是伺服器。
strict
表示此錯誤應為接收端不知道的錯誤 互動。如果收到strict
不明互動,接收端 即可關閉管道flexible
表示不明互動應由 應用程式。如果通訊協定允許這類未知互動 序數會傳遞到未知的互動處理常式,然後 決定如何回應通訊協定有哪些不明互動類型 是否能由通訊協定修飾符決定預設值為flexible
值。
通訊協定開放性修飾符 closed
、ajar
和 open
可控制
如果接收端對 flexible
互動無法辨識
序數如為單向或雙向方式,接收端即為伺服器,
事件的接收端即為用戶端。
closed
表示通訊協定不接受任何不明的互動。如果有任何 收到不明互動時,繫結會回報錯誤並結束 無論互動是strict
還是flexible
。- 所有方法和事件都必須宣告為
strict
。
- 所有方法和事件都必須宣告為
ajar
表示通訊協定允許不明的flexible
單向方法, 事件。任何不明的雙向方法和strict
單向方法或事件 仍會導致錯誤,並導致繫結關閉管道。- 單向方法和事件可宣告為
strict
或flexible
。 - 雙向方法必須宣告為
strict
。
- 單向方法和事件可宣告為
open
表示通訊協定允許任何不明的flexible
互動。 任何不明的strict
互動仍會發生錯誤, 並繫結結束管道如果沒有開放性,則預設值為「開放」 。- 所有方法和事件都可宣告為
strict
或flexible
。
- 所有方法和事件都可宣告為
以下摘要說明不同種類可使用的嚴格度修飾符
各個通訊協定型別的方法範例開放程度的預設值 open
,會標記
以斜體顯示。嚴格度的預設值 flexible
會標示為
粗體。
嚴格 M(); | 靈活的 M(); | 嚴格 ->M(); | 彈性 ->M(); | 嚴格 M() ->(); | 彈性 M() ->(); | |
---|---|---|---|---|---|---|
開啟 P | 編譯 | 編譯 | 編譯 | 編譯 | 編譯 | 編譯 |
Ajar P | 編譯 | 編譯 | 編譯 | 編譯 | 編譯 | 無法編譯 |
封閉式 P | 編譯 | 無法編譯 | 編譯 | 無法編譯 | 編譯 | 無法編譯 |
通訊協定上修飾符的使用範例。
open protocol Moderator {
flexible GetPosts() -> (Posts);
strict ApplyModeration(struct {
post Post;
decision Decision;
}) -> ();
};
ajar protocol Messenger {
strict EnableSecureMode();
flexible AddMessageContent(struct {
content string;
});
strict SendPending() -> ();
flexible -> OnReceiveMessage(Message);
};
請注意,只有在接收到 結尾不認得序數,也不知道互動是什麼。 這表示接收端不知道互動是否 因為必須嚴格或有彈性為了讓接收者知道如何處理 表示寄件者在郵件標頭中加上一些片段 告知接收者要將互動視為嚴格或彈性。 因此,互動所使用的嚴格程度取決於 寄件者對其嘗試呼叫的方法有限制,但通訊協定對該類型開放 取決於接收方的開放程度。
以下說明各種嚴格程度的方法或事件,如何處理含有 在接收端不認得方法時啟用。
嚴格 M(); | 彈性 M(); | 嚴格 ->M(); | 彈性 ->M(); | 嚴格 M() ->(); | 彈性 M() ->(); | |
---|---|---|---|---|---|---|
開啟 P | 自動結束 | 可處理 | 自動結束 | 可處理 | 自動結束 | 可處理 |
Ajar P | 自動結束 | 可處理 | 自動結束 | 可處理 | 自動結束 | 自動結束 |
封閉式 P | 自動結束 | 自動結束 | 自動結束 | 自動結束 | 自動結束 | 自動結束 |
與組合互動
flexible
方法和事件不能在 closed
通訊協定中宣告,且
無法在 ajar
通訊協定中宣告 flexible
雙向方法。為了確保
這些規則是在各通訊協定組合中強制執行,通訊協定組合
編寫其他至少按照原樣封閉的通訊協定:
open
:可撰寫任何通訊協定。ajar
:可撰寫ajar
和closed
通訊協定。closed
:只能組合其他closed
通訊協定。
疊頻效應
支援類型別名。例如:
const MAX_SIZE uint32 = 100;
alias StoryID = string:MAX_SIZE;
alias Chapters = vector<StoryID>:5;
在上述範例中,ID StoryID
是
string
,大小上限為 MAX_SIZE
。ID Chapters
是
五個 StoryId
元素的向量宣告別名。
ID StoryID
和 Chapters
可在任何具有別名的位置使用
定義參數
建議考量的因素包括:
type Message = struct {
baseline StoryID;
chapters Chapters;
};
此處的 Message
結構體包含一個名為 baseline
的 MAX_SIZE
個位元組字串,
以及最多 5
個 MAX_SIZE
字串的向量,稱為 chapters
。
FIDL 方案:別名
alias
是 FIDL 宣告,用於將新名稱指派給現有類型。
這麼做有幾個好處:
- 使用
alias
可確保概念具有單一可靠資料來源 別名類型代表的是 - 可讓你為內容命名,尤其是受限的類型。
- 目前使用別名的不同用途可能連結為 同一個概念
請務必注意,系統不會在
目前繫結程式碼也就是指派給 alias
的名稱
,在產生的 FIDL 程式碼中,宣告一律不會顯示為宣告名稱。
在此範例中,為 Key
新增 alias
可避免使用重複字詞
自訂名稱,並向讀取器明確說明 key
值
Item
類型以及 ReadItem
要求結構中使用的 key
並非只是巧合
原因
原始的唯寫鍵/值儲存庫現已使用 使用者可以將商品讀回商店外
實作
套用至 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.addreaditem; // Aliases for the key and value. Using aliases helps increase the readability of FIDL files and // reduces likelihood of errors due to differing constraints. alias Key = string:128; alias Value = vector<byte>:64000; /// 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 Key; value Value; }; /// 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; }; /// An enumeration of things that may go wrong when trying to read a value out of our store. type ReadError = flexible enum { UNKNOWN = 0; NOT_FOUND = 1; }; /// A very basic key-value store - so basic, in fact, that one may only write to it, never read! @discoverable open protocol Store { /// Writes an item to the store. flexible WriteItem(struct { attempt Item; }) -> () error WriteError; /// Reads an item from the store. flexible ReadItem(struct { key Key; }) -> (Item) error ReadError; };
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.addreaditem.Store" }, ], config: { write_items: { type: "vector", max_count: 16, element: { type: "string", max_size: 64, }, }, read_items: { 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.addreaditem.Store" }, ], expose: [ { protocol: "examples.keyvaluestore.addreaditem.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.addreaditem.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}; use config::Config; use fidl_examples_keyvaluestore_addreaditem::{Item, StoreMarker}; use fuchsia_component::client::connect_to_protocol; use std::{str, 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}"))?; match store.write_item(&Item { key: key, value: value.into_bytes() }).await? { Ok(_) => println!("WriteItem Success"), Err(err) => println!("WriteItem Error: {}", err.into_primitive()), } } // The structured config for this client contains `read_items`, a vector of strings, each of // which is meant to be read from the key-value store. We iterate over these keys, attempting to // read them in turn. for key in config.read_items.into_iter() { let res = store.read_item(key.as_str()).await; match res.unwrap() { Ok(val) => { println!("ReadItem Success: key: {}, value: {}", key, str::from_utf8(&val.1)?) } Err(err) => println!("ReadItem 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. use { anyhow::{Context as _, Error}, fidl_examples_keyvaluestore_addreaditem::{ Item, ReadError, StoreRequest, StoreRequestStream, WriteError, }, fuchsia_component::server::ServiceFs, futures::prelude::*, lazy_static::lazy_static, regex::Regex, std::cell::RefCell, std::collections::hash_map::Entry, std::collections::HashMap, }; lazy_static! { static ref KEY_VALIDATION_REGEX: Regex = Regex::new(r"^[A-Za-z][A-Za-z0-9_\./]{2,62}[A-Za-z0-9]$") .expect("Key validation regex failed to compile"); } /// Handler for the `WriteItem` method. fn write_item(store: &mut HashMap<String, Vec<u8>>, attempt: Item) -> 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); } // Validate the value. if attempt.value.is_empty() { println!("Write error: INVALID_VALUE, For key: {}", attempt.key); return Err(WriteError::InvalidValue); } // 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) => { println!("Wrote value at key: {}", entry.key()); entry.insert(attempt.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, Vec<u8>>::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::ReadItem { key, responder } => { println!("ReadItem request received"); // Read the item from the store, returning the appropriate error if it could not be found. responder .send(match store.borrow().get(&key) { Some(found) => { println!("Read value at key: {}", key); Ok((&key, found)) } None => { println!("Read error: NOT_FOUND, For key: {}", key); Err(ReadError::NotFound) } }) .context("error sending reply")?; println!("ReadItem 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.
內建
FIDL 提供下列內建功能:
- 原始類型:
bool
、int8
、int16
、int32
、int64
、uint8
、uint16
、uint32
、uint64
、float32
、float64
。 - 其他類型:
string
、client_end
、server_end
。 - 類型範本:
array
、vector
、box
。 - 別名:
byte
。 - 限制:
optional
、MAX
。
fidl
程式庫下方的所有內建項目。您隨時都能使用這個程式庫
不需要使用 using
匯入。舉例來說,如果您宣告結構體
名為 string
,您可以將原始字串類型參照為 fidl.string
。
程式庫 zx
系統不會內建程式庫 zx
,但編譯器會以特殊方式處理。是
//zircon/vdso/zx 中定義的值。程式庫會以多種方式
using zx
,最常使用 zx.Handle
類型。
內嵌版面配置
您也可以內嵌指定版面配置,而非在 type
簡介中
宣告內容特定版面配置只能使用一次時,這種做法相當實用。適用對象
例如,下列 FIDL:
type Options = table {
1: reticulate_splines bool;
};
protocol Launcher {
GenerateTerrain(struct {
options Options;
});
};
可以使用內嵌版面配置重新編寫:
protocol Launcher {
GenerateTerrain(struct {
options table {
1: reticulate_splines bool;
};
});
};
使用內嵌版面配置時,FIDL 會為該版面配置保留 保證不會重複,根據的命名情境 版面配置方式這會產生下列保留名稱:
- 若是用於做為外部版面配置成員類型的內嵌版面配置,保留的
只是對應成員的名稱。
- 在上述範例中,
Options
為內嵌名稱預留的名稱table
。
- 在上述範例中,
- 以頂層要求/回應類型來說,FIDL 會串連通訊協定名稱、
方法名稱,然後是
"Request"
或"Response"
,取決於 類型。- 在上述範例中,
LauncherGenerateTerrainRequest
的名稱是 為做為GenerateTerrain
要求的結構保留 方法。 - 請注意,
"Request"
後置字串表示類型用於啟動 通訊;因此,事件類型的"Request"
,而非"Response"
後置字串。
- 在上述範例中,
產生的程式碼中實際使用的名稱取決於繫結。 會在個別繫結參考資料中描述。
如果內嵌版面配置用做版面配置成員的類型,有兩種方法可 取得不同的保留名稱:
- 重新命名版面配置成員。
- 使用
@generated_name
覆寫保留名稱 屬性。