Как скопировать родную память в DirectByteBuffer - PullRequest
5 голосов
/ 28 мая 2019

Я знаю один способ - использование memcpy на стороне C ++:

Метод C ++:

void CopyData(void* buffer, int size)
{
    memcpy(buffer, source, size);
}

Отображение JNR:

void CopyData(@Pinned @Out ByteBuffer byteBuffer, @Pinned @In int size);

Вызов Java:

ByteBuffer buffer = ByteBuffer.allocateDirect(size);
adapter.CopyData(buffer, size);

Но я бы хотел обработать случай, когда нативный код не копирует данные, а только возвращает указатель на память, которая должна быть скопирована:

C ++ методы:

void* GetData1()
{
    return source;
}

// or

struct Data
{
    void* data;
};

void* GetData2(Data* outData)
{
    outData->data = source;
}

Я знаю, как написать сопоставление JNR, чтобы иметь возможность копировать данные в HeapByteBuffer:

Pointer GetData1();

// or

void GetData2(@Pinned @Out Data outData);

final class Data extends Struct {

    public final Struct.Pointer data;

    public DecodeResult(Runtime runtime) {
        super(runtime);

        data = new Struct.Pointer();
    }
}

Вызов Java:

ByteBuffer buffer = ByteBuffer.allocate(size);
Pointer dataPtr = adapter.GetData1();
dataPtr.get(0, buffer.array(), 0, buffer.array().length);

// or

ByteBuffer buffer = ByteBuffer.allocate(size);
Data outData = new Data(runtime);
adapter.GetData2(outData);

Pointer dataPtr = outData.data.get();
dataPtr.get(0, buffer.array(), 0, buffer.array().length);

Но я не нашел способаскопировать память в DirectByteBuffer вместо HeapByteBuffer.Приведенный выше фрагмент кода не работает для DirectByteBuffer, поскольку buffer.array() для такого буфера равно нулю, так как оно поддерживается собственной областью памяти.

Пожалуйста, помогите.

1 Ответ

2 голосов
/ 04 июня 2019

Я нашел несколько способов выполнить копирование собственной памяти JNR в DirectByteBuffer. Они отличаются по эффективности. В настоящее время я использую следующий подход, я не знаю, является ли это лучшим или предназначенным авторами JNR:

ByteBuffer buffer = ByteBuffer.allocateDirect(size);
Pointer dataPtr = adapter.GetData1();
long destAddress = ((DirectBuffer)buffer).address();
Pointer destPtr = AsmRuntime.pointerValue(destAddress, runtime);

assert dataPtr.isDirect() && destPtr.isDirect();

dataPtr.transferTo(0, destPtr, 0, size);

или

ByteBuffer buffer = ByteBuffer.allocateDirect(size);
Data outData = new Data(runtime);
adapter.GetData2(outData);

Pointer dataPtr = outData.data.get();
long destAddress = ((DirectBuffer)buffer).address();
Pointer destPtr = AsmRuntime.pointerValue(destAddress, runtime);

assert dataPtr.isDirect() && destPtr.isDirect();

dataPtr.transferTo(0, destPtr, 0, size);

Важно, чтобы вышеприведенное утверждение было выполнено. Это гарантирует, что указатели являются jnr.ffi.provider.jffi.DirectMemoryIO экземплярами, а эффективный метод memcpy используется для копирования (проверьте реализацию DirectMemoryIO.transferTo()).

Альтернатива - обернуть DirectByteBuffer, используя следующий метод:

Pointer destPtr = Pointer.wrap(runtime, destAddress);

или

Pointer destPtr = Pointer.wrap(runtime, destAddress, size);

но нет:

Pointer destPtr = Pointer.wrap (время выполнения, буфер);

Первый и второй указатели поддерживаются DirectMemoryIO, но третий указатель поддерживается ByteBufferMemoryIO и включает медленное побайтное копирование.

Единственным недостатком является то, что экземпляр DirectMemoryIO довольно тяжелый. Он выделяет 32 байта в куче JVM, поэтому в случае большого количества вызовов JNR все экземпляры DirectMemoryIO занимают большую часть памяти.

...