deno系列第二篇,給deno作rust擴展

這篇文章主要接着 《編譯deno,deno結構解析》做的第二篇,因爲deno目標是給提供像瀏覽器同樣的安全的環境,可是若是你須要在後端實現一些deno不方便實現的東西,你要如何作呢?那爲何咱們不能給deno作一個擴展呢?咱們就以作一個計算斐波那契數列的方法作一個deno作rust擴展的例子。c++

第一步:定義消息類型

上篇文章目錄解析說到,deno是經過中間層使得v8和rust互相調用,那麼v8是c++寫的,rust又是另外一門語言,那須要通信要怎麼怎麼作呢?deno使用很常規的相似RPC來調用,只不過去掉了r。使用過thrift和grpc的同窗都知道若是要實現多語言通信其實是要互相定義類型,deno也不例外,只不過使用的是flatbuffers,這裏有興趣自行學習。git

因此咱們第一步定義類型:github

  • 在src/msg.fbs中增長GetFibo和GetFiboRes兩種類型,類型名字能夠隨便取,代碼以下
union Any {
  Start,
  ...
  GetFibo,
  GetFiboRes
}

table GetFibo {
  num: int32;
}

table GetFiboRes {
  result: int32;
}
複製代碼

什麼意思呢?你能夠這樣認爲GetFibo就是定義了我傳入的參數列表類型,GetFiboRes則是定義了返回值的類型。而咱們要作計算斐波那契數列的方法,那麼參數只有一個數字,結果也只有一個數字,因此將咱們都只要定義一個數字類型就好。後端

寫好後,咱們能夠編譯一下瀏覽器

./tools/build.py 
# 生成target/debug/gen/msg_generated.ts,這個咱們後面要用到
複製代碼

第二步:創建與rust進行通信的方法和ts的方法定義

  • 新建一個文件js/get_fibo.ts,代碼以下
import * as msg from "gen/msg_generated";
import * as flatbuffers from "./flatbuffers";
import { assert } from "./util";
import * as dispatch from "./dispatch";

function req( num: number, ): [flatbuffers.Builder, msg.Any, flatbuffers.Offset] {
    const builder = flatbuffers.createBuilder();
    msg.GetFibo.startGetFibo(builder);
    msg.GetFibo.addNum(builder, num);
    const inner = msg.GetFibo.endGetFibo(builder);
    return [builder, msg.Any.GetFibo, inner];
  }
  
  function res(baseRes: null | msg.Base): number {
    assert(baseRes !== null);
    assert(msg.Any.GetFiboRes === baseRes!.innerType());
    const res = new msg.GetFiboRes();
    assert(baseRes!.inner(res) !== null);
    return res.result();
  }


export function getFiboSync(num: number): number {
    return res(dispatch.sendSync(...req(num)));
}
  

export async function getFibo(num: number): Promise<number> {
    return res(await dispatch.sendAsync(...req(num)));
}
複製代碼

做下說明:安全

  • gen/msg_generated 就是咱們以前生成的數據類型定義
  • flatbuffers 用來產生協議數據的工具
  • assert 檢測數據是否異常的工具
  • dispatch 發送數據通信的方法

此外若是咱們只須要寫js而不須要通信rust的話,其實就也不須要引用這些庫了,直接在getFiboSync和getFibo寫方法就行了。這個文件ts主要用途就是和rust交互用的,同時定義下要暴露的ts方法,req方法是組轉要發送的數據結構,res則是處理接收回來的消息,dispatch發送數據。數據結構

:getFiboSync和getFibo 分別表明同步方法和異步方法異步

增長rust方法

在src/ops.rs增長方法,這裏的方法也主要是接收和數據組裝,代碼以下:async

...
let op_creator: OpCreator = match inner_type {
      msg::Any::Accept => op_accept,
      msg::Any::Chdir => op_chdir,
      ...
      msg::Any::GetFibo => op_get_fibo //增長咱們的方法
      _ => panic!(format!(
        "Unhandled message {}",
        msg::enum_name_any(inner_type)
      )),
...
fn op_get_fibo(
  _state: &Arc<IsolateState>,
  base: &msg::Base<'_>,
  data: libdeno::deno_buf,
) -> Box<Op> {
  assert_eq!(data.len(), 0);
  let inner = base.inner_as_get_fibo().unwrap();
  let cmd_id = base.cmd_id();
  let num = inner.num();

  blocking(base.sync(), move || -> OpResult {
    // 計算fibonacci數列
    let sqrt5 = 5_f64.sqrt();
    let c1 = (1.0_f64+sqrt5)/2.0_f64;
    let c2 = (1.0_f64-sqrt5)/2.0_f64;
    let result_f = (sqrt5/5.0_f64)*(c1.powi(num)-c2.powi(num));
    let result = result_f as i32;

    let builder = &mut FlatBufferBuilder::new();
    let inner = msg::GetFiboRes::create(
      builder,
      &msg::GetFiboResArgs {
        result, 
      },
    );

    Ok(serialize_response(
      cmd_id,
      builder,
      msg::BaseArgs {
        inner: Some(inner.as_union_value()),
        inner_type: msg::Any::GetFiboRes,
        ..Default::default()
      },
    ))
  })
}
...
複製代碼

這裏稍微解釋一下rust的match在這裏的意思,你能夠理解爲一個加強版的switch,就是GetFibo的數據類型過來的話,就執行op_get_fibo方法,而op_get_fibo主要是在封裝FlatBufferBuilder數據,而真正有效計算斐波那契數列的代碼其實就一點,固然若是功能代碼量大則能夠新建一個rust文件來搞,以下:工具

let sqrt5 = 5_f64.sqrt();
    let c1 = (1.0_f64+sqrt5)/2.0_f64;
    let c2 = (1.0_f64-sqrt5)/2.0_f64;
    let result_f = (sqrt5/5.0_f64)*(c1.powi(num)-c2.powi(num));
    let result = result_f as i32;
複製代碼

最後一步

其實到這裏鏈路就算完全打通了,咱們只差最後一步,把咱們的方法暴露出來

  • 修改js/deno.ts文件,把get_fibo.ts的方法暴露出去便可
...
export { getFiboSync, getFibo } from "./get_fibo";
...
複製代碼

編譯以後就搞定了

./tools/build.py 
複製代碼

測試代碼以下:

import * as deno from "deno";

(async()=>{ console.log(deno.getFiboSync(10)); console.log(await deno.getFibo(11)); })(); 複製代碼

其實在上一篇我也有講到,學習deno就是學習一個庫,相信看過測試代碼就知道緣由了。

結語

此次應該真的是過年前的最後一篇。

相關文章
相關標籤/搜索