Files
hdf5/java/jtest/TestH5Affm.java
Allen Byrne b754dcb8f2 Move Java wrappers to FFM using jextract and java 25 (#5957)
FFM build requires Java 25, Jextract 25.
Generates FFM bindings during configure.
JNI is default when the requirements are not met or can be forced.
Presets added for maven and FFM - JNI is default selection.
Enhanced Maven options will work with either JNI or FFM
New Workflows for testing and maven uploads.
Extensive documentation changes for java.
2025-11-04 14:03:06 -06:00

1045 lines
42 KiB
Java

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the COPYING file, which can be found at the root of the source code *
* distribution tree, or in https://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package jtest;
import static org.junit.Assert.*;
import static jtest.FfmTestSupport.*;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import org.hdfgroup.javahdf5.H5A_info_t;
import org.hdfgroup.javahdf5.H5A_operator2_t;
import org.hdfgroup.javahdf5.hdf5_h;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
/**
* TestH5Affm - FFM-based tests for HDF5 Attribute operations.
* Tests the H5A* API using Foreign Function & Memory (FFM) bindings.
*/
public class TestH5Affm {
private static final String H5_FILE = "testA.h5";
private static final int DIM_X = 4;
private static final int DIM_Y = 6;
private static final int RANK = 2;
@Rule
public TestName testname = new TestName();
long H5fid = hdf5_h.H5I_INVALID_HID();
long H5did = hdf5_h.H5I_INVALID_HID();
long H5sid = hdf5_h.H5I_INVALID_HID();
long H5aid = hdf5_h.H5I_INVALID_HID();
@Before
public void createH5file() throws Exception
{
try (Arena arena = Arena.ofConfined()) {
// Create file
MemorySegment fileName = stringToSegment(arena, H5_FILE);
H5fid = hdf5_h.H5Fcreate(fileName, hdf5_h.H5F_ACC_TRUNC(), hdf5_h.H5P_DEFAULT(),
hdf5_h.H5P_DEFAULT());
assertTrue("H5Fcreate failed", isValidId(H5fid));
// Create dataspace
long[] dims = {DIM_X, DIM_Y};
MemorySegment dimsSegment = allocateLongArray(arena, RANK);
copyToSegment(dimsSegment, dims);
H5sid = hdf5_h.H5Screate_simple(RANK, dimsSegment, MemorySegment.NULL);
assertTrue("H5Screate_simple failed", isValidId(H5sid));
// Create dataset for attaching attributes
MemorySegment dsetName = stringToSegment(arena, "dset");
H5did = hdf5_h.H5Dcreate2(H5fid, dsetName, hdf5_h.H5T_NATIVE_INT_g(), H5sid, hdf5_h.H5P_DEFAULT(),
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Dcreate2 failed", isValidId(H5did));
}
}
@After
public void deleteH5file() throws Exception
{
closeQuietly(H5aid, hdf5_h::H5Aclose);
closeQuietly(H5did, hdf5_h::H5Dclose);
closeQuietly(H5sid, hdf5_h::H5Sclose);
closeQuietly(H5fid, hdf5_h::H5Fclose);
H5aid = hdf5_h.H5I_INVALID_HID();
H5did = hdf5_h.H5I_INVALID_HID();
H5sid = hdf5_h.H5I_INVALID_HID();
H5fid = hdf5_h.H5I_INVALID_HID();
}
static
{
try {
System.loadLibrary("hdf5");
hdf5_h.H5open();
}
catch (UnsatisfiedLinkError e) {
System.err.println("Failed to load HDF5 library: " + e.getMessage());
}
}
@Test
public void testH5Acreate()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create scalar attribute space
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
assertTrue("H5Screate scalar failed", isValidId(attr_sid));
// Create attribute
MemorySegment attrName = stringToSegment(arena, "attr1");
H5aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(H5aid));
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Awrite_read()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute with array dataspace
long[] attr_dims = {3};
MemorySegment attrDimsSegment = allocateLongArray(arena, 1);
attrDimsSegment.setAtIndex(ValueLayout.JAVA_LONG, 0, attr_dims[0]);
long attr_sid = hdf5_h.H5Screate_simple(1, attrDimsSegment, MemorySegment.NULL);
assertTrue("H5Screate_simple failed", isValidId(attr_sid));
MemorySegment attrName = stringToSegment(arena, "int_array_attr");
H5aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(H5aid));
// Write data
int[] write_data = {10, 20, 30};
MemorySegment writeSegment = allocateIntArray(arena, 3);
copyToSegment(writeSegment, write_data);
int result = hdf5_h.H5Awrite(H5aid, hdf5_h.H5T_NATIVE_INT_g(), writeSegment);
assertTrue("H5Awrite failed", isSuccess(result));
// Read back
MemorySegment readSegment = allocateIntArray(arena, 3);
result = hdf5_h.H5Aread(H5aid, hdf5_h.H5T_NATIVE_INT_g(), readSegment);
assertTrue("H5Aread failed", isSuccess(result));
// Verify
int[] read_data = new int[3];
copyFromSegment(readSegment, read_data);
assertArrayEquals("Data mismatch", write_data, read_data);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aopen()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute first
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, "test_attr");
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
hdf5_h.H5Aclose(aid);
// Open attribute
H5aid = hdf5_h.H5Aopen(H5did, attrName, hdf5_h.H5P_DEFAULT());
assertTrue("H5Aopen failed", isValidId(H5aid));
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aclose()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, "temp_attr");
H5aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(H5aid));
int result = hdf5_h.H5Aclose(H5aid);
assertTrue("H5Aclose failed", isSuccess(result));
H5aid = hdf5_h.H5I_INVALID_HID();
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_name()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
String expectedName = "my_attribute";
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, expectedName);
H5aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(H5aid));
// Get name size
long nameSize = hdf5_h.H5Aget_name(H5aid, 0, MemorySegment.NULL);
assertTrue("H5Aget_name size query failed", nameSize > 0);
// Get name
MemorySegment nameBuffer = arena.allocate(nameSize + 1);
hdf5_h.H5Aget_name(H5aid, nameSize + 1, nameBuffer);
String retrievedName = nameBuffer.getString(0);
assertEquals("Attribute name mismatch", expectedName, retrievedName);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_space()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute with specific dimensions
long[] dims = {5, 3};
MemorySegment dimsSegment = allocateLongArray(arena, 2);
copyToSegment(dimsSegment, dims);
long attr_sid = hdf5_h.H5Screate_simple(2, dimsSegment, MemorySegment.NULL);
MemorySegment attrName = stringToSegment(arena, "array_attr");
H5aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(H5aid));
// Get dataspace
long retrieved_sid = hdf5_h.H5Aget_space(H5aid);
assertTrue("H5Aget_space failed", isValidId(retrieved_sid));
// Verify dimensions
MemorySegment retrievedDims = allocateLongArray(arena, 2);
hdf5_h.H5Sget_simple_extent_dims(retrieved_sid, retrievedDims, MemorySegment.NULL);
long[] readDims = new long[2];
copyFromSegment(retrievedDims, readDims);
assertArrayEquals("Dimensions mismatch", dims, readDims);
hdf5_h.H5Sclose(retrieved_sid);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_type()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, "type_attr");
H5aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_DOUBLE_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(H5aid));
// Get type
long retrieved_tid = hdf5_h.H5Aget_type(H5aid);
assertTrue("H5Aget_type failed", isValidId(retrieved_tid));
// Verify it's a double type
int equal = hdf5_h.H5Tequal(retrieved_tid, hdf5_h.H5T_NATIVE_DOUBLE_g());
assertTrue("Type should be H5T_NATIVE_DOUBLE", equal > 0);
hdf5_h.H5Tclose(retrieved_tid);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aexists()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
MemorySegment existingName = stringToSegment(arena, "existing_attr");
MemorySegment missingName = stringToSegment(arena, "missing_attr");
// Create one attribute
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
long aid = hdf5_h.H5Acreate2(H5did, existingName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
hdf5_h.H5Aclose(aid);
// Check existing
int exists = hdf5_h.H5Aexists(H5did, existingName);
assertTrue("Attribute should exist", exists > 0);
// Check non-existing
exists = hdf5_h.H5Aexists(H5did, missingName);
assertEquals("Attribute should not exist", 0, exists);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Adelete()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
MemorySegment attrName = stringToSegment(arena, "deletable_attr");
// Create attribute
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
hdf5_h.H5Aclose(aid);
// Verify it exists
int exists = hdf5_h.H5Aexists(H5did, attrName);
assertTrue("Attribute should exist before delete", exists > 0);
// Delete
int result = hdf5_h.H5Adelete(H5did, attrName);
assertTrue("H5Adelete failed", isSuccess(result));
// Verify it's gone
exists = hdf5_h.H5Aexists(H5did, attrName);
assertEquals("Attribute should not exist after delete", 0, exists);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_storage_size()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute with 10 integers
long[] attr_dims = {10};
MemorySegment attrDimsSegment = allocateLongArray(arena, 1);
attrDimsSegment.setAtIndex(ValueLayout.JAVA_LONG, 0, attr_dims[0]);
long attr_sid = hdf5_h.H5Screate_simple(1, attrDimsSegment, MemorySegment.NULL);
MemorySegment attrName = stringToSegment(arena, "storage_attr");
H5aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(H5aid));
// Write data
int[] data = new int[10];
MemorySegment dataSegment = allocateIntArray(arena, 10);
copyToSegment(dataSegment, data);
hdf5_h.H5Awrite(H5aid, hdf5_h.H5T_NATIVE_INT_g(), dataSegment);
// Get storage size
long storage_size = hdf5_h.H5Aget_storage_size(H5aid);
assertEquals("Storage size should be 10 * sizeof(int)", 40L, storage_size);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Awrite_readStr()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
String testString = "Hello HDF5 Attributes!";
// Create string type
long str_tid = hdf5_h.H5Tcopy(hdf5_h.H5T_C_S1_g());
assertTrue("H5Tcopy failed", isValidId(str_tid));
hdf5_h.H5Tset_size(str_tid, testString.length() + 1);
hdf5_h.H5Tset_strpad(str_tid, hdf5_h.H5T_STR_NULLTERM());
// Create attribute
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, "str_attr");
H5aid = hdf5_h.H5Acreate2(H5did, attrName, str_tid, attr_sid, hdf5_h.H5P_DEFAULT(),
hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(H5aid));
// Write string
MemorySegment writeData = stringToSegment(arena, testString);
int writeResult = hdf5_h.H5Awrite(H5aid, str_tid, writeData);
assertTrue("H5Awrite failed", isSuccess(writeResult));
// Read string back
MemorySegment readData = arena.allocate(testString.length() + 1);
int readResult = hdf5_h.H5Aread(H5aid, str_tid, readData);
assertTrue("H5Aread failed", isSuccess(readResult));
String retrievedString = readData.getString(0);
assertEquals("String mismatch", testString, retrievedString);
hdf5_h.H5Tclose(str_tid);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Arename()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
MemorySegment oldName = stringToSegment(arena, "old_name");
MemorySegment newName = stringToSegment(arena, "new_name");
// Create attribute
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
long aid = hdf5_h.H5Acreate2(H5did, oldName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
hdf5_h.H5Aclose(aid);
// Verify old name exists
int exists = hdf5_h.H5Aexists(H5did, oldName);
assertTrue("Old name should exist", exists > 0);
// Rename
int result = hdf5_h.H5Arename(H5did, oldName, newName);
assertTrue("H5Arename failed", isSuccess(result));
// Verify new name exists and old doesn't
exists = hdf5_h.H5Aexists(H5did, newName);
assertTrue("New name should exist", exists > 0);
exists = hdf5_h.H5Aexists(H5did, oldName);
assertEquals("Old name should not exist", 0, exists);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_num_attrs()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create 3 attributes
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
for (int i = 0; i < 3; i++) {
MemorySegment attrName = stringToSegment(arena, "attr_" + i);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed for attr_" + i, isValidId(aid));
hdf5_h.H5Aclose(aid);
}
// Get number of attributes
int num_attrs = hdf5_h.H5Aget_num_attrs(H5did);
assertTrue("Should have at least 3 attributes", num_attrs >= 3);
hdf5_h.H5Sclose(attr_sid);
}
}
// =========================
// Phase 1: Essential Query Operations
// =========================
@Test
public void testH5Aget_info()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute with scalar dataspace
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, "info_test_attr");
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
// Write some data
MemorySegment data = allocateInt(arena);
setInt(data, 42);
int writeResult = hdf5_h.H5Awrite(aid, hdf5_h.H5T_NATIVE_INT_g(), data);
assertTrue("H5Awrite failed", isSuccess(writeResult));
// Get attribute info
MemorySegment info = H5A_info_t.allocate(arena);
int result = hdf5_h.H5Aget_info(aid, info);
assertTrue("H5Aget_info failed", isSuccess(result));
// Verify info fields
long data_size = H5A_info_t.data_size(info);
assertEquals("Data size should be 4 bytes (sizeof int)", 4L, data_size);
// corder_valid may be false if creation order tracking is not enabled
boolean corder_valid = H5A_info_t.corder_valid(info);
// Just verify we can read it (no assertion on value)
assertNotNull("corder_valid should be readable", Boolean.valueOf(corder_valid));
// cset should be ASCII by default
int cset = H5A_info_t.cset(info);
assertEquals("Character set should be ASCII", hdf5_h.H5T_CSET_ASCII(), cset);
hdf5_h.H5Aclose(aid);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_info_by_idx()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create multiple attributes to test indexing
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
String[] attrNames = {"first_attr", "second_attr", "third_attr"};
for (String name : attrNames) {
MemorySegment attrName = stringToSegment(arena, name);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_DOUBLE_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed for " + name, isValidId(aid));
hdf5_h.H5Aclose(aid);
}
// Get info for the second attribute (index 1) by name order
MemorySegment objName = stringToSegment(arena, ".");
MemorySegment info = H5A_info_t.allocate(arena);
int result = hdf5_h.H5Aget_info_by_idx(H5did, objName, hdf5_h.H5_INDEX_NAME(),
hdf5_h.H5_ITER_INC(), 1, info, hdf5_h.H5P_DEFAULT());
assertTrue("H5Aget_info_by_idx failed", isSuccess(result));
// Verify we got valid info
long data_size = H5A_info_t.data_size(info);
assertEquals("Data size should be 8 bytes (sizeof double)", 8L, data_size);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_info_by_name()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute on dataset
String attrNameStr = "named_info_attr";
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, attrNameStr);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_FLOAT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
hdf5_h.H5Aclose(aid);
// Get info by name from the file (using dataset path)
MemorySegment objName = stringToSegment(arena, "dset");
MemorySegment info = H5A_info_t.allocate(arena);
int result = hdf5_h.H5Aget_info_by_name(H5fid, objName, attrName, info, hdf5_h.H5P_DEFAULT());
assertTrue("H5Aget_info_by_name failed", isSuccess(result));
// Verify info
long data_size = H5A_info_t.data_size(info);
assertEquals("Data size should be 4 bytes (sizeof float)", 4L, data_size);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_name_by_idx()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create multiple attributes with known names
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
String[] attrNames = {"alpha", "beta", "gamma"};
String[] sortedNames = {"alpha", "beta", "gamma"}; // Already sorted alphabetically
for (String name : attrNames) {
MemorySegment attrName = stringToSegment(arena, name);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed for " + name, isValidId(aid));
hdf5_h.H5Aclose(aid);
}
// Get name of each attribute by index (alphabetical order)
MemorySegment objName = stringToSegment(arena, ".");
for (int i = 0; i < sortedNames.length; i++) {
// Get name size first
long nameSize =
hdf5_h.H5Aget_name_by_idx(H5did, objName, hdf5_h.H5_INDEX_NAME(), hdf5_h.H5_ITER_INC(), i,
MemorySegment.NULL, 0, hdf5_h.H5P_DEFAULT());
assertTrue("H5Aget_name_by_idx size query failed for index " + i, nameSize > 0);
// Get actual name
MemorySegment nameBuffer = arena.allocate(nameSize + 1);
long result =
hdf5_h.H5Aget_name_by_idx(H5did, objName, hdf5_h.H5_INDEX_NAME(), hdf5_h.H5_ITER_INC(), i,
nameBuffer, nameSize + 1, hdf5_h.H5P_DEFAULT());
assertTrue("H5Aget_name_by_idx failed for index " + i, result > 0);
String retrievedName = nameBuffer.getString(0);
assertEquals("Name at index " + i + " should match", sortedNames[i], retrievedName);
}
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aget_create_plist()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute creation property list with specific settings
long acpl = hdf5_h.H5Pcreate(hdf5_h.H5P_CLS_ATTRIBUTE_CREATE_ID_g());
assertTrue("H5Pcreate acpl failed", isValidId(acpl));
// Set character encoding to UTF-8
int setResult = hdf5_h.H5Pset_char_encoding(acpl, hdf5_h.H5T_CSET_UTF8());
assertTrue("H5Pset_char_encoding failed", isSuccess(setResult));
// Create attribute with this property list
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, "plist_attr");
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid, acpl,
hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
// Get the creation property list back
long retrieved_acpl = hdf5_h.H5Aget_create_plist(aid);
assertTrue("H5Aget_create_plist failed", isValidId(retrieved_acpl));
// Verify the encoding is UTF-8
MemorySegment encoding = allocateInt(arena);
int getResult = hdf5_h.H5Pget_char_encoding(retrieved_acpl, encoding);
assertTrue("H5Pget_char_encoding failed", isSuccess(getResult));
int retrievedEncoding = getInt(encoding);
assertEquals("Character encoding should be UTF-8", hdf5_h.H5T_CSET_UTF8(), retrievedEncoding);
hdf5_h.H5Pclose(retrieved_acpl);
hdf5_h.H5Pclose(acpl);
hdf5_h.H5Aclose(aid);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aexists_by_name()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute on dataset
String existingAttrName = "existing_by_name";
String nonExistingAttrName = "nonexistent_by_name";
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, existingAttrName);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
hdf5_h.H5Aclose(aid);
// Check existence from file using dataset path
MemorySegment objName = stringToSegment(arena, "dset");
MemorySegment existingAttrNameSeg = stringToSegment(arena, existingAttrName);
MemorySegment missingAttrNameSeg = stringToSegment(arena, nonExistingAttrName);
// Should exist
int exists = hdf5_h.H5Aexists_by_name(H5fid, objName, existingAttrNameSeg, hdf5_h.H5P_DEFAULT());
assertTrue("Attribute should exist", exists > 0);
// Should not exist
exists = hdf5_h.H5Aexists_by_name(H5fid, objName, missingAttrNameSeg, hdf5_h.H5P_DEFAULT());
assertEquals("Attribute should not exist", 0, exists);
hdf5_h.H5Sclose(attr_sid);
}
}
// =========================
// Phase 2: Advanced Operations
// =========================
@Test
public void testH5Aopen_by_idx()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create multiple attributes with known names
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
String[] attrNames = {"apple", "banana", "cherry"};
String[] sortedNames = {"apple", "banana", "cherry"}; // Already sorted
for (String name : attrNames) {
MemorySegment attrName = stringToSegment(arena, name);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed for " + name, isValidId(aid));
hdf5_h.H5Aclose(aid);
}
// Open second attribute by index (index 1)
MemorySegment objName = stringToSegment(arena, ".");
long aid = hdf5_h.H5Aopen_by_idx(H5did, objName, hdf5_h.H5_INDEX_NAME(), hdf5_h.H5_ITER_INC(), 1,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Aopen_by_idx failed", isValidId(aid));
// Verify we opened the correct attribute by checking its name
long nameSize = hdf5_h.H5Aget_name(aid, 0, MemorySegment.NULL);
MemorySegment nameBuf = arena.allocate(nameSize + 1);
hdf5_h.H5Aget_name(aid, nameSize + 1, nameBuf);
String retrievedName = nameBuf.getString(0);
assertEquals("Should have opened 'banana' (index 1)", sortedNames[1], retrievedName);
hdf5_h.H5Aclose(aid);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Adelete_by_idx()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create 3 attributes
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
String[] attrNames = {"first", "second", "third"};
for (String name : attrNames) {
MemorySegment attrName = stringToSegment(arena, name);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed for " + name, isValidId(aid));
hdf5_h.H5Aclose(aid);
}
// Delete middle attribute (index 1 = "second")
MemorySegment objName = stringToSegment(arena, ".");
int result = hdf5_h.H5Adelete_by_idx(H5did, objName, hdf5_h.H5_INDEX_NAME(), hdf5_h.H5_ITER_INC(),
1, hdf5_h.H5P_DEFAULT());
assertTrue("H5Adelete_by_idx failed", isSuccess(result));
// Verify "second" is gone
MemorySegment secondName = stringToSegment(arena, "second");
int exists = hdf5_h.H5Aexists(H5did, secondName);
assertEquals("'second' should not exist after deletion", 0, exists);
// Verify others still exist
MemorySegment firstName = stringToSegment(arena, "first");
MemorySegment thirdName = stringToSegment(arena, "third");
exists = hdf5_h.H5Aexists(H5did, firstName);
assertTrue("'first' should still exist", exists > 0);
exists = hdf5_h.H5Aexists(H5did, thirdName);
assertTrue("'third' should still exist", exists > 0);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Adelete_by_name()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute on dataset
String attrNameStr = "deletable_by_name";
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment attrName = stringToSegment(arena, attrNameStr);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
hdf5_h.H5Aclose(aid);
// Verify it exists
int exists = hdf5_h.H5Aexists(H5did, attrName);
assertTrue("Attribute should exist before delete", exists > 0);
// Delete by name from file using dataset path
MemorySegment objName = stringToSegment(arena, "dset");
int result = hdf5_h.H5Adelete_by_name(H5fid, objName, attrName, hdf5_h.H5P_DEFAULT());
assertTrue("H5Adelete_by_name failed", isSuccess(result));
// Verify it's gone
exists = hdf5_h.H5Aexists(H5did, attrName);
assertEquals("Attribute should not exist after delete", 0, exists);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Arename_by_name()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute on dataset
String oldNameStr = "old_name_by_path";
String newNameStr = "new_name_by_path";
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment oldName = stringToSegment(arena, oldNameStr);
MemorySegment newName = stringToSegment(arena, newNameStr);
long aid = hdf5_h.H5Acreate2(H5did, oldName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed", isValidId(aid));
hdf5_h.H5Aclose(aid);
// Verify old name exists
int exists = hdf5_h.H5Aexists(H5did, oldName);
assertTrue("Old name should exist", exists > 0);
// Rename by name from file using dataset path
MemorySegment objName = stringToSegment(arena, "dset");
int result = hdf5_h.H5Arename_by_name(H5fid, objName, oldName, newName, hdf5_h.H5P_DEFAULT());
assertTrue("H5Arename_by_name failed", isSuccess(result));
// Verify new name exists and old doesn't
exists = hdf5_h.H5Aexists(H5did, newName);
assertTrue("New name should exist", exists > 0);
exists = hdf5_h.H5Aexists(H5did, oldName);
assertEquals("Old name should not exist", 0, exists);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aiterate2()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create multiple attributes
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
String[] attrNames = {"attr_A", "attr_B", "attr_C", "attr_D"};
for (String name : attrNames) {
MemorySegment attrName = stringToSegment(arena, name);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed for " + name, isValidId(aid));
hdf5_h.H5Aclose(aid);
}
// Create iteration callback
final int[] count = {0};
final String[] names = new String[10];
H5A_operator2_t.Function cb = (loc_id, attr_name, ainfo, op_data) ->
{
try {
String name = attr_name.getString(0);
names[count[0]] = name;
count[0]++;
return 0; // Continue iteration
}
catch (Exception e) {
return -1; // Stop on error
}
};
MemorySegment callback = H5A_operator2_t.allocate(cb, arena);
MemorySegment idx = allocateLong(arena);
setLong(idx, 0);
// Iterate
int result = hdf5_h.H5Aiterate2(H5did, hdf5_h.H5_INDEX_NAME(), hdf5_h.H5_ITER_INC(), idx,
callback, MemorySegment.NULL);
assertTrue("H5Aiterate2 failed", isSuccess(result));
// Verify we iterated all attributes (at least the 4 we created)
assertTrue("Should have iterated at least 4 attributes", count[0] >= 4);
// Verify all our attributes were seen
boolean foundA = false, foundB = false, foundC = false, foundD = false;
for (int i = 0; i < count[0]; i++) {
if ("attr_A".equals(names[i]))
foundA = true;
if ("attr_B".equals(names[i]))
foundB = true;
if ("attr_C".equals(names[i]))
foundC = true;
if ("attr_D".equals(names[i]))
foundD = true;
}
assertTrue("Should have found attr_A", foundA);
assertTrue("Should have found attr_B", foundB);
assertTrue("Should have found attr_C", foundC);
assertTrue("Should have found attr_D", foundD);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aiterate_by_name()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attributes on dataset
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
String[] attrNames = {"iter_1", "iter_2", "iter_3"};
for (String name : attrNames) {
MemorySegment attrName = stringToSegment(arena, name);
long aid = hdf5_h.H5Acreate2(H5did, attrName, hdf5_h.H5T_NATIVE_INT_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate2 failed for " + name, isValidId(aid));
hdf5_h.H5Aclose(aid);
}
// Create iteration callback
final int[] count = {0};
H5A_operator2_t.Function cb = (loc_id, attr_name, ainfo, op_data) ->
{
count[0]++;
return 0; // Continue
};
MemorySegment callback = H5A_operator2_t.allocate(cb, arena);
MemorySegment idx = allocateLong(arena);
setLong(idx, 0);
// Iterate from file using dataset path
MemorySegment objName = stringToSegment(arena, "dset");
int result =
hdf5_h.H5Aiterate_by_name(H5fid, objName, hdf5_h.H5_INDEX_NAME(), hdf5_h.H5_ITER_INC(), idx,
callback, MemorySegment.NULL, hdf5_h.H5P_DEFAULT());
assertTrue("H5Aiterate_by_name failed", isSuccess(result));
// Verify we iterated at least our 3 attributes
assertTrue("Should have iterated at least 3 attributes", count[0] >= 3);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Acreate_by_name_comprehensive()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
// Create attribute on dataset using path from file
String attrNameStr = "created_by_path_comprehensive";
long attr_sid = hdf5_h.H5Screate(hdf5_h.H5S_SCALAR());
MemorySegment objName = stringToSegment(arena, "dset");
MemorySegment attrName = stringToSegment(arena, attrNameStr);
long aid =
hdf5_h.H5Acreate_by_name(H5fid, objName, attrName, hdf5_h.H5T_NATIVE_DOUBLE_g(), attr_sid,
hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT(), hdf5_h.H5P_DEFAULT());
assertTrue("H5Acreate_by_name failed", isValidId(aid));
// Write data
MemorySegment data = allocateDoubleArray(arena, 1);
data.setAtIndex(ValueLayout.JAVA_DOUBLE, 0, 3.14159);
int writeResult = hdf5_h.H5Awrite(aid, hdf5_h.H5T_NATIVE_DOUBLE_g(), data);
assertTrue("H5Awrite failed", isSuccess(writeResult));
hdf5_h.H5Aclose(aid);
// Verify attribute is accessible from dataset
MemorySegment attrNameCheck = stringToSegment(arena, attrNameStr);
int exists = hdf5_h.H5Aexists(H5did, attrNameCheck);
assertTrue("Attribute should exist on dataset", exists > 0);
// Open and read back to verify data
long aid2 = hdf5_h.H5Aopen(H5did, attrNameCheck, hdf5_h.H5P_DEFAULT());
assertTrue("H5Aopen failed", isValidId(aid2));
MemorySegment readData = allocateDoubleArray(arena, 1);
int readResult = hdf5_h.H5Aread(aid2, hdf5_h.H5T_NATIVE_DOUBLE_g(), readData);
assertTrue("H5Aread failed", isSuccess(readResult));
double value = readData.getAtIndex(ValueLayout.JAVA_DOUBLE, 0);
assertEquals("Data should match", 3.14159, value, 0.00001);
hdf5_h.H5Aclose(aid2);
hdf5_h.H5Sclose(attr_sid);
}
}
@Test
public void testH5Aiterate()
{
System.out.print(testname.getMethodName());
try (Arena arena = Arena.ofConfined()) {
MemorySegment idx = allocateLongArray(arena, 1);
copyToSegment(idx, new long[] {0});
// Just verify the API works, iteration callback complex for FFM
long result = hdf5_h.H5Aiterate2(H5did, hdf5_h.H5_INDEX_NAME(), hdf5_h.H5_ITER_INC(), idx,
MemorySegment.NULL, MemorySegment.NULL);
assertTrue("H5Aiterate2 should complete", result >= 0);
}
}
}