前回の続きです。
pointer と Buffer を使ってみましょう
Pointer と Buffer を 使ってみる
C言語の関数を作成する。
ky.c
# include <stdio.h>
# include <stdlib.h>
// [Linux]
// find . -name "*.o" | xargs rm
// gcc -Wall -Werror -fpic -I. -c ky.c -o ky.o
// gcc -shared -o libky.so ky.o
// [Wasm]
// find . -name "*.o" | xargs rm
// find . -name "*.wasm" | xargs rm
// emcc ky.c -o ky.o
// emcc ky.o -o libky.js -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]' -s EXPORTED_FUNCTIONS="['_new_buffer','_init_buffer','_destroy_buffer']"
// cp libky.js ../web/libky.js
// cp libky.wasm ../web/libky.wasm
char* new_buffer(int size) {
char* ret = malloc(sizeof(char)*size);
return ret;
}
char* init_buffer(char* buffer, int size) {
for(int i=0;i<size;i++) {
buffer[i] = i;
}
return buffer;
}
void destroy_buffer(char* p) {
free(p);
}
Dartから 関数を呼び出す:Linux Server
main.dart
import 'dart:ffi' as ffi;
import 'dart:typed_data' as typed;
// dart ./bin/main.dart
ffi.DynamicLibrary dylib = ffi.DynamicLibrary.open('/app/libc/libky.so');
// char* new_buffer(int size)
typedef NewBufferFunc = ffi.Pointer<ffi.Uint8> Function(ffi.Int32 size);
typedef NewBuffer = ffi.Pointer<ffi.Uint8> Function(int size);
NewBuffer _new_buffer = dylib
.lookup<ffi.NativeFunction<NewBufferFunc>>('new_buffer')
.asFunction<NewBuffer>();
ffi.Pointer<ffi.Uint8> newBuffer(int length) {
return _new_buffer(length);
}
// char* init_buffer(char*, int size)
typedef InitBufferFunc = ffi.Pointer<ffi.Uint8> Function(ffi.Pointer<ffi.Uint8> buffer, ffi.Int32 size);
typedef InitBuffer = ffi.Pointer<ffi.Uint8> Function(ffi.Pointer<ffi.Uint8> buffer, int size);
InitBuffer _init_buffer = dylib
.lookup<ffi.NativeFunction<InitBufferFunc>>('init_buffer')
.asFunction<InitBuffer>();
ffi.Pointer<ffi.Uint8> initBuffer(ffi.Pointer<ffi.Uint8> buffer, int length) {
return _init_buffer(buffer, length);
}
// void destroy_buffer(char* p)
typedef DestroyBufferFunc = ffi.Void Function(ffi.Pointer<ffi.Uint8> buffer);
typedef DestroyBuffer = void Function(ffi.Pointer<ffi.Uint8> buffer);
DestroyBuffer _destroy_buffer = dylib
.lookup<ffi.NativeFunction<DestroyBufferFunc>>('init_buffer')
.asFunction<DestroyBuffer>();
void destroyBuffer(ffi.Pointer<ffi.Uint8> buffer) {
_destroy_buffer(buffer);
}
void main(List<String> args) {
// pointer and buffer
var buffer = newBuffer(20);
// new pointer
for(var i=0;i<20;i++){
print(buffer.elementAt(i).value); // random value or 0
}
// pointer -> pointer
buffer = initBuffer(buffer, 20);
for(var i=0;i<20;i++){
print(buffer.elementAt(i).value); // 0, 1, 2, 3, 4, ....19
}
// pointer -> uint8slist // 0, 1, 2, 3, 4, ....19
typed.Uint8List bufferAsUint8List = buffer.asTypedList(20);
for(var i=0;i<bufferAsUint8List.length;i++){
print(bufferAsUint8List[i]);
}
// set value into buffer
bufferAsUint8List[0] = 110;
print(buffer.elementAt(0).value); // 110
}
Dartから 関数を呼び出す:Webブラウザー
main.dart
import 'dart:js' as js;
import 'dart:typed_data' as typed;
// webdev serve --hostname=0.0.0.0
js.JsObject Module = js.context['Module'];
var HEAP8 = Module['HEAP8'];
js.JsFunction _new_buffer = Module.callMethod('cwrap',['new_buffer','number',['number']]);
int newBuffer(int length) {
return _new_buffer.apply([length]);
}
js.JsFunction _init_buffer = Module.callMethod('cwrap',['init_buffer','number',['number','number']]);
int initBuffer(int buffer, int length) {
return _init_buffer.apply([buffer, length]);
}
js.JsFunction _destroy_buffer = Module.callMethod('cwrap',['destroy_buffer','void',['number']]);
int destroyBuffer(int buffer) {
return _destroy_buffer.apply([buffer]);
}
js.JsFunction _to_uint8list= js.context['to_uint8list'];// from util.js
typed.Uint8List toUint8List(int buffer, int length) {
return _to_uint8list.apply([buffer, length]);
}
void main() {
//
// new pointer
var buffer = newBuffer(20);
for(var i=0;i<20;i++){
print('${HEAP8[i+buffer]}'); // random value or 0
}
// pointer -> pointer
buffer = initBuffer(buffer, 20);
for(var i=0;i<20;i++){
print('${HEAP8[i+buffer]}'); // random value or 0
}
// pointer -> uint8slist // 0, 1, 2, 3, 4, ....19
typed.Uint8List bufferAsUint8List = toUint8List(buffer, 20);
for(var i=0;i<bufferAsUint8List.length;i++){
print(bufferAsUint8List[i]);
}
// set value into buffer
bufferAsUint8List[0] = 110;
print('${HEAP8[buffer]}'); // 110
print("${HEAP8.runtimeType}");//
typed.Uint8List bufferAsUint8List2 = (HEAP8 as typed.Int8List).buffer.asUint8List(buffer, 20);
for(var i=0;i<bufferAsUint8List2.length;i++){
print(bufferAsUint8List2[i]);
}
}
util.js
to_uint8list = function(index, len) {
var v = new Uint8Array(Module.HEAP8.buffer, index, len);
return v;
}
解説
Pointer への参照
Pointerは、dart:ffiでは、ffi.Pointer<ffi.Uint8>
で、 dart:jsでは'number' で扱います。
実際のpointerに対応する値を読み込む時は
ioの場合
buffer.elementAt(i).value
として参照できます。
jsの場合
js.JsObject Module = js.context['Module'];
var HEAP8 = Module['HEAP8'];
typed.Uint8List bufferAsUint8List2 = (HEAP8 as typed.Int8List).buffer.asUint8List(buffer, 20);
bufferAsUint8List2[i]
として参照できます。
ただ、HEAP8 は Int8List に変換されるようですが、対応するでキュメントがありませんでした。
ので、以下のようなJSを用意してアクセスすると安全かも知れません
to_uint8list = function(index, len) {
var v = new Uint8Array(Module.HEAP8.buffer, index, len);
return v;
}
Uint8List への対応
ioの場合は、buffer.asTypedList(20)
で、jsの場合は、`(HEAP8 as typed.Int8List).buffer.asUint8List(buffer, 20);
Uint8Listに変換後に変更すると、C言語のBufferも変更されます。
もしかすると、Bufferの使い回しや、offsetのコントロールが出来るので、普段よりも扱いやすいかも知れません。
# 注意点!!
C言語でmallocされたメモリーはfreeで解放できます。
freeで解放した後で、そのメモリーにアクセスするのは推奨できません。
適切にメモリーを管理するようにしてください!!
# 次回
オブジェクトを扱ってみます。
# PS
今回のコードは以下です
https://github.com/kyorohiro/dart_clang_codeserver/tree/03_buffer_int_double
以下でも書いています
https://dev.to/kyorohiro/dart-and-c-how-to-ffi-and-wasm-3-int-doube-buffer-pointer-832