这是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
您误解了
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
(顺序错误)并尝试写入甚至未分配的内存位置。总结:
GoSlice
以匹配本机标头
toArray()
为它们的数组分配空间。
data
字段写入字符串值。
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
例如,从 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系统的第一篇文章😊