简体   繁体   English

如何通过 cgo/JNA 传递 Java 字符串数组 []String to Go

[英]How to pass a Java string array []String to Go via cgo/JNA

I would like to pass a String[] in Java to my Go function via JNA.我想通过 JNA 将 Java 中的 String[] 传递给我的 Go 函数。

My go function has the following signature:我的 go 函数具有以下签名:

func PredicateEval(keys, values []string, expression string) *C.char

I have compiled the go library with link mode as "c-shared".我已将链接模式的 go 库编译为“c-shared”。 I have a GoString in java defined as:我在 Java 中有一个GoString定义为:

package predicates;
import com.ochafik.lang.jnaerator.runtime.NativeSize;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;
/**
 * <i>native declaration : coverage_server/predicate_jvm_bridge/lib/libtest.h</i><br>
 * This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br>
 * a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br>
 * For help, please visit <a href="http://nativelibs4java.googlecode.com/">NativeLibs4Java</a> , <a href="http://rococoa.dev.java.net/">Rococoa</a>, or <a href="http://jna.dev.java.net/">JNA</a>.
 */
public class _GoString_ extends Structure {
    /** C type : const char* */
    public Pointer p;
    public NativeSize n;
    public _GoString_() {
        super();
    }
    protected List<String> getFieldOrder() {
        return Arrays.asList("p", "n");
    }
    /** @param p C type : const char* */
    public _GoString_(Pointer p, NativeSize n) {
        super();
        this.p = p;
        this.n = n;
    }
    public static class ByReference extends _GoString_ implements Structure.ByReference {
        
    };
    public static class ByValue extends _GoString_ implements Structure.ByValue {
        
    };
}

I also have a Go slice defined as:我还有一个 Go 切片定义为:

package predicates;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import java.util.Arrays;
import java.util.List;
/**
 * <i>native declaration : coverage_server/predicate_jvm_bridge/lib/libtest.h</i><br>
 * This file was autogenerated by <a href="http://jnaerator.googlecode.com/">JNAerator</a>,<br>
 * a tool written by <a href="http://ochafik.com/">Olivier Chafik</a> that <a href="http://code.google.com/p/jnaerator/wiki/CreditsAndLicense">uses a few opensource projects.</a>.<br>
 * For help, please visit <a href="http://nativelibs4java.googlecode.com/">NativeLibs4Java</a> , <a href="http://rococoa.dev.java.net/">Rococoa</a>, or <a href="http://jna.dev.java.net/">JNA</a>.
 */
public class GoSlice extends Structure {
    /** C type : void* */
    public Pointer data;
    /** C type : GoInt */
    public long len;
    /** C type : GoInt */
    public long cap;
    public GoSlice() {
        super();
    }
    protected List<String> getFieldOrder() {
        return Arrays.asList("data", "len", "cap");
    }
    /**
     * @param data C type : void*<br>
     * @param len C type : GoInt<br>
     * @param cap C type : GoInt
     */
    public GoSlice(Pointer data, long len, long cap) {
        super();
        this.data = data;
        this.len = len;
        this.cap = cap;
    }
    public static class ByReference extends GoSlice implements Structure.ByReference {
        
    };
    public static class ByValue extends GoSlice implements Structure.ByValue {
        
    };
}

This is my attempt to convert a Java []String to a Go string[].这是我尝试将 Java []String 转换为 Go string[]。

  static {
    try {
      Field field = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
      field.setAccessible(true);
      unsafe = (sun.misc.Unsafe) field.get(null);
      Class<?> clazz = ByteBuffer.allocateDirect(0).getClass();
      DIRECT_BYTE_BUFFER_ADDRESS_OFFSET = unsafe.objectFieldOffset(Buffer.class.getDeclaredField("address"));
      DIRECT_BYTE_BUFFER_CLASS = clazz;
    } catch (Exception e) {
      throw new AssertionError(e);
    }
  }

  private static long getAddress(ByteBuffer buffer) {
    assert buffer.getClass() == DIRECT_BYTE_BUFFER_CLASS;
    return unsafe.getLong(buffer, DIRECT_BYTE_BUFFER_ADDRESS_OFFSET);
  }

  public static _GoString_.ByValue JavaStringToGo(String jstr) {
    try {
      byte[] bytes = jstr.getBytes("utf-8");
      //ByteBuffer bb = ByteBuffer.wrap(bytes);
      ByteBuffer bb = ByteBuffer.allocateDirect(bytes.length);
      bb.put(bytes);
      Pointer p = new Pointer(getAddress(bb));
      _GoString_.ByValue value = new _GoString_.ByValue();

      value.n = new NativeSize(bytes.length);
      value.p = p;
      return value;
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }
  
  public static GoSlice.ByValue JavaStringArrayToGoStringSlice(String[] strings) {
    _GoString_.ByValue[] goStrings = new _GoString_.ByValue[strings.length];
    for (int i = 0; i < strings.length; i++) {
      goStrings[i] = JavaStringToGo(strings[i]);
    }

    Memory arr = new Memory(strings.length * Native.getNativeSize(_GoString_.class));
    for (int i = 0; i < strings.length; i++) {
      System.out.println(Native.getNativeSize(_GoString_.class));
      byte[] bytes = goStrings[0].getPointer().getByteArray(0, Native.getNativeSize(_GoString_.class));
      arr.write(i*Native.getNativeSize(_GoString_.class), bytes, 0, bytes.length);
    }
    GoSlice.ByValue slice = new GoSlice.ByValue();
    slice.data = arr;
    slice.len = strings.length;
    slice.cap = strings.length;

    return slice;
  }

Everything compiles, but when i try to access the slice elements on the Go side, i get a seg fault:一切都可以编译,但是当我尝试访问 Go 端的切片元素时,出现了段错误:

unexpected fault address 0xb01dfacedebac1e
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x1 addr=0xb01dfacedebac1e pc=0x10d7d3d6f]

goroutine 17 [running, locked to thread]:

You're losing track (and control) of the memory allocation of the actual Strings.您正在失去对实际字符串的内存分配的跟踪(和控制)。

Your mapping of _GoString_ only includes allocation of a Pointer (4 or 8 bytes) and a NativeSize (a 4- or 8-byte size_t ).您对_GoString_映射仅包括指针(4 或 8 字节)和NativeSize (4 或 8 字节size_t )的分配。 This mapping assumes the Pointer remains valid:此映射假定Pointer保持有效:

public class _GoString_ extends Structure {
    /** C type : const char* */
    public Pointer p;
    public NativeSize n;
    // constructors, etc.
}

However, when you assign the value to p you only keep track of the pointer's address and not the actual memory allocation (I've added comments to your code):但是,当您将值分配给p您只会跟踪指针的地址,而不是实际的内存分配(我已在代码中添加了注释):

  public static _GoString_.ByValue JavaStringToGo(String jstr) {
    try {
      byte[] bytes = jstr.getBytes("utf-8");
      // 
      // Here you allocate memory for the bytes
      //
      ByteBuffer bb = ByteBuffer.allocateDirect(bytes.length);
      bb.put(bytes);
      // 
      // Here you only keep track of the pointer to the bytes
      //
      Pointer p = new Pointer(getAddress(bb));
      // 
      // You never reference bb again, it is no longer reachable
      // and its allocation can be reclaimed by the system
      //
      _GoString_.ByValue value = new _GoString_.ByValue();

      value.n = new NativeSize(bytes.length);
      value.p = p;
      return value;
    } catch (UnsupportedEncodingException e) {
      throw new RuntimeException(e);
    }
  }

As a direct bytebuffer, the memory location and (de)allocation mechanism are different than normal objects and GC, but the fundamental principle applies that once you've lost reference to the Java object (the ByteBuffer ) you have no control over when that native memory will be freed.作为直接字节缓冲区,内存位置和(解除)分配机制与普通对象和 GC 不同,但基本原则适用,一旦您失去对 Java 对象( ByteBuffer )的引用,您将无法控制该本地对象何时内存将被释放。 (When bb is GC'd, its internal fields include the reference that will trigger the deallocation when it is processed.) (当bb被 GC 处理时,其内部字段包括在处理时将触发释放的引用。)

One possible solution is to add a private field to your _GoString_ class that maintains a strong reference to the ByteBuffer and prevents the system from reclaiming its memory (possibly adding a ByteBuffer constrcutor).一种可能的解决方案是向您的_GoString_类添加一个private字段,该字段维护对ByteBuffer的强引用并防止系统回收其内存(可能添加一个ByteBuffer构造器)。

Another solution is to use JNA's Memory class for the Strings and directly store that Memory object (which extends Pointer ) to the p field.另一种解决方案是将 JNA 的Memory类用于字符串,并将该Memory对象(扩展Pointer )直接存储到p字段。 I'm not sure why you have chosen direct byte buffers for this application, so this may not apply to your use case but it would certainly simplify your code.我不确定您为什么为此应用程序选择了直接字节缓冲区,因此这可能不适用于您的用例,但它肯定会简化您的代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM