FIDL 語言規格

本文件是 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。當「嘗試解決」時 遷移失敗,請繼續下一個步驟。「解決」狀態該步驟失敗, 編譯器會產生錯誤。

  • 如果代碼不符合資格:
    1. 請嘗試在目前程式庫中將宣告視為宣告。
    2. 請嘗試以內建宣告的形式重新修改,例如:bool 是指 fidl.bool
    3. 解析為結構定義位元/列舉成員,例如CHANNELzx.handle:CHANNEL 是指 zx.ObjType.Channel
  • 如果不符合 X.Y 的資格:
    1. 請嘗試在目前程式庫中將 X 解析為宣告:
      1. X 的成員身分解決 Y
    2. X 解析為程式庫名稱或別名。
      1. Y 視為 X 中的宣告。
  • 如果資格為 x.Y.Z,其中 x 代表一或多個元件:
    1. 請嘗試將 x.Y 解析為程式庫名稱或別名:
      1. Z 視為 x.Y 中的宣告。
    2. x 解析為程式庫名稱或別名:
      1. Y 視為 x 中的宣告方式:
        1. 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 支援下列種類的文字:

  • 布林值:truefalse
  • 整數:十進位 (123)、十六進位 (0xA1B2)、八進位 (0755)、二進位 (0b101)。
    • 只有小數常值可以為負數。
  • 浮點值:1.23-0.011e52.0e-3
    • 系統只允許使用 ee-,但不允許 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)。

libraryusing 宣告的範圍僅限於單一檔案。 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;
};

點數

  • 已命名位元類型。
  • 從基礎整數類型選擇的個別位元值子集。
  • 不得為選用項目。
  • 位元可以是 strictflexible
  • 位元預設為 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;

列舉

  • 正確的列舉類型。
  • 從基礎整數類型選擇的具名值子集。
  • 不得為選用項目。
  • 列舉可以是 strictflexible
  • 列舉預設值為 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>:Nvector<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 控制代碼 (具備相關權限 R
  • zx.Handle:<H, R, optional>:選用的 H 類型的 Zircon 控制代碼, 權利 R

H 可以是任何支援的物件 Zircon,例如:channelthreadvmo。詳情請參閱 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 修飾符
  • 參考檔案可以選擇性使用。
  • 聯集可為 strictflexible
  • 聯集預設為 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 類型都是「值類型」或「資源類型」。資源 類型包括:

  • 帳號代碼
  • 通訊協定端點
  • 資源類型的 aliases
  • 資源類型的陣列和向量
  • 使用 resource 修飾符標示的結構、資料表和聯集
  • 選用 (或裝箱) 參照任何上述類型的資料。

所有其他類型都是值類型。

值類型不得包含資源類型。舉例來說,這不正確:

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 的值。然後再定義來自伺服器的垃圾郵件。
    • 雙向方法可能會宣告伺服器可傳送的錯誤類型 而不是回應。這個類型必須是 int32uint32 或 有 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>;
};

通訊協定組合

通訊協定可包含其他通訊協定的方法。 這稱為組合:您從其他通訊協定建立一個通訊協定。

組合的用途如下:

  1. 貴公司有多個通訊協定,這些共通行為都很常見
  2. 假設您希望針對不同的目標對象 公開不同層級的功能

常見行為

在第一個情況下,可能有跨多個通訊協定共用的行為。 例如,在圖形系統中,多個不同的通訊協定可能都共用 使用者通常必須設定背景和前景顏色。 您不會讓每種通訊協定都定義各自的色彩設定方法, 您可以指定的通訊協定

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

上述有三種通訊協定:SceneryControllerDrawerWriterDrawer 是用來繪製圖形物件,例如特定位置的圓形和正方形 指定的尺寸 它構成 SetBackground()SetForeground() 方法, SceneryController 通訊協定,因為其中包含 SceneryController 通訊協定 (透過 compose 關鍵字)。

用於在螢幕上寫入文字的 Writer 通訊協定包括 SceneryController 透過相同方式使用這個通訊協定

現在 DrawerWriter 都包含 SetBackground()SetForeground()

相較於指定 DrawerWriter 自行指定顏色,這有幾項好處 設定方法:

  • 背景和前景色彩的設定方式都相同,無論是否設為使用 在螢幕上繪製圓形、正方形或輸入文字。
  • 可在 DrawerWriter 中新增新方法,而無須變更 只要在 SceneryController 通訊協定中加入這些定義即可

最後一點格外重要,因為這能讓我們新增 與既有通訊協定相比 比方說,我們可能會在 我們的圖形系統 擴充 SceneryController 通訊協定來進行處理,如下所示:

protocol SceneryController {
    SetBackground(struct { color Color; });
    SetForeground(struct { color Color; });
    SetAlphaChannel(struct { a int; });
};

我們現在擴充了 DrawerWriter,以便支援 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;
};

不明互動

通訊協定可以定義它們在接收方法呼叫或事件時的回應方式 並無法識別其序數出現無法辨識的常態 用戶端和伺服器是使用不同的 且通訊協定可能包含較多或較少的方法,不過,如果 用戶端和伺服器在同一管道上誤用不同通訊協定。

為了控制發生這些未知互動時的通訊協定行為, 方法可標示為 strictflexible,且通訊協定可 標示為 closedajaropen

方法嚴格度修飾符 strictflexible 可指定 傳送端,希望接收端如果收到回應 無法辨識序數如果是單向或雙向方式,傳送端為 用戶端,而傳送端的事件則是伺服器。

  • strict 表示此錯誤應為接收端不知道的錯誤 互動。如果收到 strict 不明互動,接收端 即可關閉管道
  • flexible 表示不明互動應由 應用程式。如果通訊協定允許這類未知互動 序數會傳遞到未知的互動處理常式,然後 決定如何回應通訊協定有哪些不明互動類型 是否能由通訊協定修飾符決定預設值為 flexible 值。

通訊協定開放性修飾符 closedajaropen 可控制 如果接收端對 flexible 互動無法辨識 序數如為單向或雙向方式,接收端即為伺服器, 事件的接收端即為用戶端。

  • closed 表示通訊協定不接受任何不明的互動。如果有任何 收到不明互動時,繫結會回報錯誤並結束 無論互動是 strict 還是 flexible
    • 所有方法和事件都必須宣告為 strict
  • ajar 表示通訊協定允許不明的 flexible 單向方法, 事件。任何不明的雙向方法和 strict 單向方法或事件 仍會導致錯誤,並導致繫結關閉管道。
    • 單向方法和事件可宣告為 strictflexible
    • 雙向方法必須宣告為 strict
  • open 表示通訊協定允許任何不明的 flexible 互動。 任何不明的 strict 互動仍會發生錯誤, 並繫結結束管道如果沒有開放性,則預設值為「開放」 。
    • 所有方法和事件都可宣告為 strictflexible

以下摘要說明不同種類可使用的嚴格度修飾符 各個通訊協定型別的方法範例開放程度的預設值 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:可撰寫 ajarclosed 通訊協定。
  • closed:只能組合其他 closed 通訊協定。

疊頻效應

支援類型別名。例如:

const MAX_SIZE uint32 = 100;
alias StoryID = string:MAX_SIZE;
alias Chapters = vector<StoryID>:5;

在上述範例中,ID StoryIDstring,大小上限為 MAX_SIZE。ID Chapters 是 五個 StoryId 元素的向量宣告別名。

ID StoryIDChapters 可在任何具有別名的位置使用 定義參數 建議考量的因素包括:

type Message = struct {
    baseline StoryID;
    chapters Chapters;
};

此處的 Message 結構體包含一個名為 baselineMAX_SIZE 個位元組字串, 以及最多 5MAX_SIZE 字串的向量,稱為 chapters

FIDL 方案:別名

alias 是 FIDL 宣告,用於將新名稱指派給現有類型。 這麼做有幾個好處:

  • 使用 alias 可確保概念具有單一可靠資料來源 別名類型代表的是
  • 可讓你為內容命名,尤其是受限的類型。
  • 目前使用別名的不同用途可能連結為 同一個概念

請務必注意,系統不會在 目前繫結程式碼也就是指派給 alias 的名稱 ,在產生的 FIDL 程式碼中,宣告一律不會顯示為宣告名稱。

在此範例中,為 Key 新增 alias 可避免使用重複字詞 自訂名稱,並向讀取器明確說明 keyItem 類型以及 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 提供下列內建功能:

  • 原始類型:boolint8int16int32int64uint8uint16uint32uint64float32float64
  • 其他類型:stringclient_endserver_end
  • 類型範本:arrayvectorbox
  • 別名:byte
  • 限制:optionalMAX

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 覆寫保留名稱 屬性。