如何使用JNA传递byte[][]去

问题描述 投票:0回答:2

这是go代码

import (
    "C"
    "fmt"
)
//export Print
func Print(keys, values [][]byte) {
  for len(keys) > 0 {
  err := txn.Set(keys[0], values[0])
  errMustBeNil(err)
  fmt.Printf("%s %s", string(keys[0]), string(values[0]))
  keys = keys[1:]
  values = values[1:]
 }
}

这是libPrint.h

/*
  static assertion to make sure the file is being used on architecture
  at least with matching size of GoInt.
*/
typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];

#ifndef GO_CGO_GOSTRING_TYPEDEF
typedef _GoString_ GoString;
#endif
typedef void *GoMap;
typedef void *GoChan;
typedef struct { void *t; void *v; } GoInterface;
typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

#endif

/* End of boilerplate cgo prologue.  */

#ifdef __cplusplus
extern "C" {
#endif

extern void Print(GoSlice keys, GoSlice values);

#ifdef __cplusplus
}
#endif

这是我的Java代码,我想将byte[]一一放入指针,

GoSlice
代表
go
中的数组[],我不知道这是否正确

public interface Test extends Library {
  Test TEST = (Test) Native.load("Print", Test.class);

  void Print(GoSlice key, GoSlice val);

  class GoSlice extends Structure {
    public Pointer[] data;
    public long len;
    public long cap;
  }
  static void main(String[] args) {
    byte[] byte1 = "key1".getBytes(StandardCharsets.UTF_8);
    byte[] byte2 = "value1new".getBytes(StandardCharsets.UTF_8);

    GoSlice keys = new GoSlice();
    keys.data = new Pointer[1];
    keys.data[0] = new Pointer(byte1.length + 1);
    keys.len = 1;
    keys.cap = 1;

    GoSlice values = new GoSlice();
    values.data = new Pointer[1];
    keys.data[0] = new Pointer(byte2.length + 1);
    values.len = 1;
    values.cap = 1;

    keys.data[0].write(0, byte1, 0, byte1.length);
    values.data[0].write(0, byte2, 0, byte2.length);

    Test.TEST.Print(keys, values);
  }
}

但是运行不正常,感觉byte[][]没有正确转换为go中的类型。

这是运行Java程序后的控制台日志

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  SIGSEGV (0xb) at pc=0x00000001a2775a34, pid=64329, tid=0x0000000000002903
#
# JRE version: OpenJDK Runtime Environment (Zulu 8.62.0.19-CA-macos-aarch64) (8.0_332-b09) (build 1.8.0_332-b09)
# Java VM: OpenJDK 64-Bit Server VM (25.332-b09 mixed mode bsd-aarch64 compressed oops)
# Problematic frame:
# C  [libsystem_platform.dylib+0x3a34]  _platform_memmove+0xf4
#
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
# An error report file with more information is saved as:
# /Users/user/project/hs_err_pid64329.log
#
# If you would like to submit a bug report, please visit:
#   http://www.azul.com/support/
#

这是我的第一个改变

public interface TxnClient extends Library {
  TxnClient TNX_CLIENT = (TxnClient) Native.load("Tikv", TxnClient.class);

  int TxnSave(GoSlice[] key, GoSlice[] val);

  class GoSlice extends Structure {
    public long cap;
    public Pointer data;
    public long len;

    @Override
    protected List<String> getFieldOrder() {
      return Arrays.asList("cap", "data", "len");
    }
  }

  static void main(String[] args) {
    byte[] key1 = "key1".getBytes(StandardCharsets.UTF_8);
    byte[] value1 = "value1new".getBytes(StandardCharsets.UTF_8);
    byte[] key2 = "key2".getBytes(StandardCharsets.UTF_8);
    byte[] value2 = "value2new".getBytes(StandardCharsets.UTF_8);

    GoSlice tmp = new GoSlice();
    GoSlice[] keys = (GoSlice[]) tmp.toArray(2);
    keys[0].data = new Memory(key1.length + 1);
    keys[0].len = keys[0].cap = key1.length;
    keys[0].data.write(0, key1, 0, key1.length);

    keys[1].data = new Memory(key2.length + 1);
    keys[1].len = keys[1].cap = key2.length;
    keys[1].data.write(0, key2, 0, key2.length);

    GoSlice[] values = (GoSlice[]) tmp.toArray(2);
    values[0].data = new Memory(value1.length + 1);
    values[0].len = values[0].cap = value1.length;
    values[0].data.write(0, value1, 0, value1.length);

    values[1].data = new Memory(value2.length + 1);
    values[1].len = values[1].cap = value2.length;
    values[1].data.write(0, value2, 0, value2.length);

    int i = TxnClient.TNX_CLIENT.TxnSave(keys, values);
    System.out.println(i);
  }
}
5431788016 5431788016panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x9 pc=0x1225b2b80]

goroutine 17 [running, locked to thread]:
main.TxnSave({0x9, 0x143c281f0, 0x14000182000?}, {0x9, 0x143c281f0, 0x121daf2c8?})
    /Users/user/project/print.go:52 +0x180

第二次改变

//export TxnSave
func TxnSave(keys, values [][]byte) (res int) {
 // ignore
}
public interface TxnClient extends Library {
  TxnClient TNX_CLIENT = (TxnClient) Native.load("Tikv", TxnClient.class);

  int TxnSave(GoSlice key, GoSlice val);

  class GoSlice extends Structure {
    public long cap;
    public Pointer data;
    public long len;

    @Override
    protected List<String> getFieldOrder() {
      return Arrays.asList("cap", "data", "len");
    }
  }

  static void main(String[] args) {
    byte[] key1 = "key1".getBytes(StandardCharsets.UTF_8);
    byte[] value1 = "value1new".getBytes(StandardCharsets.UTF_8);
    byte[] key2 = "key2".getBytes(StandardCharsets.UTF_8);
    byte[] value2 = "value2new".getBytes(StandardCharsets.UTF_8);

    GoSlice keys = new GoSlice();
    keys.data = new Memory(key1.length + key2.length + 1);
    keys.data.write(0, key1, 0, key1.length);
    keys.len = keys.cap = key1.length + key2.length;
    keys.data.write(key1.length, key2, 0, key2.length);

    GoSlice values = new GoSlice();
    values.data = new Memory(value1.length + value2.length + 1);
    values.len = values.cap = value1.length + value2.length;
    values.data.write(0, value1, 0, value1.length);
    values.data.write(value1.length, value2, 0, value2.length);

    int i = TxnClient.TNX_CLIENT.TxnSave(keys, values);
    System.out.println(i);
  }
}
[2022/07/31 22:36:57.225 +08:00] [INFO] [client.go:378] ["[pd] create pd client with endpoints"] [pd-address="[127.0.0.1:2379]"]
[2022/07/31 22:36:57.228 +08:00] [INFO] [base_client.go:350] ["[pd] switch leader"] [new-leader=http://127.0.0.1:2379] [old-leader=]
[2022/07/31 22:36:57.228 +08:00] [INFO] [base_client.go:105] ["[pd] init cluster id"] [cluster-id=7126545341327379321]
[2022/07/31 22:36:57.228 +08:00] [INFO] [client.go:673] ["[pd] tso dispatcher created"] [dc-location=global]

4981923696 4981923392panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x8 pc=0x12b00a6e0]
// this is error
goroutine 17 [running, locked to thread]:
main.TxnSave({0x8, 0x128f21f70, 0x14000190000?}, {0x12, 0x128f21e40, 0x12a806d38?})
    /Users/user/go-tikv/tikv.go:53 +0x180
java arrays go jna
2个回答
1
投票

您误解了

Pointer
构造函数的参数。 这是本机对等值,而不是分配大小。

你拥有什么:

keys.data[0] = new Pointer(byte1.length + 1);

来自

Pointer
Javadoc

public Pointer(long peer)

从本机指针创建。除非您知道自己在做什么,否则不要使用此功能。

分配新内存的正确方法(内部调用

malloc()
)是扩展
Memory
Pointer
类。
Memory
构造函数的参数确实是大小:

public Memory(long size)

通过调用 C 的 malloc 在本机堆中分配空间。

参数:

大小 - 要分配的空间字节数

所以你想要:

keys.data[0] = new Memory(byte1.length + 1);
另一注意事项:

Memory

 在分配时不会被清除,因此,如果您想要“+1”空终止符,您需要 
clear()
 内存或在向其写入字符串时显式设置该空字节(或者可能附加一个空字节)在转换为字节之前先转换为 Java 中的字符串)。

更新您的后续问题:

C 标头(首先是

data

,然后是 
len
cap
)与您自己的 
GoSlice
 结构定义(将 
data
 放在中间)之间存在不匹配。当您将其传递给本机时,您只是发送初始 
long
 的值(可能为零),并收到 
nil
 指针错误。

此外,您没有为您正在执行的操作分配足够的内存。 当您在 JNA 中声明

Structure

 时,它会分配它所需的内存。  在您早期版本的代码中,您使用 
Structure.toArray()
 为更多结构分配更多内存——这是正确的(但您做了两次!)。  你应该这样做一次。

在最新的代码中,您将

value

 设置为一个 
GoSlice
 结构。对于两个 
long
Pointer
,该内存只有 24 个字节。  但是对于第二个数组值,您使用原始的 
data
 (顺序错误)并尝试写入甚至未分配的内存位置。

总结:

    在 Java 中声明字段顺序
  1. GoSlice
    以匹配本机标头
  2. 在初始 GoSlice 结构上使用结构的
  3. toArray()
     为它们的数组分配空间。
  4. 使用 GoSlice[n] 结构的
  5. data
     字段写入字符串值。

0
投票
问题涉及 JNA,但您可以使用

MetaFFI 轻松做到这一点。

例如,如果这是 Go 代码 (BytesPrinter.go):

package bytesprinter import "fmt" func PrintBytesArrays(bytesArrays [][]byte) [][]byte{ for _, bytes := range bytesArrays { fmt.Printf("%v\n", bytes) } return bytesArrays }
您可以使用 MetaFFI 编译器构建代码:

metaffi -c --idl BytesPrinter.go -g


这会构建一个 Go 共享对象(.dll 或 .so),您可以使用 MetaFFI 从多种语言访问该对象。

例如,从 Java 使用它:

// load Go MetaFFI plugin api.MetaFFIRuntime runtime = new MetaFFIRuntime("go"); runtime.loadRuntimePlugin(); // load the module (.dll in windows, .so in linux) MetaFFIModule bprinter = runtime.loadModule("BytesPrinter.dll"); // load the function var PrintBytesArrays = bprinter.load("callable=PrintBytesArrays", // parameters new MetaFFITypeInfo[]{new MetaFFITypeInfo(MetaFFITypes.MetaFFIUInt8Array, 2) }, // return values new MetaFFITypeInfo[]{new MetaFFITypeInfo(MetaFFITypes.MetaFFIUInt8Array, 2) }); // lets create some array byte[][] arr = new byte[][]{ new byte[]{1, 2, 3, 4, 5}, new byte[]{6, 7, 8, 9, 10} }; // call the function byte[][] result = (byte[][])PrintBytesArrays.call((Object)arr)[0]; // print the results System.out.printf("Returned back to java: " + Arrays.deepToString(result));
您不仅可以将它用于函数,还可以用于所有 Go 实体。

免责声明:我是MetaFFI的作者,这是我真正参考MetaFFI系统的第一篇文章😊

© www.soinside.com 2019 - 2024. All rights reserved.