LoginSignup
1
1

More than 3 years have passed since last update.

Dart とC言語の連携 : dart:js と dart:ffi の両立 (4) Pointer Buffer

Last updated at Posted at 2020-05-23

前回の続きです。
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としてBufferを扱う事ができます。

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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1