【20211202_toranoana.deno#3】denoでFFI

虎の穴 開発室
虎の穴 開発室虎の穴 開発室
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
denoでFFI



虎の穴ラボ 藤原 佳顕

1
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
アジェンダ

● 概要
● やりたかったこと
● ソースを見てみよう
● 作ってみよう
2
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
自己紹介

3
● 名前

○ 藤原佳顕(@nuhera, @zonuko)

● 仕事

○ FantiaとかCreatiaとか社内アプリとか

● 好み

○ Clojure、Rust

● 趣味

○ 格闘ゲーム

■ Melty Blood、Guilty Gear

○ STG(ダライアスとか)も好き

○ 最近は女神転生Vずっとやってました

Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
概要

4
● deno1.13あたりからFFI機能が追加されています


○ what’s ffi?

■ 別の言語で作られた関すなどをまた別の言語から呼び出せる機能


■ deno固有の言葉ではない

○ これによってDLLやSOファイルなど、いわゆるダイナミック/スタティックライブラリを
Denoから呼び出せるように

Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
概要

5
● 代表的なライブラリ

○ と言われて何が思い浮かびますか?


○ libmysqlclient.so

○ RTP

■ RPGツクールのランタイムライブラリ群


○ Win32 DLL (kernel32.dll、user32.dll、gdi32.dllなど)


Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
やりたかったこと

● DenoにFFIが入ったということはlibmysqlclient経由でmysql接続できるのでは?

● mysqlの接続方法自体はいくつかある

○ TCP接続

○ Unix Domain Socket接続

○ 名前付きパイプ

■ プロセス間通信で一般的に使われるやつ





6
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
やりたかったこと

● とはいえDenoにはmysql用のライブラリがすでにある

○ https://deno.land/x/mysql@v2.10.1





7
private async _connect() {
// TODO: implement connect timeout
const { hostname, port = 3306, socketPath, username = "", password } = this.config;
log.info(`connecting ${this.remoteAddr}`);
this.conn = !socketPath
? await Deno.connect({
transport: "tcp",
hostname,
port,
})
: await Deno.connect({
transport: "unix",
path: socketPath,
} as any);
…
mysqlがインストールされているかに関わらず、tcp/ドメイ
ンソケット経由なのがわかる



Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
やりたかったこと



● ではDenoでのFFIするときにインターフェースはどうなっているか

○ ソースはこんな感じ











● 対応している型はchar / signed char, unsigned char, short int, unsigned short int, int / signed int, unsigned int, long long int,
unsigned long long int, size_t, float, double, void, const uint8_t *

○ https://deno.land/manual@main/runtime/ffi_api



8
const libName = `./libadd.${libSuffix}`;
// Open library and define exported symbols
const dylib = Deno.dlopen(libName, {
"add": { parameters: ["isize", "isize"], result: "isize" },
});
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
やりたかったこと



● 一方でlibmysql側のインターフェースは?


○ 例:コネクション貼りたい場合

■ https://dev.mysql.com/doc/refman/5.6/ja/mysql-real-connect.html


■ MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char
*user, const char *passwd, const char *db, unsigned int port, const
char *unix_socket, unsigned long client_flag)
■ MYSQL構造体が出てきているので現段階ではうまく扱えなさそう


● →とりあえず他の言語でどうやっているか見てみよう


9
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
ソースを見てみよう

● mysqlといえばRails

○ https://github.com/brianmario/mysql2

● Rubyには拡張ライブラリという仕組みがあります

○ https://docs.ruby-lang.org/en/2.4.0/extension_ja_rdoc.html

○ ルール基づいて作られたCライブラリをRuby側から一本のクラスやモジュールであるかのように呼び出せる

○ 抜粋

■ rb_define_module:新しいモジュール定義

■ rb_define_class_under: あるクラスを継承したクラスをモジュール配下に定義

■ rb_define_method:クラスにメソッドを定義

■ rb_define_private_method: クラスにプライベートメソッドを定義

○ Ruby側でどこからともなくconnectionメソッドが呼ばれているので、rb_define_method、rb_define_private_methodあたりで検
索かければなにかでてくるのでは?

10
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
ソースを見てみよう

● https://github.com/brianmario/mysql2/blob/6652da20010ddfbbe6bceb8e41666d05e512346c/ext/mysql2/client.c#L445

● 最終的な部分

11
static void *nogvl_connect(void *ptr) {
struct nogvl_connect_args *args = ptr;
MYSQL *client;
client = mysql_real_connect(args->mysql, args->host,
args->user, args->passwd,
args->db, args->port, args->unix_socket,
args->client_flag);
return (void *)(client ? Qtrue : Qfalse);
}
=>直接libmysqlclient.soとかを呼び出していることはなく、C言語等でラップされている 

Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
作ってみよう

● まずはコネクション取得(C側)

○ mysql_init→mysql_real_connectとする必要があるのでどちらのラッパー関数も定義(ついでにcloseも)

12
#include <mysql/mysql.h>
#include <stdio.h>
MYSQL *conn = NULL;
void init()
{
conn = mysql_init(NULL);
}
int connect(const char *host, const char *user, const char *passwd, const char *db_name, int port)
{
if (!mysql_real_connect(conn, host, user, passwd, db_name, port, NULL, 0)) {
fprintf(stderr, "Failed to connect to database: Error: %sn", mysql_error(conn));
fflush(stderr);
return 0;
}
return 1;
}
void close(void)
{
mysql_close(conn);
}
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
作ってみよう

● ネクション取得(deno側)

13
const C_TERMINAL_SYMBOL = "0";
const MYSQL_HOST = "localhost";
const MYSQL_USER = "root";
const MYSQL_PASSWD = "";
const MYSQL_DB_NAME = "mysql";
const MYSQL_PORT = 3306;
function strToBytes(word: string): Uint8Array {
return new TextEncoder().encode(`${word}${C_TERMINAL_SYMBOL}`);
}
function ffiSuffix(): string {
switch (Deno.build.os) {
case "windows":
return "dll";
case "darwin":
return "dylib";
case "linux":
return "so";
}
}
interface LibInterfaces extends Record<string, Deno.ForeignFunction> {
init: Deno.ForeignFunction;
connect: Deno.ForeignFunction;
close: Deno.ForeignFunction;
}
const libInterfaces: LibInterfaces = {
init: { parameters: [], result: "void" },
connect: {
parameters: ["buffer", "buffer", "buffer", "buffer", "i32"],
result: "i32",
},
close: { parameters: [], result: "void" },
};
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
作ってみよう

● コネクション取得続き(deno側)

14
class MySQL {
#dylib: Deno.DynamicLibrary<LibInterfaces> | null = null;
readonly host = strToBytes(MYSQL_HOST);
readonly user = strToBytes(MYSQL_USER);
readonly passwd = strToBytes(MYSQL_PASSWD);
readonly dbName = strToBytes(MYSQL_DB_NAME);
private isConn = false;
constructor() {
const libName = `./ext/mysql/mysql.${ffiSuffix()}`;
this.#dylib = Deno.dlopen(libName, libInterfaces);
this.#dylib.symbols.init();
const res = this.#dylib.symbols.connect(this.host, this.user, this.passwd, this.dbName, MYSQL_PORT);
if (res === 0) {
this.close();
throw new Error("コネクション確立に失敗しました。 ");
}
this.isConn = true;
}
close() {
if (!this.#dylib) {
return;
}
if (this.isConn) {
this.#dylib.symbols.close();
this.#dylib.close();
this.#dylib = null;
}
}
}
const client = new MySQL();
client.close();
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
作ってみよう

● 動かしてみる

○ libmysqlclient.(so|dll|dylib)のフォルダを指定してコンパイルする必要がある

○ 今回はコネクション貼って即時クローズするので、例外がでなければとりあえずOK

15
> gcc -c -fPIC -o ./ext/mysql/mysql.o ./ext/mysql/mysql.c
> gcc -shared -W -o ./ext/mysql/mysql.so ./ext/mysql/mysql.o -L/usr/lib/x86_64-linux-gnu -lmysqlclient
> deno run --unstable --allow-ffi mod.ts
(macの場合はlibフォルダが異なるので -L/usr/local/lib などとする)

Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
課題とハマったところ

● たまに接続文字列の後ろにゴミがくっついてエラーになる(“127.0.0.1L”とか)

○ bufferに文字列食わせるときに終端文字が必要だった(0)

○ C言語で文字列扱う場合は必要な処理だがDeno側に必要なのは盲点だった

● queryの結果取得が面倒

○ mysql_real_query→mysql_use_resultで結果取得といった流れ

○ mysql_use_resultの戻り値がMYSQL_RES構造体なのでこれもそのままは扱えない

○ また、まだbufferを戻り値にできない→クエリ実行結果を文字列としては戻せない

● そもそもRustでlibmysqlclientをラップしたほうが楽では?

○ こちらであればdeno_bindgenが使える

○ https://deno.land/manual@v1.16.3/runtime/ffi_api

16
Copyright  (C) 2021 Toranoana Inc. All Rights Reserved.
まとめ

● そのままでは複雑な構造体をI/Fとして持つものをFFIすることは難しそう


● CやC++でさらに使いたいdllやsoファイルをラップする必要がありそう


○ Cのグローバルにconnectionとか置かないとダメそう


■ Cの中だけに収まってくれるか?という勝負


● ただし、やり方さえ確立できればどんなものでもFFIできそう


● より細かい話は12/4に公開するアドベントカレンダーのブログで紹介しています。


17
1 of 17

Recommended

【20220120 toranoana.deno#4】denoでffiの続き by
【20220120 toranoana.deno#4】denoでffiの続き【20220120 toranoana.deno#4】denoでffiの続き
【20220120 toranoana.deno#4】denoでffiの続き虎の穴 開発室
174 views14 slides
GitHub APIとfreshで遊ぼう by
GitHub APIとfreshで遊ぼうGitHub APIとfreshで遊ぼう
GitHub APIとfreshで遊ぼう虎の穴 開発室
443 views22 slides
Deno の node 互換モードと ソケット by
Deno の node 互換モードと ソケットDeno の node 互換モードと ソケット
Deno の node 互換モードと ソケット虎の穴 開発室
264 views16 slides
【とらラボLT】go言語でのweb apiの作り方3選 by
【とらラボLT】go言語でのweb apiの作り方3選【とらラボLT】go言語でのweb apiの作り方3選
【とらラボLT】go言語でのweb apiの作り方3選虎の穴 開発室
436 views14 slides
はじめようVue3!ハンズオンでとらのあなラボのフロントエンドを学ぶ_20210611_TechDay#1 by
はじめようVue3!ハンズオンでとらのあなラボのフロントエンドを学ぶ_20210611_TechDay#1はじめようVue3!ハンズオンでとらのあなラボのフロントエンドを学ぶ_20210611_TechDay#1
はじめようVue3!ハンズオンでとらのあなラボのフロントエンドを学ぶ_20210611_TechDay#1虎の穴 開発室
554 views66 slides
Kotlinではじめる Webアプリケーション入門 by
Kotlinではじめる Webアプリケーション入門Kotlinではじめる Webアプリケーション入門
Kotlinではじめる Webアプリケーション入門虎の穴 開発室
10.8K views45 slides

More Related Content

What's hot

【とらのあなラボ Tech Day #3】新規システムにおける技術選定〜GoとgRPCを採用した話〜 by
【とらのあなラボ Tech Day #3】新規システムにおける技術選定〜GoとgRPCを採用した話〜	【とらのあなラボ Tech Day #3】新規システムにおける技術選定〜GoとgRPCを採用した話〜
【とらのあなラボ Tech Day #3】新規システムにおける技術選定〜GoとgRPCを採用した話〜 虎の穴 開発室
1.1K views30 slides
Rake by
RakeRake
RakeTomoya Kawanishi
1.2K views23 slides
Webアプリケーション開発者のためのDockerハンズオン by
Webアプリケーション開発者のためのDockerハンズオンWebアプリケーション開発者のためのDockerハンズオン
Webアプリケーション開発者のためのDockerハンズオン虎の穴 開発室
115 views45 slides
Shadow Server on Fluentd at Fluentd Casual Talks #3 by
Shadow Server on Fluentd at Fluentd Casual Talks #3Shadow Server on Fluentd at Fluentd Casual Talks #3
Shadow Server on Fluentd at Fluentd Casual Talks #3Naotoshi Seo
7.2K views41 slides
最近僕が使うようになったPerl 5.10以降の新しいやつ by
最近僕が使うようになったPerl 5.10以降の新しいやつ最近僕が使うようになったPerl 5.10以降の新しいやつ
最近僕が使うようになったPerl 5.10以降の新しいやつazumakuniyuki 🐈
1.2K views24 slides
オタク×Node.js勉強会 by
オタク×Node.js勉強会オタク×Node.js勉強会
オタク×Node.js勉強会虎の穴 開発室
5.4K views42 slides

What's hot(20)

【とらのあなラボ Tech Day #3】新規システムにおける技術選定〜GoとgRPCを採用した話〜 by 虎の穴 開発室
【とらのあなラボ Tech Day #3】新規システムにおける技術選定〜GoとgRPCを採用した話〜	【とらのあなラボ Tech Day #3】新規システムにおける技術選定〜GoとgRPCを採用した話〜
【とらのあなラボ Tech Day #3】新規システムにおける技術選定〜GoとgRPCを採用した話〜
虎の穴 開発室1.1K views
Webアプリケーション開発者のためのDockerハンズオン by 虎の穴 開発室
Webアプリケーション開発者のためのDockerハンズオンWebアプリケーション開発者のためのDockerハンズオン
Webアプリケーション開発者のためのDockerハンズオン
Shadow Server on Fluentd at Fluentd Casual Talks #3 by Naotoshi Seo
Shadow Server on Fluentd at Fluentd Casual Talks #3Shadow Server on Fluentd at Fluentd Casual Talks #3
Shadow Server on Fluentd at Fluentd Casual Talks #3
Naotoshi Seo7.2K views
最近僕が使うようになったPerl 5.10以降の新しいやつ by azumakuniyuki 🐈
最近僕が使うようになったPerl 5.10以降の新しいやつ最近僕が使うようになったPerl 5.10以降の新しいやつ
最近僕が使うようになったPerl 5.10以降の新しいやつ
azumakuniyuki 🐈1.2K views
PyCon JP 2012 hands-on セッション/ FlaskによるWebアプリケーションの実装とプログラミングツール by Atsuo Ishimoto
PyCon JP 2012 hands-on セッション/ FlaskによるWebアプリケーションの実装とプログラミングツールPyCon JP 2012 hands-on セッション/ FlaskによるWebアプリケーションの実装とプログラミングツール
PyCon JP 2012 hands-on セッション/ FlaskによるWebアプリケーションの実装とプログラミングツール
Atsuo Ishimoto23K views
【20211022_toranoana@LT#Vue3】Vue2からVue3にして困ったところ by 虎の穴 開発室
【20211022_toranoana@LT#Vue3】Vue2からVue3にして困ったところ【20211022_toranoana@LT#Vue3】Vue2からVue3にして困ったところ
【20211022_toranoana@LT#Vue3】Vue2からVue3にして困ったところ
社内ツールが支えるドリコムの社内勉強会文化 #metabenkyokai by Go Sueyoshi (a.k.a sue445)
社内ツールが支えるドリコムの社内勉強会文化 #metabenkyokai社内ツールが支えるドリコムの社内勉強会文化 #metabenkyokai
社内ツールが支えるドリコムの社内勉強会文化 #metabenkyokai
JSONでメール送信 | HTTP API Server ``Haineko''/YAPC::Asia Tokyo 2013 LT Day2 by azumakuniyuki 🐈
JSONでメール送信 | HTTP API Server ``Haineko''/YAPC::Asia Tokyo 2013 LT Day2JSONでメール送信 | HTTP API Server ``Haineko''/YAPC::Asia Tokyo 2013 LT Day2
JSONでメール送信 | HTTP API Server ``Haineko''/YAPC::Asia Tokyo 2013 LT Day2
azumakuniyuki 🐈1.9K views
Pelicanによる www.python.jpの構築 by Atsuo Ishimoto
Pelicanによる www.python.jpの構築Pelicanによる www.python.jpの構築
Pelicanによる www.python.jpの構築
Atsuo Ishimoto14.3K views
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの... by Yoshifumi Kawai
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
What, Why, How Create OSS Libraries - 過去に制作した30のライブラリから見るC#コーディングテクニックと個人OSSの...
Yoshifumi Kawai56.5K views
Resemaraを支えた技術 フライングゲットガチャの舞台裏 #ksgstudy #ドリコム by Go Sueyoshi (a.k.a sue445)
Resemaraを支えた技術 フライングゲットガチャの舞台裏 #ksgstudy #ドリコムResemaraを支えた技術 フライングゲットガチャの舞台裏 #ksgstudy #ドリコム
Resemaraを支えた技術 フライングゲットガチャの舞台裏 #ksgstudy #ドリコム
Markup Template Engine introduced Groovy 2.3 by Uehara Junji
Markup Template Engine introduced Groovy 2.3Markup Template Engine introduced Groovy 2.3
Markup Template Engine introduced Groovy 2.3
Uehara Junji6.1K views
AWS SDK for Smalltalk by Sho Yoshida
AWS SDK for SmalltalkAWS SDK for Smalltalk
AWS SDK for Smalltalk
Sho Yoshida3.5K views
Introduce Groovy 2.3 trait by Uehara Junji
Introduce Groovy 2.3 trait Introduce Groovy 2.3 trait
Introduce Groovy 2.3 trait
Uehara Junji7.7K views
Rust-DPDK by Masaru Oki
Rust-DPDKRust-DPDK
Rust-DPDK
Masaru Oki2.6K views

Similar to 【20211202_toranoana.deno#3】denoでFFI

レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる by
レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れるレガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる
レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れるsairoutine
3.9K views77 slides
Unityでlinqを使おう by
Unityでlinqを使おうUnityでlinqを使おう
Unityでlinqを使おうYuuki Takada
3.2K views26 slides
griffon plugin を 実際に作ってみよう #jggug by
griffon plugin を 実際に作ってみよう #jgguggriffon plugin を 実際に作ってみよう #jggug
griffon plugin を 実際に作ってみよう #jggugkimukou_26 Kimukou
1K views25 slides
スタート低レイヤー #0 by
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0Kiwamu Okabe
3.1K views27 slides
ニコニコを支える Erlang / Elixir by
ニコニコを支える Erlang / Elixirニコニコを支える Erlang / Elixir
ニコニコを支える Erlang / Elixirkojingharang
2.2K views68 slides
Newcomer2020 Docker研修 by
Newcomer2020 Docker研修Newcomer2020 Docker研修
Newcomer2020 Docker研修Suguru Yazawa
224 views38 slides

Similar to 【20211202_toranoana.deno#3】denoでFFI(20)

レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる by sairoutine
レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れるレガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる
レガシーな Perl システムに DDD (ドメイン駆動設計)を取り入れる
sairoutine3.9K views
Unityでlinqを使おう by Yuuki Takada
Unityでlinqを使おうUnityでlinqを使おう
Unityでlinqを使おう
Yuuki Takada3.2K views
griffon plugin を 実際に作ってみよう #jggug by kimukou_26 Kimukou
griffon plugin を 実際に作ってみよう #jgguggriffon plugin を 実際に作ってみよう #jggug
griffon plugin を 実際に作ってみよう #jggug
スタート低レイヤー #0 by Kiwamu Okabe
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0
Kiwamu Okabe3.1K views
ニコニコを支える Erlang / Elixir by kojingharang
ニコニコを支える Erlang / Elixirニコニコを支える Erlang / Elixir
ニコニコを支える Erlang / Elixir
kojingharang 2.2K views
Newcomer2020 Docker研修 by Suguru Yazawa
Newcomer2020 Docker研修Newcomer2020 Docker研修
Newcomer2020 Docker研修
Suguru Yazawa224 views
「AROW」お披露目(実用編) by Drecom Co., Ltd.
「AROW」お披露目(実用編)「AROW」お披露目(実用編)
「AROW」お披露目(実用編)
Drecom Co., Ltd.2.3K views
Dockerを使ってみよう by Ryo Adachi
Dockerを使ってみようDockerを使ってみよう
Dockerを使ってみよう
Ryo Adachi493 views
Unityネイティブプラグインマニアクス #denatechcon by DeNA
Unityネイティブプラグインマニアクス #denatechconUnityネイティブプラグインマニアクス #denatechcon
Unityネイティブプラグインマニアクス #denatechcon
DeNA15.7K views
Visual Studio Community 2013 で始めるプログラミング Win32/MFC #clrh93 by hiyohiyo
Visual Studio Community 2013 で始めるプログラミング Win32/MFC #clrh93Visual Studio Community 2013 で始めるプログラミング Win32/MFC #clrh93
Visual Studio Community 2013 で始めるプログラミング Win32/MFC #clrh93
hiyohiyo8.7K views
The Art of Network Protocols - RIP編 - by kirin_gumi
The Art of Network Protocols - RIP編 -The Art of Network Protocols - RIP編 -
The Art of Network Protocols - RIP編 -
kirin_gumi2.5K views
Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話 by azuma satoshi
Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話
Perl暦およそ10年(?)の僕がデータベースを使えるようになるまでの昔話
azuma satoshi2.9K views
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2 by tamtam180
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
Getting Started GraalVM / GraalVM超入門 #jjug_ccc #ccc_c2
tamtam180 8.3K views
Getting Started GraalVM (再アップロード) by tamtam180
Getting Started GraalVM (再アップロード)Getting Started GraalVM (再アップロード)
Getting Started GraalVM (再アップロード)
tamtam180 504 views
XenServerによるお手軽開発サーバ運用 by Shinya Okano
XenServerによるお手軽開発サーバ運用XenServerによるお手軽開発サーバ運用
XenServerによるお手軽開発サーバ運用
Shinya Okano4.9K views
20170527 inside .NET Core on Linux by Takayoshi Tanaka
20170527 inside .NET Core on Linux20170527 inside .NET Core on Linux
20170527 inside .NET Core on Linux
Takayoshi Tanaka1.5K views
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行 by gree_tech
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行コードの自動修正によって実現する、機能開発を止めないフレームワーク移行
コードの自動修正によって実現する、機能開発を止めないフレームワーク移行
gree_tech2.9K views
Node.jsでつくるNode.js ミニインタープリター&コンパイラー by mganeko
Node.jsでつくるNode.js ミニインタープリター&コンパイラーNode.jsでつくるNode.js ミニインタープリター&コンパイラー
Node.jsでつくるNode.js ミニインタープリター&コンパイラー
mganeko1.3K views
【学習メモ#1st】12ステップで作る組込みOS自作入門 by sandai
【学習メモ#1st】12ステップで作る組込みOS自作入門【学習メモ#1st】12ステップで作る組込みOS自作入門
【学習メモ#1st】12ステップで作る組込みOS自作入門
sandai10K views

More from 虎の穴 開発室

FizzBuzzで学ぶJavaの進化 by
FizzBuzzで学ぶJavaの進化FizzBuzzで学ぶJavaの進化
FizzBuzzで学ぶJavaの進化虎の穴 開発室
310 views33 slides
Railsのデバッグ どうやるかを改めて確認する by
Railsのデバッグ どうやるかを改めて確認するRailsのデバッグ どうやるかを改めて確認する
Railsのデバッグ どうやるかを改めて確認する虎の穴 開発室
421 views16 slides
虎の穴ラボ エンジニア採用説明資料 .pdf by
虎の穴ラボ エンジニア採用説明資料 .pdf虎の穴ラボ エンジニア採用説明資料 .pdf
虎の穴ラボ エンジニア採用説明資料 .pdf虎の穴 開発室
53.2K views34 slides
Deno Deployと組み合わせるのに Upstashをおすすめしたい.pdf by
Deno Deployと組み合わせるのに Upstashをおすすめしたい.pdfDeno Deployと組み合わせるのに Upstashをおすすめしたい.pdf
Deno Deployと組み合わせるのに Upstashをおすすめしたい.pdf虎の穴 開発室
208 views20 slides
toranoana.deno #6 アジェンダ 採用説明 by
toranoana.deno #6 アジェンダ 採用説明toranoana.deno #6 アジェンダ 採用説明
toranoana.deno #6 アジェンダ 採用説明虎の穴 開発室
391 views14 slides
Deno 向け WEB 開発用のツールを作ったので 紹介します by
Deno 向け WEB 開発用のツールを作ったので 紹介しますDeno 向け WEB 開発用のツールを作ったので 紹介します
Deno 向け WEB 開発用のツールを作ったので 紹介します虎の穴 開発室
299 views12 slides

More from 虎の穴 開発室(20)

Railsのデバッグ どうやるかを改めて確認する by 虎の穴 開発室
Railsのデバッグ どうやるかを改めて確認するRailsのデバッグ どうやるかを改めて確認する
Railsのデバッグ どうやるかを改めて確認する
虎の穴ラボ エンジニア採用説明資料 .pdf by 虎の穴 開発室
虎の穴ラボ エンジニア採用説明資料 .pdf虎の穴ラボ エンジニア採用説明資料 .pdf
虎の穴ラボ エンジニア採用説明資料 .pdf
虎の穴 開発室53.2K views
Deno Deployと組み合わせるのに Upstashをおすすめしたい.pdf by 虎の穴 開発室
Deno Deployと組み合わせるのに Upstashをおすすめしたい.pdfDeno Deployと組み合わせるのに Upstashをおすすめしたい.pdf
Deno Deployと組み合わせるのに Upstashをおすすめしたい.pdf
Deno 向け WEB 開発用のツールを作ったので 紹介します by 虎の穴 開発室
Deno 向け WEB 開発用のツールを作ったので 紹介しますDeno 向け WEB 開発用のツールを作ったので 紹介します
Deno 向け WEB 開発用のツールを作ったので 紹介します
Supabase Edge Functions と Netlify Edge Functions を使ってみる – 機能とその比較 – by 虎の穴 開発室
Supabase Edge Functions と Netlify Edge Functions を使ってみる – 機能とその比較 –Supabase Edge Functions と Netlify Edge Functions を使ってみる – 機能とその比較 –
Supabase Edge Functions と Netlify Edge Functions を使ってみる – 機能とその比較 –
通販開発部の西田さん「通販開発マネジメントの5ルール」 by 虎の穴 開発室
通販開発部の西田さん「通販開発マネジメントの5ルール」通販開発部の西田さん「通販開発マネジメントの5ルール」
通販開発部の西田さん「通販開発マネジメントの5ルール」
社内DX推進!非エンジニア向けにプログラミング講座を実施してみた! by 虎の穴 開発室
社内DX推進!非エンジニア向けにプログラミング講座を実施してみた!社内DX推進!非エンジニア向けにプログラミング講座を実施してみた!
社内DX推進!非エンジニア向けにプログラミング講座を実施してみた!
セキュリティを強化しよう!CloudArmorの機能解説 by 虎の穴 開発室
セキュリティを強化しよう!CloudArmorの機能解説セキュリティを強化しよう!CloudArmorの機能解説
セキュリティを強化しよう!CloudArmorの機能解説
JavaScript LT会 〜 React.js Node.js歓迎 〜 Deno で やってみるweb開発 by 虎の穴 開発室
JavaScript LT会 〜 React.js   Node.js歓迎 〜 Deno で やってみるweb開発JavaScript LT会 〜 React.js   Node.js歓迎 〜 Deno で やってみるweb開発
JavaScript LT会 〜 React.js Node.js歓迎 〜 Deno で やってみるweb開発
いいテスト会 (スプリントレビュー) をやろう! by 虎の穴 開発室
いいテスト会 (スプリントレビュー) をやろう!いいテスト会 (スプリントレビュー) をやろう!
いいテスト会 (スプリントレビュー) をやろう!
虎の穴ラボ Tech day#3 チームで戦う!とらのあな通販冬の大感謝祭でのフロント開発について by 虎の穴 開発室
虎の穴ラボ Tech day#3 チームで戦う!とらのあな通販冬の大感謝祭でのフロント開発について虎の穴ラボ Tech day#3 チームで戦う!とらのあな通販冬の大感謝祭でのフロント開発について
虎の穴ラボ Tech day#3 チームで戦う!とらのあな通販冬の大感謝祭でのフロント開発について
虎の穴ラボ TechDay#3 フルリモート率100%!リモートワークを可能にするマネージメント by 虎の穴 開発室
虎の穴ラボ TechDay#3 フルリモート率100%!リモートワークを可能にするマネージメント 虎の穴ラボ TechDay#3 フルリモート率100%!リモートワークを可能にするマネージメント
虎の穴ラボ TechDay#3 フルリモート率100%!リモートワークを可能にするマネージメント
虎の穴ラボ エンジニア採用説明資料 by 虎の穴 開発室
虎の穴ラボ エンジニア採用説明資料 虎の穴ラボ エンジニア採用説明資料
虎の穴ラボ エンジニア採用説明資料
虎の穴ラボにおけるリモートワークの働き方 by 虎の穴 開発室
虎の穴ラボにおけるリモートワークの働き方虎の穴ラボにおけるリモートワークの働き方
虎の穴ラボにおけるリモートワークの働き方

Recently uploaded

JJUG CCC.pptx by
JJUG CCC.pptxJJUG CCC.pptx
JJUG CCC.pptxKanta Sasaki
6 views14 slides
SNMPセキュリティ超入門 by
SNMPセキュリティ超入門SNMPセキュリティ超入門
SNMPセキュリティ超入門mkoda
301 views15 slides
The Things Stack説明資料 by The Things Industries by
The Things Stack説明資料 by The Things IndustriesThe Things Stack説明資料 by The Things Industries
The Things Stack説明資料 by The Things IndustriesCRI Japan, Inc.
51 views29 slides
さくらのひやおろし2023 by
さくらのひやおろし2023さくらのひやおろし2023
さくらのひやおろし2023法林浩之
94 views58 slides
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料) by
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)NTT DATA Technology & Innovation
18 views38 slides

Recently uploaded(11)

SNMPセキュリティ超入門 by mkoda
SNMPセキュリティ超入門SNMPセキュリティ超入門
SNMPセキュリティ超入門
mkoda301 views
The Things Stack説明資料 by The Things Industries by CRI Japan, Inc.
The Things Stack説明資料 by The Things IndustriesThe Things Stack説明資料 by The Things Industries
The Things Stack説明資料 by The Things Industries
CRI Japan, Inc.51 views
さくらのひやおろし2023 by 法林浩之
さくらのひやおろし2023さくらのひやおろし2023
さくらのひやおろし2023
法林浩之94 views
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料) by NTT DATA Technology & Innovation
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
速習! PostgreSQL専用HAソフトウェア: Patroni(PostgreSQL Conference Japan 2023 発表資料)
Windows 11 information that can be used at the development site by Atomu Hidaka
Windows 11 information that can be used at the development siteWindows 11 information that can be used at the development site
Windows 11 information that can be used at the development site
Atomu Hidaka80 views
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20... by NTT DATA Technology & Innovation
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...
今、改めて考えるPostgreSQLプラットフォーム - マルチクラウドとポータビリティ -(PostgreSQL Conference Japan 20...
SSH応用編_20231129.pdf by icebreaker4
SSH応用編_20231129.pdfSSH応用編_20231129.pdf
SSH応用編_20231129.pdf
icebreaker4287 views

【20211202_toranoana.deno#3】denoでFFI

  • 1. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. denoでFFI
 
 虎の穴ラボ 藤原 佳顕
 1
  • 2. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. アジェンダ
 ● 概要 ● やりたかったこと ● ソースを見てみよう ● 作ってみよう 2
  • 3. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. 自己紹介
 3 ● 名前
 ○ 藤原佳顕(@nuhera, @zonuko)
 ● 仕事
 ○ FantiaとかCreatiaとか社内アプリとか
 ● 好み
 ○ Clojure、Rust
 ● 趣味
 ○ 格闘ゲーム
 ■ Melty Blood、Guilty Gear
 ○ STG(ダライアスとか)も好き
 ○ 最近は女神転生Vずっとやってました

  • 4. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. 概要
 4 ● deno1.13あたりからFFI機能が追加されています 
 ○ what’s ffi?
 ■ 別の言語で作られた関すなどをまた別の言語から呼び出せる機能 
 ■ deno固有の言葉ではない
 ○ これによってDLLやSOファイルなど、いわゆるダイナミック/スタティックライブラリを Denoから呼び出せるように

  • 5. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. 概要
 5 ● 代表的なライブラリ
 ○ と言われて何が思い浮かびますか? 
 ○ libmysqlclient.so
 ○ RTP
 ■ RPGツクールのランタイムライブラリ群 
 ○ Win32 DLL (kernel32.dll、user32.dll、gdi32.dllなど) 

  • 6. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. やりたかったこと
 ● DenoにFFIが入ったということはlibmysqlclient経由でmysql接続できるのでは?
 ● mysqlの接続方法自体はいくつかある
 ○ TCP接続
 ○ Unix Domain Socket接続
 ○ 名前付きパイプ
 ■ プロセス間通信で一般的に使われるやつ
 
 
 6
  • 7. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. やりたかったこと
 ● とはいえDenoにはmysql用のライブラリがすでにある
 ○ https://deno.land/x/mysql@v2.10.1
 
 
 7 private async _connect() { // TODO: implement connect timeout const { hostname, port = 3306, socketPath, username = "", password } = this.config; log.info(`connecting ${this.remoteAddr}`); this.conn = !socketPath ? await Deno.connect({ transport: "tcp", hostname, port, }) : await Deno.connect({ transport: "unix", path: socketPath, } as any); … mysqlがインストールされているかに関わらず、tcp/ドメイ ンソケット経由なのがわかる
 

  • 8. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. やりたかったこと
 
 ● ではDenoでのFFIするときにインターフェースはどうなっているか
 ○ ソースはこんな感じ
 
 
 
 
 
 ● 対応している型はchar / signed char, unsigned char, short int, unsigned short int, int / signed int, unsigned int, long long int, unsigned long long int, size_t, float, double, void, const uint8_t *
 ○ https://deno.land/manual@main/runtime/ffi_api
 
 8 const libName = `./libadd.${libSuffix}`; // Open library and define exported symbols const dylib = Deno.dlopen(libName, { "add": { parameters: ["isize", "isize"], result: "isize" }, });
  • 9. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. やりたかったこと
 
 ● 一方でlibmysql側のインターフェースは? 
 ○ 例:コネクション貼りたい場合
 ■ https://dev.mysql.com/doc/refman/5.6/ja/mysql-real-connect.html 
 ■ MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag) ■ MYSQL構造体が出てきているので現段階ではうまく扱えなさそう 
 ● →とりあえず他の言語でどうやっているか見てみよう 
 9
  • 10. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. ソースを見てみよう
 ● mysqlといえばRails
 ○ https://github.com/brianmario/mysql2
 ● Rubyには拡張ライブラリという仕組みがあります
 ○ https://docs.ruby-lang.org/en/2.4.0/extension_ja_rdoc.html
 ○ ルール基づいて作られたCライブラリをRuby側から一本のクラスやモジュールであるかのように呼び出せる
 ○ 抜粋
 ■ rb_define_module:新しいモジュール定義
 ■ rb_define_class_under: あるクラスを継承したクラスをモジュール配下に定義
 ■ rb_define_method:クラスにメソッドを定義
 ■ rb_define_private_method: クラスにプライベートメソッドを定義
 ○ Ruby側でどこからともなくconnectionメソッドが呼ばれているので、rb_define_method、rb_define_private_methodあたりで検 索かければなにかでてくるのでは?
 10
  • 11. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. ソースを見てみよう
 ● https://github.com/brianmario/mysql2/blob/6652da20010ddfbbe6bceb8e41666d05e512346c/ext/mysql2/client.c#L445
 ● 最終的な部分
 11 static void *nogvl_connect(void *ptr) { struct nogvl_connect_args *args = ptr; MYSQL *client; client = mysql_real_connect(args->mysql, args->host, args->user, args->passwd, args->db, args->port, args->unix_socket, args->client_flag); return (void *)(client ? Qtrue : Qfalse); } =>直接libmysqlclient.soとかを呼び出していることはなく、C言語等でラップされている 

  • 12. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. 作ってみよう
 ● まずはコネクション取得(C側)
 ○ mysql_init→mysql_real_connectとする必要があるのでどちらのラッパー関数も定義(ついでにcloseも)
 12 #include <mysql/mysql.h> #include <stdio.h> MYSQL *conn = NULL; void init() { conn = mysql_init(NULL); } int connect(const char *host, const char *user, const char *passwd, const char *db_name, int port) { if (!mysql_real_connect(conn, host, user, passwd, db_name, port, NULL, 0)) { fprintf(stderr, "Failed to connect to database: Error: %sn", mysql_error(conn)); fflush(stderr); return 0; } return 1; } void close(void) { mysql_close(conn); }
  • 13. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. 作ってみよう
 ● ネクション取得(deno側)
 13 const C_TERMINAL_SYMBOL = "0"; const MYSQL_HOST = "localhost"; const MYSQL_USER = "root"; const MYSQL_PASSWD = ""; const MYSQL_DB_NAME = "mysql"; const MYSQL_PORT = 3306; function strToBytes(word: string): Uint8Array { return new TextEncoder().encode(`${word}${C_TERMINAL_SYMBOL}`); } function ffiSuffix(): string { switch (Deno.build.os) { case "windows": return "dll"; case "darwin": return "dylib"; case "linux": return "so"; } } interface LibInterfaces extends Record<string, Deno.ForeignFunction> { init: Deno.ForeignFunction; connect: Deno.ForeignFunction; close: Deno.ForeignFunction; } const libInterfaces: LibInterfaces = { init: { parameters: [], result: "void" }, connect: { parameters: ["buffer", "buffer", "buffer", "buffer", "i32"], result: "i32", }, close: { parameters: [], result: "void" }, };
  • 14. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. 作ってみよう
 ● コネクション取得続き(deno側)
 14 class MySQL { #dylib: Deno.DynamicLibrary<LibInterfaces> | null = null; readonly host = strToBytes(MYSQL_HOST); readonly user = strToBytes(MYSQL_USER); readonly passwd = strToBytes(MYSQL_PASSWD); readonly dbName = strToBytes(MYSQL_DB_NAME); private isConn = false; constructor() { const libName = `./ext/mysql/mysql.${ffiSuffix()}`; this.#dylib = Deno.dlopen(libName, libInterfaces); this.#dylib.symbols.init(); const res = this.#dylib.symbols.connect(this.host, this.user, this.passwd, this.dbName, MYSQL_PORT); if (res === 0) { this.close(); throw new Error("コネクション確立に失敗しました。 "); } this.isConn = true; } close() { if (!this.#dylib) { return; } if (this.isConn) { this.#dylib.symbols.close(); this.#dylib.close(); this.#dylib = null; } } } const client = new MySQL(); client.close();
  • 15. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. 作ってみよう
 ● 動かしてみる
 ○ libmysqlclient.(so|dll|dylib)のフォルダを指定してコンパイルする必要がある
 ○ 今回はコネクション貼って即時クローズするので、例外がでなければとりあえずOK
 15 > gcc -c -fPIC -o ./ext/mysql/mysql.o ./ext/mysql/mysql.c > gcc -shared -W -o ./ext/mysql/mysql.so ./ext/mysql/mysql.o -L/usr/lib/x86_64-linux-gnu -lmysqlclient > deno run --unstable --allow-ffi mod.ts (macの場合はlibフォルダが異なるので -L/usr/local/lib などとする)

  • 16. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. 課題とハマったところ
 ● たまに接続文字列の後ろにゴミがくっついてエラーになる(“127.0.0.1L”とか)
 ○ bufferに文字列食わせるときに終端文字が必要だった(0)
 ○ C言語で文字列扱う場合は必要な処理だがDeno側に必要なのは盲点だった
 ● queryの結果取得が面倒
 ○ mysql_real_query→mysql_use_resultで結果取得といった流れ
 ○ mysql_use_resultの戻り値がMYSQL_RES構造体なのでこれもそのままは扱えない
 ○ また、まだbufferを戻り値にできない→クエリ実行結果を文字列としては戻せない
 ● そもそもRustでlibmysqlclientをラップしたほうが楽では?
 ○ こちらであればdeno_bindgenが使える
 ○ https://deno.land/manual@v1.16.3/runtime/ffi_api
 16
  • 17. Copyright  (C) 2021 Toranoana Inc. All Rights Reserved. まとめ
 ● そのままでは複雑な構造体をI/Fとして持つものをFFIすることは難しそう 
 ● CやC++でさらに使いたいdllやsoファイルをラップする必要がありそう 
 ○ Cのグローバルにconnectionとか置かないとダメそう 
 ■ Cの中だけに収まってくれるか?という勝負 
 ● ただし、やり方さえ確立できればどんなものでもFFIできそう 
 ● より細かい話は12/4に公開するアドベントカレンダーのブログで紹介しています。 
 17