JNA ¿Cómo llenar un campo de puntero a estructura dentro de una estructura para PASAR a la biblioteca nativa?

El problema que necesita resolver (y la fuente de los Illegal memory accesserrores) es que el código del lado del C aceptar la matriz está esperando una Pointera un bloque contiguo de memoria. En C, solo necesita la dirección de memoria del primer elemento, más el desplazamiento de tamaño; para acceder a la matriz [1] encontrará la memoria de la matriz [0] y se compensará con el tamaño de la estructura.

En su caso, ha asignado memoria no contigua para cada estructura en este bloque:

// Application builds the attachments
final List<VkAttachmentDescription> attachments = ...

Cada uno VkAttachmentDescriptionse asigna a su propia memoria, e intentar leer la memoria al final de la primera estructura causa el error. Si no tiene control sobre qué memoria se usa cuando VkAttachmentDescriptionse instancian estos objetos, terminará duplicando sus requisitos de memoria y tendrá que copiar la memoria nativa desde el bloque no contiguo al bloque contiguo.

Editado para agregar: como se señaló en su otra respuesta, si solo trabajó con la VkAttachmentDescriptionEstructura en el lado de Java y no la pasó a una función en C, es posible que la memoria nativa no se haya escrito. Las siguientes soluciones se basan en Pointer.get*()métodos leídos directamente desde la memoria C, por lo que requerirían una write()llamada en algún momento.

Suponiendo que no tiene otra opción que comenzar con a List<VkAttachmentDescription>, lo primero que debe hacer es asignar la memoria contigua que C necesita. Consigamos los tamaños de bytes que necesitamos:

int size = attachments.size();
int bytes = attachments.get(0).size();

Necesitamos asignar size * bytesde memoria.

Tiene dos opciones aquí: asignar directamente la memoria usando un Memoryobjeto (una subclase de Pointer) o usar Structure.toArray. Para la asignación directa:

Memory mem = new Memory(size * bytes);

Podemos usar directamente memcomo a Pointersi definimos la referencia de esta manera:

public class VkRenderPassCreateInfo extends Structure {
    public int attachmentCount;
    public Pointer pAttachments;
}

Entonces es un simple:

info.pAttachments = mem;

Ahora todo lo que queda es copiar los bytes de la memoria no contigua en su memoria asignada. Podemos hacerlo byte a byte (es más fácil ver lo que sucede en el nivel de byte en el lado C):

for (int n = 0; n < size; ++n) {
    Pointer p = attachments.get(n).getPointer();
    for (int b = 0; b < bytes; ++b) {
        mem.setByte(n * bytes + b, p.getByte(b));
    }
}

O podemos hacerlo estructura por estructura:

for (int n = 0; n < size; ++n) {
    byte[] attachment = attachments.get(n).getPointer().getByteArray(0, bytes);
    mem.write(n * bytes, attachment, 0, bytes);
}

(Compensación de rendimiento: sobrecarga de instanciación de matriz frente a llamadas Java <--> C).

Ahora que el búfer está escrito, puede enviarlo a C, donde espera la matriz de estructuras, y no sabrá la diferencia ... ¡los bytes son bytes!

Editado para agregar: creo que es posible cambiar el respaldo de la memoria nativa usando useMemory()y luego escribir directamente en la nueva ubicación (contigua). Este código no ha sido probado pero sospecho que en realidad podría funcionar:

for (int n = 0; n < size; ++n) {
    attachments.get(n).useMemory(mem, n * bytes);
    attachments.get(n).write();
}

Personalmente, dado que solo estamos haciendo una copia de algo que ya existe, preferiría esta Memoryasignación basada en. Sin embargo ... algunos codificadores son masoquistas.

Si desea ser un poco más "seguro de tipo", puede usar la ByReferencedeclaración de clase dentro de la estructura y crear la matriz Estructura usando toArray(). Ha incluido en su código una forma de crear la matriz utilizando el tipo ByReference. Eso funciona, o también puede crearlo con el tipo (valor predeterminado de ByValue) y luego extraer el puntero al primer elemento más tarde para crear el tipo ByReference al asignarlo al campo de estructura:

VkAttachmentDescription[] array = 
    (VkAttachmentDescription[]) new VkAttachmentDescription().toArray(attachments.size());

Entonces puedes configurarlo así:

info.pAttachments = new VkAttachmentDescription.ByReference(array[0].getPointer());

En este caso, es un poco más complejo copiar los valores de la Lista (de estructuras respaldadas por bloques de memoria asignados individualmente) a la matriz (de memoria contigua) porque el mapeo de memoria se tipea más estrictamente, pero sigue el mismo patrón que para el Memorymapeo ¡Una forma, que has descubierto, es copiar manualmente sobre cada elemento de la Estructura! (Ugh.) Otra forma de salvarlo de algunos errores de copiar / pegar es usar Reflection (lo que hace JNA debajo del capó). Eso también es mucho trabajo y duplica lo que hace JNA, por lo que es feo y propenso a errores. Sin embargo, todavía es posible copiar los bytes nativos sin procesar del bloque de memoria no contiguo al bloque contiguo. (En cuyo caso ... ¿por qué no simplemente ir directamente Memorysino que se muestra mi sesgo?Memory ejemplo, así:

for (int n = 0; n < size; ++n) {
    Pointer p = attachments.get(n).getPointer();
    Pointer q = array[n].getPointer();
    for (int b = 0; b < bytes; ++b) {
        q.setByte(b, p.getByte(b));
    }
}

O puede leer los bytes en fragmentos como este:

for (int n = 0; n < size; ++n) {
    byte[] attachment = attachments.get(n).getPointer().getByteArray(0, bytes);
    array[n].getPointer().write(0, attachment, 0, bytes);
}

Tenga en cuenta que no he probado este código; escribe en el lado nativo, pero no en la estructura de Java, así que creo que funcionará como está, pero es posible que necesite una array[n].read()llamada al final del ciclo anterior para leer de C a Java en caso de que haya una copia incorporada de Java a C de lo que no estoy al tanto.

En respuesta a su "campo de estructura principal tiene que ser ByReference": Como se muestra arriba, un Pointermapeo funciona y permite un poco más de flexibilidad a costa de "seguridad de tipo" y tal vez (o no) "legibilidad". No necesita usarlo en ByReferenceotro lugar, como he mostrado con el lugar toArray()donde solo lo necesita para el campo Estructura (que podría definir como Pointery eliminar por completo la necesidad de ByReference... pero si lo está haciendo, ¿por qué no? ¿solo copiar al Memorybúfer? ¡Estoy golpeando a un caballo muerto aquí!).

Finalmente, la solución ideal si sabe cuántos elementos tendrá eventualmente (o un límite superior en ese número), sería crear una instancia de la matriz usando memoria contigua desde el principio. Luego, en lugar de crear una nueva instancia de VkAttachmentDescriptionusted, podría obtener una preexistente de la matriz. Está bien si se sobreasigna y no los usa todos siempre y cuando los use contiguamente desde el principio. Todo lo que está pasando a C es el número de estructuras y la dirección de la primera, no le importa si tiene bytes adicionales.

Respuesta 1

¿Es posible tomar un proyecto en el que he estado trabajando, importarlo en Spring STS y aplicar todos los beneficios que STS proporciona para el desarrollo fácil de Spring al proyecto? Por ejemplo, me gustaría ...

Tengo un algoritmo gráfico que genera resultados intermedios asociados a diferentes nodos. Actualmente, he resuelto esto usando un ConcurrentHashMap <Node, List <Result> (Estoy ejecutando ...

Intento poner una barra de desplazamiento en un marco, pero no funciona, no veo la barra de desplazamiento. Este es mi código: Popup noticePopup = new Popup ("Notice", 1500, 900); JPanel noticePanel = new JPanel (); ...

Estoy tratando de pasar datos entre 2 clases de acción. Actualmente estoy haciendo esto en mi primera clase de acción doExecute () {request.setAttribute ("Order_ID", 2); // código para buscar ...