This article follows the second part of Deno Compilation, DenO Structure Parsing, since deno aims to provide a secure environment like a browser, but how do you do it if you need to implement something on the back end that deno can’t easily implement? So why can’t we make an extension for deno? Let’s do a Fibonacci sequence calculation method to do an example of deno doing rust extension.
Step 1: Define the message type
Deno uses an intermediate layer to make v8 and rust call each other. V8 is written in c++ and rust is another language. Deno uses a very regular RPC-like call, minus the r. Those of you who have used Thrift and GRPC know that if you want to implement multilingual communication, you actually need to define types for each other. Deno is no exception, but it uses flatbuffers.
So the first step is to define the type:
- SRC /msg. FBS add GetFibo and GetFiboRes to SRC /msg. FBS
union Any {
Start,
...
GetFibo,
GetFiboRes
}
table GetFibo {
num: int32;
}
table GetFiboRes {
result: int32;
}
Copy the code
What does that mean? You can think of GetFibo as the type that defines the parameter list I passed in, and GetFiboRes as the type that defines the return value. And if we want to compute the Fibonacci sequence, we only have one number as an argument, and we only have one number as a result, so we just have to define a number type.
Once we’ve written it, we can compile it
./tools/build.py
Generate target/debug/gen/msg_generated
Copy the code
Step 2: Establish a method for communicating with RUST and a method definition for TS
- Create a new file js/get_fibo.ts with the following code
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 = newmsg.GetFiboRes(); assert(baseRes! .inner(res) ! = =null);
return res.result();
}
export function getFiboSync(num: number) :number {
returnres(dispatch.sendSync(... req(num))); }export async function getFibo(num: number) :Promise<number> {
return res(awaitdispatch.sendAsync(... req(num))); }Copy the code
An explanation:
- Gen/MSg_generated is the data type definition we generated earlier
- Flatbuffers is a tool used to generate protocol data
- Assert A tool that detects whether data is abnormal
- Dispatch A method of sending data communications
In addition, if we only need to write JS and do not need to communicate rust, there is no need to refer to these libraries and write methods directly in getFiboSync and getFibo. This file ts is mainly used to interact with rust and define ts methods to expose. Req methods are used to group data structures to be sent, RES is used to process received messages, and DISPATCH is used to send data.
Note: getFiboSync and getFibo represent synchronous and asynchronous methods respectively
Add RUST method
Add methods in SRC /ops.rs, where methods are also mainly receiving and data assembly, with the following code:
.let op_creator: OpCreator = match inner_type {
msg::Any::Accept => op_accept,
msg::Any::Chdir => op_chdir,
...
msg::Any::GetFibo => op_get_fibo // Add our methods_ = >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 {
// Calculate the Fibonacci sequence
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()},)})}...Copy the code
The op_get_fibo method is used to encapsulate the FlatBufferBuilder data. The Fibonacci sequence can be effectively calculated using only a little code. Of course, if the number of functional code is large, you can create a new rust file to do it, as follows:
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;
Copy the code
The last step
In fact, at this point the link is completely broken, we just need one final step, to expose our method
- Get_fibo. ts = get_fibo.ts = get_fibo.ts = get_fibo.ts
.export { getFiboSync, getFibo } from "./get_fibo"; .Copy the code
Once you compile it, you’re done
./tools/build.py
Copy the code
The test code is as follows:
import * as deno from "deno";
(async() = > {console.log(deno.getFiboSync(10));
console.log(await deno.getFibo(11));
})(a);
Copy the code
In fact, IN the last article I also mentioned that learning deno is learning a library, I believe that the test code will know why.
conclusion
This should really be the last one before the New Year.