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就是学习一个库,相信看过测试代码就知道缘由了。

结语

此次应该真的是过年前的最后一篇。

相关文章
相关标签/搜索