Add Java bindings for H5Pmodify_filter_by_idx

Implements the two overloads specified in RFC-HDFG-2026-001
sec:modify-bindings -- one taking a String, one an int[] -- mirroring
the existing H5Pappend_filter overloads.

Both Java binding paths are covered, since the tree maintains two:

  - JNI (java/src-jni): two private native methods dispatched from the
    public overloads, plus the JNI implementations and declarations in
    h5pDCPLImp.c/.h, following the H5Pappend_filter_str/_raw pattern
    exactly.

  - Panama/FFM (java/hdf): the H5Z_params_t struct is built directly in
    an Arena and passed to the generated org.hdfgroup.javahdf5.hdf5_h
    downcall, as the append string form already does. Unlike the append
    case the cd_values form cannot delegate to H5Pmodify_filter, which
    addresses an entry by filter ID and resolves to the first match
    rather than by index, so it constructs the struct explicitly. The
    generated binding is produced by jextract from the public headers,
    so it picks up the new declaration automatically.

Adds testH5Pmodify_filter_by_idx_string and
testH5Pmodify_filter_by_idx_cdvalues to TestH5Z.java and updates the
JUnit golden file. Placement and the test count were derived from
@FixMethodOrder(MethodSorters.NAME_ASCENDING) rather than guessed, and
the file's surrounding format is unchanged.

Not compile-verified locally: this configuration has HDF5_BUILD_JAVA
OFF and the machine has a JRE but no JDK (no javac) and no jextract, so
nothing under java/ can be built here. The C library still builds clean
and the full C/Fortran suite is unchanged. What was checked statically:
the JNI symbol names against the mangling rule (underscores in the Java
method name become _1) and against the existing append pair; the
H5Z_params_t offsets (int type at 0, union at 8, cd_nelmts at 8,
cd_values at 16, 24 bytes total on 64-bit) and the enum values
(H5Z_PARAMS_CDVALUES 0, H5Z_PARAMS_STRING 1) against H5Zpublic.h; and
the H5Pget_filter call in the new test against the working call in
testH5Pappend_filter_cdvalues. Java CI should be treated as the real
gate.
This commit is contained in:
Scot Breitenfeld
2026-08-20 23:12:06 -05:00
parent 34b567a9c7
commit 39fa3e5538
6 changed files with 343 additions and 1 deletions
+105
View File
@@ -12495,6 +12495,111 @@ public class H5 implements java.io.Serializable {
return H5Pset_filter(plist_id, filter_id, flags, (long)values.length, values);
}
/**
* @ingroup JH5P
*
* H5Pmodify_filter_by_idx replaces the configuration of the filter at a pipeline index using a
* human-readable key=value parameter string. The entry keeps a stored configuration string.
*
* @param plist_id
* IN: Property list identifier.
* @param filter_idx
* IN: Zero-based index of the filter in the pipeline.
* @param flags
* IN: Bit vector specifying certain general properties of the filter.
* @param params
* IN: Parameter string in "key=value" format, or null for no parameters.
*
* @return a non-negative value if successful
*
* @exception HDF5LibraryException
* Error from the HDF5 Library.
**/
public static int H5Pmodify_filter_by_idx(long plist_id, int filter_idx, int flags, String params)
throws HDF5LibraryException
{
if (plist_id < 0)
throw new HDF5FunctionArgumentException("Negative property list identifier");
int retVal = -1;
/* H5Z_params_t layout (64-bit): int type(4) + pad(4) + union(16) = 24 bytes */
final int H5Z_PARAMS_STRING = 1;
try (Arena arena = Arena.ofConfined()) {
MemorySegment str_seg =
(params != null && !params.isEmpty()) ? arena.allocateFrom(params) : MemorySegment.NULL;
MemorySegment params_struct = arena.allocate(24, 8);
params_struct.set(ValueLayout.JAVA_INT, 0, H5Z_PARAMS_STRING);
params_struct.set(ValueLayout.ADDRESS, 8, str_seg);
retVal = org.hdfgroup.javahdf5.hdf5_h.H5Pmodify_filter_by_idx(plist_id, filter_idx, flags,
params_struct);
}
catch (HDF5LibraryException e) {
throw e;
}
catch (Throwable t) {
throw new HDF5LibraryException("H5Pmodify_filter_by_idx failed: " + t.getMessage());
}
if (retVal < 0)
h5libraryError();
return retVal;
}
/**
* @ingroup JH5P
*
* H5Pmodify_filter_by_idx replaces the configuration of the filter at a pipeline index using raw
* cd_values parameters. This form clears any stored configuration string on the entry, so
* introspection falls back to the filter's get_config reconstruction.
*
* @param plist_id
* IN: Property list identifier.
* @param filter_idx
* IN: Zero-based index of the filter in the pipeline.
* @param flags
* IN: Bit vector specifying certain general properties of the filter.
* @param cd_values
* IN: Auxiliary data for the filter, or null for no parameters.
*
* @return a non-negative value if successful
*
* @exception HDF5LibraryException
* Error from the HDF5 Library.
**/
public static int H5Pmodify_filter_by_idx(long plist_id, int filter_idx, int flags, int[] cd_values)
throws HDF5LibraryException
{
if (plist_id < 0)
throw new HDF5FunctionArgumentException("Negative property list identifier");
int retVal = -1;
/* Unlike the append case this cannot delegate to H5Pmodify_filter: that
* addresses an entry by filter ID and resolves to the first match, whereas
* this API addresses by pipeline index. The struct is built explicitly. */
final int H5Z_PARAMS_CDVALUES = 0;
int[] values = (cd_values != null) ? cd_values : new int[0];
try (Arena arena = Arena.ofConfined()) {
MemorySegment vals_seg = (values.length > 0)
? arena.allocateFrom(ValueLayout.JAVA_INT, values)
: MemorySegment.NULL;
MemorySegment params_struct = arena.allocate(24, 8);
params_struct.set(ValueLayout.JAVA_INT, 0, H5Z_PARAMS_CDVALUES);
/* union { struct { size_t cd_nelmts; const unsigned *cd_values; } raw; ... } */
params_struct.set(ValueLayout.JAVA_LONG, 8, (long)values.length);
params_struct.set(ValueLayout.ADDRESS, 16, vals_seg);
retVal = org.hdfgroup.javahdf5.hdf5_h.H5Pmodify_filter_by_idx(plist_id, filter_idx, flags,
params_struct);
}
catch (HDF5LibraryException e) {
throw e;
}
catch (Throwable t) {
throw new HDF5LibraryException("H5Pmodify_filter_by_idx failed: " + t.getMessage());
}
if (retVal < 0)
h5libraryError();
return retVal;
}
/**
* @ingroup JH5P
*
+61
View File
@@ -8500,6 +8500,67 @@ public class H5 implements java.io.Serializable {
private synchronized static native int H5Pappend_filter_raw(long plist_id, int filter_id, int flags,
int[] cd_values) throws HDF5LibraryException;
/**
* @ingroup JH5P
*
* H5Pmodify_filter_by_idx replaces the configuration of the filter at a pipeline index using a
* human-readable key=value parameter string. The entry keeps a stored configuration string.
*
* @param plist_id
* IN: Property list identifier.
* @param filter_idx
* IN: Zero-based index of the filter in the pipeline.
* @param flags
* IN: Bit vector specifying general filter properties.
* @param params
* IN: Parameter string in "key=value" format, or null for no parameters.
*
* @return a non-negative value if successful
*
* @exception HDF5LibraryException
* Error from the HDF5 Library.
**/
public synchronized static int H5Pmodify_filter_by_idx(long plist_id, int filter_idx, int flags,
String params) throws HDF5LibraryException
{
return H5Pmodify_filter_by_idx_str(plist_id, filter_idx, flags, params);
}
private synchronized static native int H5Pmodify_filter_by_idx_str(long plist_id, int filter_idx,
int flags, String params)
throws HDF5LibraryException;
/**
* @ingroup JH5P
*
* H5Pmodify_filter_by_idx replaces the configuration of the filter at a pipeline index using raw
* cd_values parameters. This form clears any stored configuration string on the entry, so
* introspection falls back to the filter's get_config reconstruction.
*
* @param plist_id
* IN: Property list identifier.
* @param filter_idx
* IN: Zero-based index of the filter in the pipeline.
* @param flags
* IN: Bit vector specifying general filter properties.
* @param cd_values
* IN: Auxiliary data for the filter, or null for no parameters.
*
* @return a non-negative value if successful
*
* @exception HDF5LibraryException
* Error from the HDF5 Library.
**/
public synchronized static int H5Pmodify_filter_by_idx(long plist_id, int filter_idx, int flags,
int[] cd_values) throws HDF5LibraryException
{
return H5Pmodify_filter_by_idx_raw(plist_id, filter_idx, flags, cd_values);
}
private synchronized static native int H5Pmodify_filter_by_idx_raw(long plist_id, int filter_idx,
int flags, int[] cd_values)
throws HDF5LibraryException;
/**
* @ingroup JH5P
*
+72
View File
@@ -1458,6 +1458,78 @@ done:
return (jint)status;
} /* end Java_hdf_hdf5lib_H5_H5Pappend_1filter_1raw */
/*
* Class: hdf_hdf5lib_H5
* Method: H5Pmodify_filter_by_idx (string form)
* Signature: (JIILjava/lang/String;)I
*/
JNIEXPORT jint JNICALL
Java_hdf_hdf5lib_H5_H5Pmodify_1filter_1by_1idx_1str(JNIEnv *env, jclass clss, jlong plist_id,
jint filter_idx, jint flags, jstring params)
{
H5Z_params_t p;
const char *c_params = NULL;
jboolean isCopy;
herr_t status = FAIL;
UNUSED(clss);
if (params)
PIN_JAVA_STRING(ENVONLY, params, c_params, &isCopy,
"H5Pmodify_filter_by_idx: params string not pinned");
p.type = H5Z_PARAMS_STRING;
p.u.str = c_params;
if ((status = H5Pmodify_filter_by_idx((hid_t)plist_id, (unsigned)filter_idx, (unsigned)flags, &p)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
done:
if (c_params)
UNPIN_JAVA_STRING(ENVONLY, params, c_params);
return (jint)status;
} /* end Java_hdf_hdf5lib_H5_H5Pmodify_1filter_1by_1idx_1str */
/*
* Class: hdf_hdf5lib_H5
* Method: H5Pmodify_filter_by_idx (raw cd_values form)
* Signature: (JII[I)I
*/
JNIEXPORT jint JNICALL
Java_hdf_hdf5lib_H5_H5Pmodify_1filter_1by_1idx_1raw(JNIEnv *env, jclass clss, jlong plist_id,
jint filter_idx, jint flags, jintArray cd_values)
{
H5Z_params_t p;
jint *c_cd_values = NULL;
jboolean isCopy;
herr_t status = FAIL;
jsize cd_nelmts = 0;
UNUSED(clss);
if (cd_values) {
cd_nelmts = ENVPTR->GetArrayLength(ENVONLY, cd_values);
if (cd_nelmts < 0)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Pmodify_filter_by_idx: cd_values array length < 0");
PIN_INT_ARRAY(ENVONLY, cd_values, c_cd_values, &isCopy,
"H5Pmodify_filter_by_idx: cd_values not pinned");
}
p.type = H5Z_PARAMS_CDVALUES;
p.u.raw.cd_nelmts = (size_t)cd_nelmts;
p.u.raw.cd_values = (const unsigned *)c_cd_values;
if ((status = H5Pmodify_filter_by_idx((hid_t)plist_id, (unsigned)filter_idx, (unsigned)flags, &p)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
done:
if (c_cd_values)
UNPIN_INT_ARRAY(ENVONLY, cd_values, c_cd_values, JNI_ABORT);
return (jint)status;
} /* end Java_hdf_hdf5lib_H5_H5Pmodify_1filter_1by_1idx_1raw */
/*
* Class: hdf_hdf5lib_H5
* Method: H5Pget_filter_params_by_idx
+16
View File
@@ -155,6 +155,22 @@ JNIEXPORT jint JNICALL Java_hdf_hdf5lib_H5_H5Pappend_1filter_1str(JNIEnv *, jcla
JNIEXPORT jint JNICALL Java_hdf_hdf5lib_H5_H5Pappend_1filter_1raw(JNIEnv *, jclass, jlong, jint, jint,
jintArray);
/*
* Class: hdf_hdf5lib_H5
* Method: H5Pmodify_filter_by_idx (string form)
* Signature: (JIILjava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_hdf_hdf5lib_H5_H5Pmodify_1filter_1by_1idx_1str(JNIEnv *, jclass, jlong, jint,
jint, jstring);
/*
* Class: hdf_hdf5lib_H5
* Method: H5Pmodify_filter_by_idx (raw cd_values form)
* Signature: (JII[I)I
*/
JNIEXPORT jint JNICALL Java_hdf_hdf5lib_H5_H5Pmodify_1filter_1by_1idx_1raw(JNIEnv *, jclass, jlong, jint,
jint, jintArray);
/*
* Class: hdf_hdf5lib_H5
* Method: H5Pget_filter_params_by_idx
+86
View File
@@ -243,6 +243,92 @@ public class TestH5Z {
}
}
@Test
public void testH5Pmodify_filter_by_idx_string()
{
long dcpl_id = HDF5Constants.H5I_INVALID_HID;
try {
dcpl_id = H5.H5Pcreate(HDF5Constants.H5P_DATASET_CREATE);
assertTrue("H5Pcreate", dcpl_id >= 0);
if (1 == H5.H5Zfilter_avail(HDF5Constants.H5Z_FILTER_DEFLATE)) {
int ret = H5.H5Pappend_filter(dcpl_id, HDF5Constants.H5Z_FILTER_DEFLATE, 0, "level=1");
assertTrue("H5Pappend_filter (string, deflate)", ret >= 0);
// Replace the configuration in place; position and ID are unchanged.
ret = H5.H5Pmodify_filter_by_idx(dcpl_id, 0, 0, "level=9");
assertTrue("H5Pmodify_filter_by_idx (string)", ret >= 0);
int nfilters = H5.H5Pget_nfilters(dcpl_id);
assertEquals("nfilters after H5Pmodify_filter_by_idx", 1, nfilters);
// The entry keeps a stored string, which reports the new value.
String[] params = new String[1];
ret = H5.H5Pget_filter_params_by_idx(dcpl_id, 0, params);
assertTrue("H5Pget_filter_params_by_idx", ret >= 0);
assertNotNull("params[0] is non-null", params[0]);
assertTrue("stored string reflects the modify: " + params[0],
params[0].contains("level=9"));
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5Pmodify_filter_by_idx_string: " + err);
}
finally {
if (dcpl_id != HDF5Constants.H5I_INVALID_HID)
try {
H5.H5Pclose(dcpl_id);
}
catch (Exception e) { /* ignore */
}
}
}
@Test
public void testH5Pmodify_filter_by_idx_cdvalues()
{
long dcpl_id = HDF5Constants.H5I_INVALID_HID;
try {
dcpl_id = H5.H5Pcreate(HDF5Constants.H5P_DATASET_CREATE);
assertTrue("H5Pcreate", dcpl_id >= 0);
if (1 == H5.H5Zfilter_avail(HDF5Constants.H5Z_FILTER_DEFLATE)) {
int ret = H5.H5Pappend_filter(dcpl_id, HDF5Constants.H5Z_FILTER_DEFLATE, 0, "level=1");
assertTrue("H5Pappend_filter (string, deflate)", ret >= 0);
int[] cd_values = new int[] {6};
ret = H5.H5Pmodify_filter_by_idx(dcpl_id, 0, 0, cd_values);
assertTrue("H5Pmodify_filter_by_idx (cd_values)", ret >= 0);
int nfilters = H5.H5Pget_nfilters(dcpl_id);
assertEquals("nfilters after H5Pmodify_filter_by_idx", 1, nfilters);
// cd_values were replaced.
int[] cd_out = new int[1];
int[] flags_out = new int[1];
long[] cd_nelmts = new long[] {1};
String[] name_out = new String[] {""};
int filter_id =
H5.H5Pget_filter(dcpl_id, 0, flags_out, cd_nelmts, cd_out, 256, name_out, new int[1]);
assertEquals("filter id unchanged by modify", HDF5Constants.H5Z_FILTER_DEFLATE, filter_id);
assertEquals("cd_values[0] after modify", 6, cd_out[0]);
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5Pmodify_filter_by_idx_cdvalues: " + err);
}
finally {
if (dcpl_id != HDF5Constants.H5I_INVALID_HID)
try {
H5.H5Pclose(dcpl_id);
}
catch (Exception e) { /* ignore */
}
}
}
@Test
public void testH5Pget_filter_params_by_idx()
{
+3 -1
View File
@@ -2,6 +2,8 @@ JUnit version 4.13.2
.testH5Pappend_filter_cdvalues
.testH5Pappend_filter_string
.testH5Pget_filter_params_by_idx
.testH5Pmodify_filter_by_idx_cdvalues
.testH5Pmodify_filter_by_idx_string
.testH5Zconfig_get_param_bool
.testH5Zconfig_get_param_double
.testH5Zconfig_get_param_int
@@ -15,5 +17,5 @@ JUnit version 4.13.2
Time: XXXX
OK (13 tests)
OK (15 tests)