DEV Community

kyorohiro (kiyohiro kawamura)
kyorohiro (kiyohiro kawamura)

Posted on • Updated on

Dart and C : how to ffi and wasm (4) buffer pointer

This document is a continuation of the previous one.

In This document, I introduce to how to use ponter and buffer at ffi and wasm.

How to use Pointer and Buffer

Create clang function

#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);
}

Enter fullscreen mode Exit fullscreen mode

Call from linux server at dart:ffi

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
}



Enter fullscreen mode Exit fullscreen mode

Call from web browser at dart:js

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() {  
  // hello
  printHello(); // Hello!!
  // int
  print('${sumInt(10, 100)}'); // 110
  // double
  print('${sumDouble(10.1, 100.2)}'); // 110.3
  //
  // 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]);
  }
}


Enter fullscreen mode Exit fullscreen mode
to_uint8list = function(index, len) {
    var v =  new Uint8Array(Module.HEAP8.buffer, index, len);
    return v;
}
Enter fullscreen mode Exit fullscreen mode

Explanation

Pointer

Clang's pointer is used as Pointer object in dart:ffi. Clang's pointer is used as number object in dart:js.

If you want to use the value of the pointer

buffer.elementAt(i).value
Enter fullscreen mode Exit fullscreen mode
js.JsObject Module = js.context['Module'];
var HEAP8 = Module['HEAP8'];
typed.Uint8List bufferAsUint8List2 = (HEAP8 as typed.Int8List).buffer.asUint8List(buffer, 20);
bufferAsUint8List2[i]
Enter fullscreen mode Exit fullscreen mode

But I couldn't find any documentation to back this up about dart:js's code
I recommend the following code to use js function

to_uint8list = function(index, len) {
    var v =  new Uint8Array(Module.HEAP8.buffer, index, len);
    return v;
}
Enter fullscreen mode Exit fullscreen mode

About Uint8List

You can handle the Buffer as Uint8List
In the case of dart:io, buffer.asTypedList(20).
In the case of daer:js, (HEAP8 as typed.Int8List).buffer.asUint8List(buffer, 20)

And, if you change Uint8List, the C language Buffer will also change.

Note!!

Clang's buffer is released by free function.
but, it is not recommended to access released buffer.

Next Time

About Clang's Object

PS

Here's the code for this one
https://github.com/kyorohiro/dart_clang_codeserver/tree/03_buffer_int_double

Discussion (0)