本文档是 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;
};
标识符
库名称
FIDL 库名称标签为 FIDL 库。它们包含一个
多个组成部分(用英文句点 (.
) 分隔)。每个组成部分都必须与正则表达式匹配
[a-z][a-z0-9]*
。也就是说:库名称组成部分必须以小写字母开头
可以包含小写字母和数字。
// A library named "foo".
library foo;
标识符
FIDL 标识符标签声明及其成员。它们必须与
正则表达式 [a-zA-Z]([a-zA-Z0-9_]*[a-zA-Z0-9])?
。换句话说:标识符必须以
,可以包含字母、数字和下划线,但不能以字母结尾
以下划线分隔。
// A struct named "Foo".
type Foo = struct {};
FIDL 标识符区分大小写。但是,每个标识符的标识符
规范形式,否则 FIDL 编译器将失败并显示 fi-0035:
规范名称冲突。通过
规范形式的标识符是通过将标识符转换为 snake_case
来获取的。
符合条件的标识符
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 使用以下算法来解析标识符。当“尝试解决”时 步骤失败,则会继续执行下面的下一步。收到“解决”失败后, 会生成错误。
- 如果不符合条件:
<ph type="x-smartling-placeholder">
- </ph>
- 尝试在当前库中解析为声明。
- 请尝试将 resoving 作为内置声明,例如
bool
是指fidl.bool
。 - 作为上下文位/枚举成员解析,例如
CHANNEL
的zx.handle:CHANNEL
是指zx.ObjType.Channel
。
- 如果它符合
X.Y
条件: <ph type="x-smartling-placeholder">- </ph>
- 尝试将
X
解析为当前库中的声明: <ph type="x-smartling-placeholder">- </ph>
- 以
X
的成员的身份解析Y
。
- 以
- 将
X
解析为库名称或别名。- 将
Y
解析为X
中的声明。
- 将
- 尝试将
- 如果它符合
x.Y.Z
条件,其中x
表示一个或多个组件: <ph type="x-smartling-placeholder">- </ph>
- 尝试将
x.Y
解析为库名称或别名: <ph type="x-smartling-placeholder">- </ph>
- 将
Z
解析为x.Y
中的声明。
- 将
- 将
x
解析为库名称或别名: <ph type="x-smartling-placeholder">- </ph>
- 将
Y
解析为x
中的声明: <ph type="x-smartling-placeholder">- </ph>
- 以
x.Y
的成员的身份解析Z
。
- 以
- 将
- 尝试将
完全限定名称
FIDL 使用完全限定名称(缩写为“FQN”)明确指代
和成员的方法。库名称、斜杠 /
、
声明标识符,以及可选的 .
点和成员标识符。例如:
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;
};
使用
枚举类型由其标识符表示,可以根据需要加以限定。
// A record which contains two enum fields.
type Order = struct {
beverage Beverage;
vessel Vessel;
};
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 编码的文本。
- 可以选填;缺失字符串和空字符串不同。
- 可以指定大小上限,例如
string:40
(不超过 40 字节) 字符串。默认情况下,string
表示string:MAX
,即无界限。 - 字符串字面量支持转义序列
\\
、\"
、\n
、\r
、\t
和\u{X}
,其中X
是 Unicode 代码点的 1 到 6 个十六进制数字。 - 与传统的 C 字符串不同,可能包含嵌入式
NUL
字节。
使用
字符串的表示方式如下:
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_
并非破坏性更改。
字符串不应用于传递任意二进制数据,因为绑定会强制执行 有效的 UTF-8。如果数据量较少,请考虑使用
bytes
;fuchsia.mem.Buffer
。请参阅 我应该使用字符串还是矢量? 了解详情。
矢量
- 可变长度的同构元素序列。
- 可以选填;不存在向量和空向量是不同的。
- 可以指定大小上限,例如
vector<T>:40
(最多 40 个元素) 矢量。默认情况下,vector<T>
表示vector<T>:MAX
,即无界限。 - 布尔值矢量没有特殊情况。每个布尔值元素 字节。
使用
向量表示如下:
vector<T>
:必需的元素类型向量 T(如果缺失,则会出现验证错误)vector<T>:optional
:元素类型的可选矢量 Tvector<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
:必需的 Zircon 句柄(类型为 H)zx.Handle:<H, optional>
:H 类型的可选 Zircon 句柄zx.Handle:<H, R>
:必需的 Zircon 句柄(类型为 H),且具有权限 右zx.Handle:<H, R, optional>
:H 类型的可选 Zircon 句柄, 权利 R
H 可以是以下对象支持的任何对象:
Zircon,例如channel
,thread
,vmo
。请参阅
grammar 可查看完整列表。
R 可以是 Zircon 支持的任何右侧。
权限是位类型的值,如 zx
中所定义
FIDL 库,例如zx.Rights.READ
。在传入和传出
标识名经过验证,具有正确的 Zircon 对象类型,并且
版权数量不得少于 FIDL 中规定的数量。如果标识名拥有更多权限
则 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>;
};
结构体
- 由一系列类型化字段组成的记录类型。
- 通常情况下,添加或移除字段或更改字段类型 不兼容 ABI。
- 声明可以使用
resource
修饰符。 - 可以对参考文件进行
box
处理。 - 结构体包含零个或多个成员。
声明
type CirclePoint = struct {
x float32;
y float32;
};
type Color = struct {
r float32;
g float32;
b float32;
};
使用
结构体由其声明的名称表示(例如 Circle):
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
如何进化以同时携带温度单位。
客户知道之前对 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
仍然可以从
已更新为包含更多变体的服务器。如果
并集是未知变体的,绑定可能会将其作为未知数据(即
原始字节和句柄),并允许对未知并集(例如,
以支持类似于代理的用例)。提供的用于
绑定
参考。
有关更多详情,请参见 RFC-0033:处理未知字段和严格程度。
价值与资源
每个 FIDL 类型要么是值类型,要么是资源类型。资源 类型包括:
其他所有类型均为值类型。
值类型不得包含资源类型。例如,以下就是不正确的:
type Foo = struct { // ERROR: must be "resource struct Foo"
h zx.Handle;
};
即使类型不包含,也可以使用 resource
修饰符进行标记。
标识名。如果您打算为
因为添加或移除 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:默认无句柄。
协议
- 描述可通过某个通道发送消息来调用的方法。
- 方法由其序数进行标识。编译器的计算方法如下:
<ph type="x-smartling-placeholder">
- </ph>
- 获取该方法的完全限定名称的 SHA-256 哈希值。
- 提取哈希摘要的前 8 个字节。
- 将这些字节解释为小端序整数,
- 将该值的高位(即最后位)设置为 0。
- 如需替换序数,方法可以具有
@selector
属性。如果 属性的 参数是有效的 FQN,系统将使用该参数替代 FQN 。否则,它必须是有效的标识符,并将直接使用。 方法名称的一部分。
每个方法声明都会声明其参数和结果。
- 如果没有声明任何结果,则该方法是单向的:没有响应 由服务器生成
- 如果声明结果(即使为空),则该方法是双向的: 每次调用该方法都会从服务器生成响应。
- 如果只声明结果,则该方法称为 event。然后定义来自服务器的垃圾消息。
- 双向方法可以声明服务器可发送的错误类型
而不是响应。此类型必须是
int32
、uint32
或 其中enum
个。
当使用协议的服务器要关闭自己一端的通道时, 可能会选择向客户端发送 epitaph 消息,以指明 连接的处理方式墓碑必须是最后一条消息 通过渠道投放的内容墓记消息包含一个 32 位整数 zx_status_t 类型的值。为系统保留负值 错误代码。值
ZX_OK
(0) 表示操作成功。 应用定义的错误代码(之前定义为所有正例zx_status_t
值)已被弃用。有关墓记的更多详情,请参阅 拒绝 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
用于绘制图形对象,例如在指定位置绘制圆形和方形
并具有给定的尺寸
它从 Cloud Storage 中构建 SetBackground() 和 SetForeground() 方法,
SceneryController
协议,因为它包含 SceneryController
协议
(通过 compose
关键字)。
用于在显示屏上写入文本的 Writer
协议包括 SceneryController
采用相同的方式进行连接。
现在,Drawer
和 Writer
都包含 SetBackground() 和 SetForeground()。
与让 Drawer
和 Writer
指定自己的颜色相比,这具有多项优势
设置方法:
- 无论是使用背景色还是前景色, 绘制圆形或方形,或者在显示屏上显示文字。
- 可以将新方法添加到
Drawer
和Writer
中,而无需更改其 只需将它们添加到SceneryController
协议中即可。
最后一点特别重要
现有协议
例如,我们可能会引入 alpha 混合(或“透明度”)功能,
图形系统。
通过扩展 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
表示未知互动应由 应用。如果协议允许进行此类未知互动, 该序数会传递给一个未知的交互处理程序,然后 决定如何应对。协议的未知互动类型 allow 则由协议修饰符决定。flexible
是默认值 值。
协议开放性修饰符 closed
、ajar
和 open
用于控制
如果接收端无法识别 flexible
互动,
序数。对于单向或双向方法,接收端是服务器,
对于事件,接收端是客户端。
closed
表示协议不接受任何未知交互。如果有 收到未知交互时,绑定报告错误并结束 通信,无论互动是strict
还是flexible
。- 所有方法和事件都必须声明为
strict
。
- 所有方法和事件都必须声明为
ajar
表示协议允许未知的flexible
单向方法和 事件。任何未知的双向方法和strict
单向方法或事件 仍然会引发错误,并导致绑定关闭通道。- 单向方法和事件可以声明为
strict
或flexible
。 - 双向方法必须声明为
strict
。
- 单向方法和事件可以声明为
open
表示协议允许任何未知的flexible
互动。 任何未知的strict
互动仍然会导致错误并导致 以及用于关闭通道的绑定如果没有开放性,则“开放”为默认值 。- 所有方法和事件都可以声明为
strict
或flexible
。
- 所有方法和事件都可以声明为
下面概要列出了针对不同类型可以使用哪些严格程度修饰符
每种协议类型中的方法。开放性的默认值 open
,标记为
以斜体表示。严格程度的默认值 flexible
标记在
粗体。
严格 M(); | flexible M(); | 严格 ->M(); | 灵活 ->M(); | strict M() ->(); | flexible M() ->(); | |
---|---|---|---|---|---|---|
打开 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(); | strict M() ->(); | 灵活 M() ->(); | |
---|---|---|---|---|---|---|
打开 P | 自动关闭 | 可处理 | 自动关闭 | 可处理 | 自动关闭 | 可处理 |
阿贾尔北 | 自动关闭 | 可处理 | 自动关闭 | 可处理 | 自动关闭 | 自动关闭 |
已关闭 P | 自动关闭 | 自动关闭 | 自动关闭 | 自动关闭 | 自动关闭 | 自动关闭 |
与组合互动
无法在 closed
协议中声明 flexible
方法和事件,并且
无法在 ajar
协议中声明 flexible
双向方法。为确保
这些规则会在所有协议组合中强制执行,
编写其他至少具有其关闭性的协议:
open
:可编写任何协议。ajar
:可以编写ajar
和closed
协议。closed
:只能编写其他closed
协议。
走样
支持类型别名。例如:
const MAX_SIZE uint32 = 100;
alias StoryID = string:MAX_SIZE;
alias Chapters = vector<StoryID>:5;
在上面的代码中,标识符 StoryID
是声明
string
,大小上限为 MAX_SIZE
。标识符 Chapters
是一个
五个 StoryId
元素的矢量声明的别名。
标识符 StoryID
和 Chapters
可在任何别名地址使用
定义。
考虑:
type Message = struct {
baseline StoryID;
chapters Chapters;
};
此处,Message
结构体包含一个名为 baseline
的 MAX_SIZE
字节字符串。
以及一个名为 chapters
的向量(最多 5
个 MAX_SIZE
字符串)。
FIDL 配方:Alias
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 会为其预留一个名称,即 根据命名上下文, 布局。这会产生以下预留名称:
- 对于用作外部布局成员类型的内嵌布局,预留的
name 只是相应成员的名称。
- 在上面的示例中,名称
Options
已预留用于内嵌table
。
- 在上面的示例中,名称
- 对于顶级请求/响应类型,FIDL 会将协议名称、
方法名称,然后是
"Request"
或"Response"
,具体取决于 使用 类型的位置。- 在上面的示例中,名称
LauncherGenerateTerrainRequest
为 为用作GenerateTerrain
的请求的结构体预留 方法。 - 请注意,
"Request"
后缀表示类型用于发起 通信;因此,事件类型的"Request"
会预留后缀,而不是"Response"
后缀。
- 在上面的示例中,名称
生成的代码中实际使用的名称取决于绑定, 具体说明请参阅各个绑定参考。
对于用作布局成员类型的内嵌布局,您可以通过两种方式 获取不同的预留名称:
- 重命名布局成员。
- 使用
@generated_name
替换预留名称 属性。