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

[前回](https://qiita.com/kyorohiro/items/c99ed94bfadd2fafbf6d)の続きです。
pointer と Buffer を使ってみましょう


# Pointer と Buffer を 使ってみる




# C言語の関数を作成する。

```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

```dart: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ブラウザー

```dart: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]);
  }
}

```

```js: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に対応する値を読み込む時は

```dart:ioの場合
buffer.elementAt(i).value
```
として参照できます。

```dart: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を用意してアクセスすると安全かも知れません

```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