From 56be7dc2c3f628acb794edc58e60adca6be984e1 Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Fri, 15 May 2026 11:48:00 -0500 Subject: [PATCH 1/9] Fix H5DreadVL failing for pre-allocate cmpd-of-seq dsets --- java/src-jni/jni/h5util.c | 29 +- java/src-jni/test/TestH5D.java | 286 ++++++++++++++++++ java/src-jni/test/testfiles/JUnit-TestH5D.txt | 5 +- 3 files changed, 304 insertions(+), 16 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index 684e36db6c4a..5bc25dd84b53 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -4576,20 +4576,23 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ if (!(vlSize = H5Tget_size(memb))) H5_LIBRARY_ERROR(ENVONLY); - /* Convert element to a vlen element */ - hvl_t vl_elem; - - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, in_obj); + /* Convert ArrayList to plain array */ + if (mToArray == NULL) + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts < 0) H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: number of VL elements < 0"); + /* Convert element to a vlen element */ + hvl_t vl_elem; vl_elem.len = (size_t)jnelmts; if (NULL == (vl_elem.p = malloc((size_t)jnelmts * vlSize))) H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_atomic_wbuf: failed to allocate vlen ptr buffer"); - translate_wbuf(ENVONLY, (jobjectArray)in_obj, memb, vlClass, (jsize)jnelmts, vl_elem.p); + translate_wbuf(ENVONLY, array, memb, vlClass, (jsize)jnelmts, vl_elem.p); memcpy(char_buf, &vl_elem, sizeof(hvl_t)); break; @@ -4873,24 +4876,23 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t break; } /* H5T_VLEN */ case H5T_COMPOUND: { - /* Convert each compound element to a list */ + /* Convert each compound element to a list. If a per-row slot was + * pre-allocated by the caller, reuse it, otherwise allocate a new + * ArrayList. */ for (i = 0; i < (size_t)count; i++) { - found_jList = JNI_TRUE; - jList = NULL; + jList = NULL; - /* The list we're going to return: */ if (i < (size_t)ret_buflen) { jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); } if (NULL == jList) { - found_jList = JNI_FALSE; if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_rbuf: failed to allocate list read buffer"); } int nmembs = H5Tget_nmembers(mem_type_id); - /* Convert each element to a list */ + /* Append each member's value to this row's ArrayList */ for (x = 0; x < (size_t)nmembs; x++) { H5T_class_t memb_vlClass; size_t memb_vlSize; @@ -4908,10 +4910,7 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t jobj = translate_atomic_rbuf(ENVONLY, memb, memb_vlClass, char_buf + i * typeSize + memb_offset); if (jobj) { - if (found_jList == JNI_FALSE) - ENVPTR->CallBooleanMethod(ENVONLY, jList, arrAddMethod, (jobject)jobj); - else - ENVPTR->SetObjectArrayElement(ENVONLY, jList, (jsize)i, (jobject)jobj); + ENVPTR->CallBooleanMethod(ENVONLY, jList, arrAddMethod, (jobject)jobj); CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE); ENVPTR->DeleteLocalRef(ENVONLY, jobj); } diff --git a/java/src-jni/test/TestH5D.java b/java/src-jni/test/TestH5D.java index 794c7c39f727..8aeaaa132c8c 100644 --- a/java/src-jni/test/TestH5D.java +++ b/java/src-jni/test/TestH5D.java @@ -12,7 +12,9 @@ package test; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -1947,4 +1949,288 @@ public void testH5DArrayenum_rw() } } } + + /* + * Build a 1-D dataset of type COMPOUND { seq: VLEN { int32 }, n: int32 } + * inside the per-test file (H5fid) and write canonical data via H5DwriteVL. + * Closes the vlen/compound/dataspace ids internally and returns only the + * open dataset id; the caller closes the dataset and re-fetches the type + * via H5Dget_type if needed. + */ + private long writeCompoundOfVlenDataset(String dsetName) throws Exception + { + long vlen_tid = HDF5Constants.H5I_INVALID_HID; + long cmpd_tid = HDF5Constants.H5I_INVALID_HID; + long dspace_id = HDF5Constants.H5I_INVALID_HID; + long dset_id = HDF5Constants.H5I_INVALID_HID; + + try { + vlen_tid = H5.H5Tvlen_create(HDF5Constants.H5T_NATIVE_INT); + assertTrue("writeCompoundOfVlenDataset: H5Tvlen_create: ", vlen_tid >= 0); + + long hvlSize = H5.H5Tget_size(vlen_tid); + long intSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT); + long packedSize = hvlSize + intSize; + cmpd_tid = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, packedSize); + assertTrue("writeCompoundOfVlenDataset: H5Tcreate compound: ", cmpd_tid >= 0); + H5.H5Tinsert(cmpd_tid, "seq", 0, vlen_tid); + H5.H5Tinsert(cmpd_tid, "n", hvlSize, HDF5Constants.H5T_NATIVE_INT); + H5.H5Tpack(cmpd_tid); + + long[] dims = {2}; + dspace_id = H5.H5Screate_simple(1, dims, null); + assertTrue("writeCompoundOfVlenDataset: H5Screate_simple: ", dspace_id >= 0); + + dset_id = H5.H5Dcreate(H5fid, dsetName, cmpd_tid, dspace_id, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT); + assertTrue("writeCompoundOfVlenDataset: H5Dcreate: ", dset_id >= 0); + + ArrayList seq0 = new ArrayList<>(); + seq0.add(1); + seq0.add(2); + seq0.add(3); + ArrayList seq1 = new ArrayList<>(); + seq1.add(5); + seq1.add(6); + + ArrayList[] write_data = new ArrayList[2]; + ArrayList rec0 = new ArrayList<>(); + rec0.add(seq0); + rec0.add(Integer.valueOf(4)); + write_data[0] = rec0; + ArrayList rec1 = new ArrayList<>(); + rec1.add(seq1); + rec1.add(Integer.valueOf(7)); + write_data[1] = rec1; + + H5.H5DwriteVL(dset_id, cmpd_tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, write_data); + H5.H5Fflush(H5fid, HDF5Constants.H5F_SCOPE_LOCAL); + } + finally { + if (dspace_id >= 0) + try { + H5.H5Sclose(dspace_id); + } + catch (Exception ex) { + } + if (cmpd_tid >= 0) + try { + H5.H5Tclose(cmpd_tid); + } + catch (Exception ex) { + } + if (vlen_tid >= 0) + try { + H5.H5Tclose(vlen_tid); + } + catch (Exception ex) { + } + } + + return dset_id; + } + + /* + * Read a 1-D dataset whose type is COMPOUND { seq: VLEN { int32 }, n: int32 }. + * Uses the canonical calling pattern: pass ArrayList[] with null slots + * and let the native code allocate each per-row record. + */ + @Test + public void testH5Dread_compound_of_vlen() + { + long dset_id = HDF5Constants.H5I_INVALID_HID; + long file_type_id = HDF5Constants.H5I_INVALID_HID; + final int N_ROWS = 2; + + try { + dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_rd"); + file_type_id = H5.H5Dget_type(dset_id); + assertTrue("testH5Dread_compound_of_vlen: H5Dget_type: ", file_type_id >= 0); + + ArrayList[] read_data = new ArrayList[N_ROWS]; + H5.H5DreadVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, read_data); + + assertNotNull("testH5Dread_compound_of_vlen: read_data[0] not null", read_data[0]); + assertNotNull("testH5Dread_compound_of_vlen: read_data[1] not null", read_data[1]); + assertEquals("testH5Dread_compound_of_vlen: row 0 record has 2 members", 2, + read_data[0].size()); + assertEquals("testH5Dread_compound_of_vlen: row 1 record has 2 members", 2, + read_data[1].size()); + + Object seq0obj = read_data[0].get(0); + Object seq1obj = read_data[1].get(0); + assertTrue("testH5Dread_compound_of_vlen: row 0 seq is ArrayList, got " + + (seq0obj == null ? "null" : seq0obj.getClass().getName()), + seq0obj instanceof ArrayList); + assertTrue("testH5Dread_compound_of_vlen: row 1 seq is ArrayList, got " + + (seq1obj == null ? "null" : seq1obj.getClass().getName()), + seq1obj instanceof ArrayList); + + ArrayList seq0_read = (ArrayList)seq0obj; + ArrayList seq1_read = (ArrayList)seq1obj; + assertEquals("testH5Dread_compound_of_vlen: row 0 seq length", 3, seq0_read.size()); + assertEquals("testH5Dread_compound_of_vlen: row 1 seq length", 2, seq1_read.size()); + assertEquals("testH5Dread_compound_of_vlen: row 0 seq[0]", Integer.valueOf(1), + seq0_read.get(0)); + assertEquals("testH5Dread_compound_of_vlen: row 0 seq[1]", Integer.valueOf(2), + seq0_read.get(1)); + assertEquals("testH5Dread_compound_of_vlen: row 0 seq[2]", Integer.valueOf(3), + seq0_read.get(2)); + assertEquals("testH5Dread_compound_of_vlen: row 1 seq[0]", Integer.valueOf(5), + seq1_read.get(0)); + assertEquals("testH5Dread_compound_of_vlen: row 1 seq[1]", Integer.valueOf(6), + seq1_read.get(1)); + assertEquals("testH5Dread_compound_of_vlen: row 0 n", Integer.valueOf(4), + read_data[0].get(1)); + assertEquals("testH5Dread_compound_of_vlen: row 1 n", Integer.valueOf(7), + read_data[1].get(1)); + } + catch (Throwable err) { + err.printStackTrace(); + fail("testH5Dread_compound_of_vlen: " + err); + } + finally { + if (file_type_id >= 0) + try { + H5.H5Tclose(file_type_id); + } + catch (Exception ex) { + } + if (dset_id >= 0) + try { + H5.H5Dclose(dset_id); + } + catch (Exception ex) { + } + } + } + + /* + * Same compound-of-vlen read as testH5Dread_compound_of_vlen, but the caller + * pre-allocates each row's ArrayList. Should behave the same as the + * no-pre-allocation test. + */ + @Test + public void testH5Dread_compound_of_vlen_preallocated() + { + long dset_id = HDF5Constants.H5I_INVALID_HID; + long file_type_id = HDF5Constants.H5I_INVALID_HID; + final int N_ROWS = 2; + + try { + dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_rd_prealloc"); + file_type_id = H5.H5Dget_type(dset_id); + assertTrue("testH5Dread_compound_of_vlen_preallocated: H5Dget_type: ", + file_type_id >= 0); + + ArrayList[] read_data = new ArrayList[N_ROWS]; + for (int i = 0; i < N_ROWS; i++) + read_data[i] = new ArrayList(); + + H5.H5DreadVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, read_data); + + assertEquals("testH5Dread_compound_of_vlen_preallocated: row 0 record has 2 members", 2, + read_data[0].size()); + assertEquals("testH5Dread_compound_of_vlen_preallocated: row 1 record has 2 members", 2, + read_data[1].size()); + + Object seq0obj = read_data[0].get(0); + assertTrue("testH5Dread_compound_of_vlen_preallocated: row 0 seq is ArrayList, got " + + (seq0obj == null ? "null" : seq0obj.getClass().getName()), + seq0obj instanceof ArrayList); + ArrayList seq0_read = (ArrayList)seq0obj; + assertEquals("testH5Dread_compound_of_vlen_preallocated: row 0 seq length", 3, + seq0_read.size()); + assertEquals("testH5Dread_compound_of_vlen_preallocated: row 0 seq[0]", + Integer.valueOf(1), seq0_read.get(0)); + } + catch (Throwable err) { + err.printStackTrace(); + fail("testH5Dread_compound_of_vlen_preallocated: " + err); + } + finally { + if (file_type_id >= 0) + try { + H5.H5Tclose(file_type_id); + } + catch (Exception ex) { + } + if (dset_id >= 0) + try { + H5.H5Dclose(dset_id); + } + catch (Exception ex) { + } + } + } + + /* + * Round-trip a compound-of-vlen dataset through H5DwriteVL and H5DreadVL. + * Reuses the writeCompoundOfVlenDataset helper to exercise the write path, + * then reads back and asserts the full row contents. + */ + @Test + public void testH5Dwrite_compound_of_vlen() + { + long dset_id = HDF5Constants.H5I_INVALID_HID; + long file_type_id = HDF5Constants.H5I_INVALID_HID; + final int N_ROWS = 2; + + try { + dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr"); + file_type_id = H5.H5Dget_type(dset_id); + assertTrue("testH5Dwrite_compound_of_vlen: H5Dget_type: ", file_type_id >= 0); + + ArrayList[] read_data = new ArrayList[N_ROWS]; + H5.H5DreadVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, read_data); + + assertNotNull("testH5Dwrite_compound_of_vlen: read_data[0] not null", read_data[0]); + assertNotNull("testH5Dwrite_compound_of_vlen: read_data[1] not null", read_data[1]); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 record has 2 members", 2, + read_data[0].size()); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 record has 2 members", 2, + read_data[1].size()); + + ArrayList seq0_read = (ArrayList)read_data[0].get(0); + ArrayList seq1_read = (ArrayList)read_data[1].get(0); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq length", 3, seq0_read.size()); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 seq length", 2, seq1_read.size()); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[0]", Integer.valueOf(1), + seq0_read.get(0)); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[1]", Integer.valueOf(2), + seq0_read.get(1)); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[2]", Integer.valueOf(3), + seq0_read.get(2)); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 seq[0]", Integer.valueOf(5), + seq1_read.get(0)); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 seq[1]", Integer.valueOf(6), + seq1_read.get(1)); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 n", Integer.valueOf(4), + read_data[0].get(1)); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 n", Integer.valueOf(7), + read_data[1].get(1)); + } + catch (Throwable err) { + err.printStackTrace(); + fail("testH5Dwrite_compound_of_vlen: " + err); + } + finally { + if (file_type_id >= 0) + try { + H5.H5Tclose(file_type_id); + } + catch (Exception ex) { + } + if (dset_id >= 0) + try { + H5.H5Dclose(dset_id); + } + catch (Exception ex) { + } + } + } } diff --git a/java/src-jni/test/testfiles/JUnit-TestH5D.txt b/java/src-jni/test/testfiles/JUnit-TestH5D.txt index 1651176c1ee3..dd045b018931 100644 --- a/java/src-jni/test/testfiles/JUnit-TestH5D.txt +++ b/java/src-jni/test/testfiles/JUnit-TestH5D.txt @@ -17,12 +17,15 @@ JUnit version 4.13.2 .testH5Dget_access_plist .testH5Dget_space_closed .testH5DArray_string_buffer +.testH5Dread_compound_of_vlen .testH5Dget_space_status .testH5Dvlen_write_read +.testH5Dread_compound_of_vlen_preallocated .testH5Dget_space .testH5Dget_type_closed +.testH5Dwrite_compound_of_vlen Time: XXXX -OK (22 tests) +OK (25 tests) From 7a6d03b497e24cf90640bc9f85c5869fc6f00387 Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Mon, 18 May 2026 10:18:15 -0500 Subject: [PATCH 2/9] Fix bad vlen of cmpd with null slot read --- java/src-jni/jni/h5util.c | 28 ++--- java/src-jni/test/TestH5D.java | 114 ++++++++++++++++++ java/src-jni/test/testfiles/JUnit-TestH5D.txt | 3 +- 3 files changed, 125 insertions(+), 20 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index 5bc25dd84b53..21403c33efc9 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -4832,28 +4832,20 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t if (!(vlSize = H5Tget_size(memb))) H5_LIBRARY_ERROR(ENVONLY); - /* Convert each element to a list */ for (i = 0; i < (size_t)count; i++) { hvl_t vl_elem; + jList = NULL; - found_jList = JNI_TRUE; - jList = NULL; - - /* Get the number of sequence elements */ memcpy(&vl_elem, char_buf + i * sizeof(hvl_t), sizeof(hvl_t)); jsize nelmts = (jsize)vl_elem.len; if (vl_elem.len != (size_t)nelmts) H5_JNI_FATAL_ERROR(ENVONLY, "translate_rbuf: overflow of number of VL elements"); - if (nelmts < 0) H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_rbuf: number of VL elements < 0"); - /* The list we're going to return: */ - if (i < (size_t)ret_buflen) { + if (i < (size_t)ret_buflen) jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); - } if (NULL == jList) { - found_jList = JNI_FALSE; if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) H5_OUT_OF_MEMORY_ERROR(ENVONLY, @@ -4861,16 +4853,14 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t } translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)nelmts, vl_elem.p); - if (found_jList == JNI_FALSE) { - jboolean addResult = - ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, (jobject)jList); - if (!addResult) - H5_JNI_FATAL_ERROR(ENVONLY, "translate_rbuf: cannot add VL element"); - } - else { + + // ret_buflen == 0 indicates ret_buf is an ArrayList (recursive call); use add. + // Otherwise ret_buf is a Java array; install jList at slot i. + if (ret_buflen == 0) + ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, (jobject)jList); + else ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, (jobject)jList); - CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE); - } + CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE); ENVPTR->DeleteLocalRef(ENVONLY, jList); } break; diff --git a/java/src-jni/test/TestH5D.java b/java/src-jni/test/TestH5D.java index 8aeaaa132c8c..3deea075f112 100644 --- a/java/src-jni/test/TestH5D.java +++ b/java/src-jni/test/TestH5D.java @@ -2233,4 +2233,118 @@ public void testH5Dwrite_compound_of_vlen() } } } + + /* + * Read a 1-D dataset whose type is VLEN { COMPOUND { A: int, B: int } } using the + * canonical null-row-slot read pattern. Exercises the translate_rbuf H5T_VLEN + * top-level case; pre-fix this read SIGSEGVed because the function's + * found_jList-FALSE branch called ArrayList.add() on the caller's Java array. + */ + @Test + public void testH5Dread_vlen_of_compound_nullslots() + { + long cmpd_tid = HDF5Constants.H5I_INVALID_HID; + long vlen_tid = HDF5Constants.H5I_INVALID_HID; + long dspace_id = HDF5Constants.H5I_INVALID_HID; + long dset_id = HDF5Constants.H5I_INVALID_HID; + final int N_ROWS = 2; + + try { + long intSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT); + + cmpd_tid = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, 2 * intSize); + H5.H5Tinsert(cmpd_tid, "A", 0, HDF5Constants.H5T_NATIVE_INT); + H5.H5Tinsert(cmpd_tid, "B", intSize, HDF5Constants.H5T_NATIVE_INT); + H5.H5Tpack(cmpd_tid); + + vlen_tid = H5.H5Tvlen_create(cmpd_tid); + long[] dims = {N_ROWS}; + dspace_id = H5.H5Screate_simple(1, dims, null); + + dset_id = H5.H5Dcreate(H5fid, "vlen_of_cmpd_rd", vlen_tid, dspace_id, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT); + assertTrue("testH5Dread_vlen_of_compound_nullslots: H5Dcreate: ", dset_id >= 0); + + // row 0: 1 element [ { A=1, B=2 } ] + // row 1: 2 elements [ { A=3, B=4 }, { A=5, B=6 } ] + ArrayList row0_elem0 = new ArrayList<>(); + row0_elem0.add(Integer.valueOf(1)); + row0_elem0.add(Integer.valueOf(2)); + ArrayList row0 = new ArrayList<>(); + row0.add(row0_elem0); + + ArrayList row1_elem0 = new ArrayList<>(); + row1_elem0.add(Integer.valueOf(3)); + row1_elem0.add(Integer.valueOf(4)); + ArrayList row1_elem1 = new ArrayList<>(); + row1_elem1.add(Integer.valueOf(5)); + row1_elem1.add(Integer.valueOf(6)); + ArrayList row1 = new ArrayList<>(); + row1.add(row1_elem0); + row1.add(row1_elem1); + + ArrayList[] write_data = new ArrayList[N_ROWS]; + write_data[0] = row0; + write_data[1] = row1; + + H5.H5DwriteVL(dset_id, vlen_tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, write_data); + H5.H5Fflush(H5fid, HDF5Constants.H5F_SCOPE_LOCAL); + + // Null row slots: pre-fix this would SIGSEGV inside translate_rbuf. + ArrayList[] read_data = new ArrayList[N_ROWS]; + H5.H5DreadVL(dset_id, vlen_tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, read_data); + + assertNotNull("testH5Dread_vlen_of_compound_nullslots: row 0 not null", read_data[0]); + assertNotNull("testH5Dread_vlen_of_compound_nullslots: row 1 not null", read_data[1]); + assertEquals("testH5Dread_vlen_of_compound_nullslots: row 0 has 1 element", 1, + read_data[0].size()); + assertEquals("testH5Dread_vlen_of_compound_nullslots: row 1 has 2 elements", 2, + read_data[1].size()); + + ArrayList r0e0 = (ArrayList)read_data[0].get(0); + assertEquals("row 0 elem 0 A", Integer.valueOf(1), r0e0.get(0)); + assertEquals("row 0 elem 0 B", Integer.valueOf(2), r0e0.get(1)); + + ArrayList r1e0 = (ArrayList)read_data[1].get(0); + assertEquals("row 1 elem 0 A", Integer.valueOf(3), r1e0.get(0)); + assertEquals("row 1 elem 0 B", Integer.valueOf(4), r1e0.get(1)); + + ArrayList r1e1 = (ArrayList)read_data[1].get(1); + assertEquals("row 1 elem 1 A", Integer.valueOf(5), r1e1.get(0)); + assertEquals("row 1 elem 1 B", Integer.valueOf(6), r1e1.get(1)); + } + catch (Throwable err) { + err.printStackTrace(); + fail("testH5Dread_vlen_of_compound_nullslots: " + err); + } + finally { + if (dset_id >= 0) + try { + H5.H5Dclose(dset_id); + } + catch (Exception ex) { + } + if (dspace_id >= 0) + try { + H5.H5Sclose(dspace_id); + } + catch (Exception ex) { + } + if (vlen_tid >= 0) + try { + H5.H5Tclose(vlen_tid); + } + catch (Exception ex) { + } + if (cmpd_tid >= 0) + try { + H5.H5Tclose(cmpd_tid); + } + catch (Exception ex) { + } + } + } } diff --git a/java/src-jni/test/testfiles/JUnit-TestH5D.txt b/java/src-jni/test/testfiles/JUnit-TestH5D.txt index dd045b018931..232a45653697 100644 --- a/java/src-jni/test/testfiles/JUnit-TestH5D.txt +++ b/java/src-jni/test/testfiles/JUnit-TestH5D.txt @@ -17,6 +17,7 @@ JUnit version 4.13.2 .testH5Dget_access_plist .testH5Dget_space_closed .testH5DArray_string_buffer +.testH5Dread_vlen_of_compound_nullslots .testH5Dread_compound_of_vlen .testH5Dget_space_status .testH5Dvlen_write_read @@ -27,5 +28,5 @@ JUnit version 4.13.2 Time: XXXX -OK (25 tests) +OK (26 tests) From 15ca434eca302b209da8dbf870a71a52072a4e6d Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Mon, 18 May 2026 10:41:50 -0500 Subject: [PATCH 3/9] Fix bad cmpd of cmpd read in java `translate_rbuf`'s H5T_VLEN case had a similar bug where when `found_jList` was set to false due to an entyr in `ret_buf` being null, `ret_buf.add()` would be invoked on an array of objects without the list .add() method. This would occur whenever a read was invoked of a vlen sequence with a null (non-preallocated) entry. The pre-existing tests only tested the pre-allocated cases. I removed the use of the `found_jList` flag, since it conflated the passing of an unallocated slot with `ret_buf` not being an array. Instead use `ret_buflen == 0` as the check to match the pattern in H5T_INTEGER and other branches. The test for this fix is testH5Dread_vlen_of_compound_nullslot. --- `translate_atomic_rebuf` had two issues related to handling of nested compounds. First, it discarded recursive returns, resulting in the construction of empty lists. Secondly, its member offset (`char_buf + i * typeSize + memb_offset`) was incorrect. In this case, `i` was the member index and `memberSize` was the entire cmpd size, so the offset would be erroneously large. It seems like this came from copying of the offset computation from `translate_rbuf`, which had to advance over entire elements of compound data. This error was duplicated on the write side in `translate_atomic_wbuf`'s H5T_COMPOUND case (h5util.c:4611). I changed `translate_atomic_rbuf` to capture the resultant object, and dropped the `i * typeSize` term in both routines. The new test verifying the fix works is `testH5Dread_vlen_of_nested_compound`. --- java/src-jni/jni/h5util.c | 14 +- java/src-jni/test/TestH5D.java | 140 ++++++++++++++++++ java/src-jni/test/testfiles/JUnit-TestH5D.txt | 3 +- 3 files changed, 151 insertions(+), 6 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index 21403c33efc9..5623a453e6a4 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -4252,6 +4252,7 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo /* retrieve the java.util.ArrayList interface class */ jclass arrCList = ENVPTR->FindClass(ENVONLY, "java/util/ArrayList"); jmethodID arrListMethod = ENVPTR->GetMethodID(ENVONLY, arrCList, "", "(I)V"); + jmethodID arrAddMethod = ENVPTR->GetMethodID(ENVONLY, arrCList, "add", "(Ljava/lang/Object;)Z"); /* Cache class types */ /* jclass cBool = ENVPTR->FindClass(ENVONLY, "java/lang/Boolean"); */ @@ -4308,15 +4309,14 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo case H5T_COMPOUND: { int nmembs = H5Tget_nmembers(mem_type_id); - /* The list we're going to return: */ if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_atomic_rbuf: failed to allocate list read buffer"); - /* Convert each element to a compound object */ for (i = 0; i < (size_t)nmembs; i++) { H5T_class_t memb_vlClass; size_t memb_vlSize; size_t memb_offset; + jobject memb_jobj; if ((memb = H5Tget_member_type(mem_type_id, (unsigned int)i)) < 0) H5_LIBRARY_ERROR(ENVONLY); @@ -4326,7 +4326,12 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo if (!(memb_vlSize = H5Tget_size(memb))) H5_LIBRARY_ERROR(ENVONLY); - translate_atomic_rbuf(ENVONLY, memb, memb_vlClass, char_buf + i * typeSize + memb_offset); + memb_jobj = translate_atomic_rbuf(ENVONLY, memb, memb_vlClass, char_buf + memb_offset); + if (memb_jobj) { + ENVPTR->CallBooleanMethod(ENVONLY, jList, arrAddMethod, memb_jobj); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE); + ENVPTR->DeleteLocalRef(ENVONLY, memb_jobj); + } H5Tclose(memb); } jobj = jList; @@ -4626,8 +4631,7 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ H5_LIBRARY_ERROR(ENVONLY); jobject arr_obj = ENVPTR->GetObjectArrayElement(ENVONLY, array, (jsize)i); - translate_atomic_wbuf(ENVONLY, arr_obj, memb, memb_vlClass, - char_buf + i * typeSize + memb_offset); + translate_atomic_wbuf(ENVONLY, arr_obj, memb, memb_vlClass, char_buf + memb_offset); ENVPTR->DeleteLocalRef(ENVONLY, arr_obj); H5Tclose(memb); } diff --git a/java/src-jni/test/TestH5D.java b/java/src-jni/test/TestH5D.java index 3deea075f112..8c1ac523c537 100644 --- a/java/src-jni/test/TestH5D.java +++ b/java/src-jni/test/TestH5D.java @@ -2347,4 +2347,144 @@ public void testH5Dread_vlen_of_compound_nullslots() } } } + + /* + * Read a 1-D dataset whose type is VLEN { COMPOUND { id: int, sub: COMPOUND { P: int, Q: int } } }. + * Per-row canonical shape (per testH5Dwrite_readCompound contract): + * row r ArrayList of vlen elements + * each element is ArrayList of 2 members [Integer id, ArrayList sub] + * sub is ArrayList of 2 members [Integer P, Integer Q] + */ + @Test + public void testH5Dread_vlen_of_nested_compound() + { + long inner_tid = HDF5Constants.H5I_INVALID_HID; + long outer_tid = HDF5Constants.H5I_INVALID_HID; + long vlen_tid = HDF5Constants.H5I_INVALID_HID; + long dspace_id = HDF5Constants.H5I_INVALID_HID; + long dset_id = HDF5Constants.H5I_INVALID_HID; + final int N_ROWS = 2; + + try { + long intSize = H5.H5Tget_size(HDF5Constants.H5T_NATIVE_INT); + + // inner = compound { P:int, Q:int } + inner_tid = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, 2 * intSize); + H5.H5Tinsert(inner_tid, "P", 0, HDF5Constants.H5T_NATIVE_INT); + H5.H5Tinsert(inner_tid, "Q", intSize, HDF5Constants.H5T_NATIVE_INT); + H5.H5Tpack(inner_tid); + + // outer = compound { id:int, sub:inner } + long innerSize = H5.H5Tget_size(inner_tid); + outer_tid = H5.H5Tcreate(HDF5Constants.H5T_COMPOUND, intSize + innerSize); + H5.H5Tinsert(outer_tid, "id", 0, HDF5Constants.H5T_NATIVE_INT); + H5.H5Tinsert(outer_tid, "sub", intSize, inner_tid); + H5.H5Tpack(outer_tid); + + // vlen of outer + vlen_tid = H5.H5Tvlen_create(outer_tid); + long[] dims = {N_ROWS}; + dspace_id = H5.H5Screate_simple(1, dims, null); + + dset_id = H5.H5Dcreate(H5fid, "vlen_of_nested_cmpd", vlen_tid, dspace_id, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT); + assertTrue("testH5Dread_vlen_of_nested_compound: H5Dcreate: ", dset_id >= 0); + + // row 0: 1 element [ { id=1, sub={ P=2, Q=3 } } ] + // row 1: 2 elements [ { id=4, sub={ P=5, Q=6 } }, { id=7, sub={ P=8, Q=9 } } ] + ArrayList row0_elem0_sub = new ArrayList<>(); + row0_elem0_sub.add(Integer.valueOf(2)); + row0_elem0_sub.add(Integer.valueOf(3)); + ArrayList row0_elem0 = new ArrayList<>(); + row0_elem0.add(Integer.valueOf(1)); + row0_elem0.add(row0_elem0_sub); + ArrayList row0 = new ArrayList<>(); + row0.add(row0_elem0); + + ArrayList row1_elem0_sub = new ArrayList<>(); + row1_elem0_sub.add(Integer.valueOf(5)); + row1_elem0_sub.add(Integer.valueOf(6)); + ArrayList row1_elem0 = new ArrayList<>(); + row1_elem0.add(Integer.valueOf(4)); + row1_elem0.add(row1_elem0_sub); + ArrayList row1_elem1_sub = new ArrayList<>(); + row1_elem1_sub.add(Integer.valueOf(8)); + row1_elem1_sub.add(Integer.valueOf(9)); + ArrayList row1_elem1 = new ArrayList<>(); + row1_elem1.add(Integer.valueOf(7)); + row1_elem1.add(row1_elem1_sub); + ArrayList row1 = new ArrayList<>(); + row1.add(row1_elem0); + row1.add(row1_elem1); + + ArrayList[] write_data = new ArrayList[N_ROWS]; + write_data[0] = row0; + write_data[1] = row1; + + H5.H5DwriteVL(dset_id, vlen_tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, write_data); + H5.H5Fflush(H5fid, HDF5Constants.H5F_SCOPE_LOCAL); + + ArrayList[] read_data = new ArrayList[N_ROWS]; + H5.H5DreadVL(dset_id, vlen_tid, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, read_data); + + assertNotNull("row 0 not null", read_data[0]); + assertNotNull("row 1 not null", read_data[1]); + assertEquals("row 0 has 1 element", 1, read_data[0].size()); + assertEquals("row 1 has 2 elements", 2, read_data[1].size()); + + ArrayList r0e0 = (ArrayList)read_data[0].get(0); + assertEquals("row 0 elem 0 has 2 members", 2, r0e0.size()); + assertEquals("row 0 elem 0 id", Integer.valueOf(1), r0e0.get(0)); + ArrayList r0e0sub = (ArrayList)r0e0.get(1); + assertEquals("row 0 elem 0 sub has 2 members", 2, r0e0sub.size()); + assertEquals("row 0 elem 0 sub.P", Integer.valueOf(2), r0e0sub.get(0)); + assertEquals("row 0 elem 0 sub.Q", Integer.valueOf(3), r0e0sub.get(1)); + + ArrayList r1e1 = (ArrayList)read_data[1].get(1); + assertEquals("row 1 elem 1 has 2 members", 2, r1e1.size()); + assertEquals("row 1 elem 1 id", Integer.valueOf(7), r1e1.get(0)); + ArrayList r1e1sub = (ArrayList)r1e1.get(1); + assertEquals("row 1 elem 1 sub.P", Integer.valueOf(8), r1e1sub.get(0)); + assertEquals("row 1 elem 1 sub.Q", Integer.valueOf(9), r1e1sub.get(1)); + } + catch (Throwable err) { + err.printStackTrace(); + fail("testH5Dread_vlen_of_nested_compound: " + err); + } + finally { + if (dset_id >= 0) + try { + H5.H5Dclose(dset_id); + } + catch (Exception ex) { + } + if (dspace_id >= 0) + try { + H5.H5Sclose(dspace_id); + } + catch (Exception ex) { + } + if (vlen_tid >= 0) + try { + H5.H5Tclose(vlen_tid); + } + catch (Exception ex) { + } + if (outer_tid >= 0) + try { + H5.H5Tclose(outer_tid); + } + catch (Exception ex) { + } + if (inner_tid >= 0) + try { + H5.H5Tclose(inner_tid); + } + catch (Exception ex) { + } + } + } } diff --git a/java/src-jni/test/testfiles/JUnit-TestH5D.txt b/java/src-jni/test/testfiles/JUnit-TestH5D.txt index 232a45653697..95bc0eb874dc 100644 --- a/java/src-jni/test/testfiles/JUnit-TestH5D.txt +++ b/java/src-jni/test/testfiles/JUnit-TestH5D.txt @@ -15,6 +15,7 @@ JUnit version 4.13.2 .testH5Dget_storage_size_empty .testH5Diterate .testH5Dget_access_plist +.testH5Dread_vlen_of_nested_compound .testH5Dget_space_closed .testH5DArray_string_buffer .testH5Dread_vlen_of_compound_nullslots @@ -28,5 +29,5 @@ JUnit version 4.13.2 Time: XXXX -OK (26 tests) +OK (27 tests) From fd79fa80ceb253a3397609ccb33084745f346b64 Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Wed, 20 May 2026 10:18:00 -0500 Subject: [PATCH 4/9] Add exception checks --- java/src-jni/jni/h5util.c | 75 ++++++++++++++++++++++++++++++--------- 1 file changed, 59 insertions(+), 16 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index 5623a453e6a4..fd2b822e1dc6 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -4584,8 +4584,13 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ /* Convert ArrayList to plain array */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); - jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); + if (NULL == in_obj) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: VL in_obj is NULL"); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + if (NULL == array) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: ArrayList.toArray returned NULL"); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts < 0) H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: number of VL elements < 0"); @@ -4600,6 +4605,8 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ translate_wbuf(ENVONLY, array, memb, vlClass, (jsize)jnelmts, vl_elem.p); memcpy(char_buf, &vl_elem, sizeof(hvl_t)); + + ENVPTR->DeleteLocalRef(ENVONLY, array); break; } /* H5T_VLEN */ case H5T_COMPOUND: { @@ -4609,8 +4616,13 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); - jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); + if (NULL == in_obj) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: compound in_obj is NULL"); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + if (NULL == array) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: ArrayList.toArray returned NULL"); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts != nmembs) H5_BAD_ARGUMENT_ERROR( @@ -4635,6 +4647,8 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ ENVPTR->DeleteLocalRef(ENVONLY, arr_obj); H5Tclose(memb); } + + ENVPTR->DeleteLocalRef(ENVONLY, array); break; } /* H5T_COMPOUND */ case H5T_ARRAY: { @@ -4651,8 +4665,13 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); - jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); + if (NULL == in_obj) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: array in_obj is NULL"); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + if (NULL == array) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: ArrayList.toArray returned NULL"); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts < 0) H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: number of array elements < 0"); @@ -4663,6 +4682,8 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ translate_wbuf(ENVONLY, array, memb, vlClass, (jsize)jnelmts, objBuf); memcpy(char_buf, (char *)objBuf, vlSize * (size_t)jnelmts); + + ENVPTR->DeleteLocalRef(ENVONLY, array); break; } /* H5T_ARRAY */ case H5T_ENUM: @@ -4768,8 +4789,13 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ /* Convert each array element - invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); - jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); + if (NULL == in_obj) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: complex in_obj is NULL"); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + if (NULL == array) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: ArrayList.toArray returned NULL"); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts < 0) H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: number of array elements < 0"); @@ -4784,6 +4810,7 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_ if (objBuf) free(objBuf); + ENVPTR->DeleteLocalRef(ENVONLY, array); break; } case H5T_TIME: @@ -5102,8 +5129,11 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); - jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, jList, mToArray); - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, jList, mToArray); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + if (NULL == array) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: ArrayList.toArray returned NULL"); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts < 0) H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: number of VL elements < 0"); @@ -5117,6 +5147,7 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t memcpy(char_buf + i * sizeof(hvl_t), &vl_elem, sizeof(hvl_t)); + ENVPTR->DeleteLocalRef(ENVONLY, array); ENVPTR->DeleteLocalRef(ENVONLY, jList); } /* end for (i = 0; i < count; i++) */ break; @@ -5132,8 +5163,11 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); - jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, jList, mToArray); - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, jList, mToArray); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + if (NULL == array) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: ArrayList.toArray returned NULL"); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts != nmembs) H5_BAD_ARGUMENT_ERROR( @@ -5161,6 +5195,7 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t H5Tclose(memb); } + ENVPTR->DeleteLocalRef(ENVONLY, array); ENVPTR->DeleteLocalRef(ENVONLY, jList); } /* end for (i = 0; i < count; i++) */ break; @@ -5181,8 +5216,11 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); - jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, jList, mToArray); - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, jList, mToArray); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + if (NULL == array) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: ArrayList.toArray returned NULL"); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts < 0) H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: number of array elements < 0"); @@ -5190,6 +5228,7 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t translate_wbuf(ENVONLY, array, memb, vlClass, jnelmts, char_buf + i * vlSize * (size_t)jnelmts); + ENVPTR->DeleteLocalRef(ENVONLY, array); ENVPTR->DeleteLocalRef(ENVONLY, jList); } /* end for (i = 0; i < count; i++) */ break; @@ -5229,8 +5268,11 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); - jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, jList, mToArray); - jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); + jobjectArray array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, jList, mToArray); + CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + if (NULL == array) + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: ArrayList.toArray returned NULL"); + jsize jnelmts = ENVPTR->GetArrayLength(ENVONLY, array); if (jnelmts < 0) H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: number of array elements < 0"); @@ -5238,6 +5280,7 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t translate_wbuf(ENVONLY, array, memb, base_class, jnelmts, char_buf + i * base_size * (size_t)jnelmts); + ENVPTR->DeleteLocalRef(ENVONLY, array); ENVPTR->DeleteLocalRef(ENVONLY, jList); } /* end for (i = 0; i < count; i++) */ From aed262205fe45027458188eb6db0271638f2a81f Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Wed, 20 May 2026 10:20:00 -0500 Subject: [PATCH 5/9] Update NULL checks in translate_wbuf --- java/src-jni/jni/h5util.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index fd2b822e1dc6..de95b0a93fe0 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -5123,8 +5123,10 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t for (i = 0; i < (size_t)count; i++) { hvl_t vl_elem; - if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) + if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); + } /* invoke the toArray method */ if (mToArray == NULL) @@ -5155,8 +5157,10 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t case H5T_COMPOUND: { /* Convert each list to a compound element */ for (i = 0; i < (size_t)count; i++) { - if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) + if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); + } int nmembs = H5Tget_nmembers(mem_type_id); @@ -5210,8 +5214,10 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t /* Convert each list to an array element */ for (i = 0; i < (size_t)count; i++) { - if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) + if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); + } /* invoke the toArray method */ if (mToArray == NULL) @@ -5262,8 +5268,10 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t /* Convert each list to an array element */ for (i = 0; i < (size_t)count; i++) { - if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) + if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); + H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); + } /* invoke the toArray method */ if (mToArray == NULL) From 65bf5a7e0fb7461579fb9d6d88673508cc0c8215 Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Wed, 20 May 2026 10:26:05 -0500 Subject: [PATCH 6/9] Correct potentially bad array length check --- java/src-jni/jni/h5util.c | 61 ++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index de95b0a93fe0..c312dc027a3e 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -4830,10 +4830,10 @@ void translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t type_class, jsize count, void *raw_buf) { - hid_t memb = H5I_INVALID_HID; - int ret_buflen = -1; - jboolean found_jList = JNI_TRUE; - jobjectArray jList = NULL; + hid_t memb = H5I_INVALID_HID; + int ret_buflen = -1; + jboolean retIsList = JNI_FALSE; + jobjectArray jList = NULL; jobject jobj = NULL; H5T_class_t vlClass; size_t vlSize; @@ -4850,9 +4850,19 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t if (!(typeSize = H5Tget_size(mem_type_id))) H5_LIBRARY_ERROR(ENVONLY); - ret_buflen = ENVPTR->GetArrayLength(ENVONLY, ret_buf); - if (ret_buflen < 0) - H5_JNI_FATAL_ERROR(ENVONLY, "ret_buflen: Array length cannot be negative"); + /* Top-level calls pass a Java Object[] array; recursive calls pass an + * ArrayList. Detect which so we never call GetArrayLength on a non-array + * (undefined behavior) and so the append-vs-set decision is unambiguous. */ + retIsList = ENVPTR->IsInstanceOf(ENVONLY, ret_buf, arrCList); + if (retIsList) { + /* Lists are always appended to; no slot reuse. */ + ret_buflen = 0; + } + else { + ret_buflen = ENVPTR->GetArrayLength(ENVONLY, ret_buf); + if (ret_buflen < 0) + H5_JNI_FATAL_ERROR(ENVONLY, "ret_buflen: Array length cannot be negative"); + } switch (type_class) { case H5T_VLEN: { @@ -4885,9 +4895,8 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)nelmts, vl_elem.p); - // ret_buflen == 0 indicates ret_buf is an ArrayList (recursive call); use add. - // Otherwise ret_buf is a Java array; install jList at slot i. - if (ret_buflen == 0) + /* ArrayList (recursive call): append. Java array: install at slot i. */ + if (retIsList) ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, (jobject)jList); else ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, (jobject)jList); @@ -4938,7 +4947,7 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t H5Tclose(memb); } - if (ret_buflen == 0) + if (retIsList) ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, jList); else ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, jList); @@ -4967,18 +4976,14 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t /* Convert each element to a list */ for (i = 0; i < (size_t)count; i++) { - found_jList = JNI_TRUE; - jList = NULL; + jList = NULL; /* Get the object element */ memcpy((char *)objBuf, char_buf + i * typeSize, typeSize); - /* The list we're going to return: */ - if (i < (size_t)ret_buflen) { - if (NULL == - (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i))) - found_jList = JNI_FALSE; - } + /* Reuse a pre-allocated slot if the caller supplied one. */ + if (i < (size_t)ret_buflen) + jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); if (NULL == jList) { if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) @@ -4987,7 +4992,7 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t } translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)typeCount, objBuf); - if (found_jList == JNI_FALSE) + if (retIsList) ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, jList); else ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, jList); @@ -5011,7 +5016,7 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t for (i = 0; i < (size_t)count; i++) { jobj = translate_atomic_rbuf(ENVONLY, mem_type_id, type_class, char_buf + i * typeSize); if (jobj) { - if (ret_buflen == 0) + if (retIsList) ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, (jobject)jobj); else ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, (jobject)jobj); @@ -5042,18 +5047,14 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t /* Convert each element to a list of 2 floating-point elements */ for (i = 0; i < (size_t)count; i++) { - found_jList = JNI_TRUE; - jList = NULL; + jList = NULL; /* Get the object element */ memcpy((char *)objBuf, char_buf + i * typeSize, typeSize); - /* The list we're going to return: */ - if (i < (size_t)ret_buflen) { - if (NULL == - (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i))) - found_jList = JNI_FALSE; - } + /* Reuse a pre-allocated slot if the caller supplied one. */ + if (i < (size_t)ret_buflen) + jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); if (NULL == jList) { if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) @@ -5062,7 +5063,7 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t } translate_rbuf(ENVONLY, jList, memb, base_class, (jsize)typeCount, objBuf); - if (found_jList == JNI_FALSE) + if (retIsList) ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, jList); else ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, jList); From 2adb07a836c8e8b164e2738e5f032e88fc0af654 Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Tue, 26 May 2026 11:25:05 -0500 Subject: [PATCH 7/9] Clang format --- java/src-jni/jni/h5util.c | 14 +++-- java/src-jni/test/TestH5D.java | 97 +++++++++++++--------------------- 2 files changed, 47 insertions(+), 64 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index c312dc027a3e..71b5bcc06a12 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -4834,7 +4834,7 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t int ret_buflen = -1; jboolean retIsList = JNI_FALSE; jobjectArray jList = NULL; - jobject jobj = NULL; + jobject jobj = NULL; H5T_class_t vlClass; size_t vlSize; size_t i, x; @@ -5124,7 +5124,8 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t for (i = 0; i < (size_t)count; i++) { hvl_t vl_elem; - if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { + if (NULL == + (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); } @@ -5158,7 +5159,8 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t case H5T_COMPOUND: { /* Convert each list to a compound element */ for (i = 0; i < (size_t)count; i++) { - if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { + if (NULL == + (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); } @@ -5215,7 +5217,8 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t /* Convert each list to an array element */ for (i = 0; i < (size_t)count; i++) { - if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { + if (NULL == + (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); } @@ -5269,7 +5272,8 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t /* Convert each list to an array element */ for (i = 0; i < (size_t)count; i++) { - if (NULL == (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { + if (NULL == + (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); } diff --git a/java/src-jni/test/TestH5D.java b/java/src-jni/test/TestH5D.java index 8c1ac523c537..d8f5ce2b2411 100644 --- a/java/src-jni/test/TestH5D.java +++ b/java/src-jni/test/TestH5D.java @@ -2054,10 +2054,8 @@ public void testH5Dread_compound_of_vlen() assertNotNull("testH5Dread_compound_of_vlen: read_data[0] not null", read_data[0]); assertNotNull("testH5Dread_compound_of_vlen: read_data[1] not null", read_data[1]); - assertEquals("testH5Dread_compound_of_vlen: row 0 record has 2 members", 2, - read_data[0].size()); - assertEquals("testH5Dread_compound_of_vlen: row 1 record has 2 members", 2, - read_data[1].size()); + assertEquals("testH5Dread_compound_of_vlen: row 0 record has 2 members", 2, read_data[0].size()); + assertEquals("testH5Dread_compound_of_vlen: row 1 record has 2 members", 2, read_data[1].size()); Object seq0obj = read_data[0].get(0); Object seq1obj = read_data[1].get(0); @@ -2072,20 +2070,13 @@ public void testH5Dread_compound_of_vlen() ArrayList seq1_read = (ArrayList)seq1obj; assertEquals("testH5Dread_compound_of_vlen: row 0 seq length", 3, seq0_read.size()); assertEquals("testH5Dread_compound_of_vlen: row 1 seq length", 2, seq1_read.size()); - assertEquals("testH5Dread_compound_of_vlen: row 0 seq[0]", Integer.valueOf(1), - seq0_read.get(0)); - assertEquals("testH5Dread_compound_of_vlen: row 0 seq[1]", Integer.valueOf(2), - seq0_read.get(1)); - assertEquals("testH5Dread_compound_of_vlen: row 0 seq[2]", Integer.valueOf(3), - seq0_read.get(2)); - assertEquals("testH5Dread_compound_of_vlen: row 1 seq[0]", Integer.valueOf(5), - seq1_read.get(0)); - assertEquals("testH5Dread_compound_of_vlen: row 1 seq[1]", Integer.valueOf(6), - seq1_read.get(1)); - assertEquals("testH5Dread_compound_of_vlen: row 0 n", Integer.valueOf(4), - read_data[0].get(1)); - assertEquals("testH5Dread_compound_of_vlen: row 1 n", Integer.valueOf(7), - read_data[1].get(1)); + assertEquals("testH5Dread_compound_of_vlen: row 0 seq[0]", Integer.valueOf(1), seq0_read.get(0)); + assertEquals("testH5Dread_compound_of_vlen: row 0 seq[1]", Integer.valueOf(2), seq0_read.get(1)); + assertEquals("testH5Dread_compound_of_vlen: row 0 seq[2]", Integer.valueOf(3), seq0_read.get(2)); + assertEquals("testH5Dread_compound_of_vlen: row 1 seq[0]", Integer.valueOf(5), seq1_read.get(0)); + assertEquals("testH5Dread_compound_of_vlen: row 1 seq[1]", Integer.valueOf(6), seq1_read.get(1)); + assertEquals("testH5Dread_compound_of_vlen: row 0 n", Integer.valueOf(4), read_data[0].get(1)); + assertEquals("testH5Dread_compound_of_vlen: row 1 n", Integer.valueOf(7), read_data[1].get(1)); } catch (Throwable err) { err.printStackTrace(); @@ -2122,8 +2113,7 @@ public void testH5Dread_compound_of_vlen_preallocated() try { dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_rd_prealloc"); file_type_id = H5.H5Dget_type(dset_id); - assertTrue("testH5Dread_compound_of_vlen_preallocated: H5Dget_type: ", - file_type_id >= 0); + assertTrue("testH5Dread_compound_of_vlen_preallocated: H5Dget_type: ", file_type_id >= 0); ArrayList[] read_data = new ArrayList[N_ROWS]; for (int i = 0; i < N_ROWS; i++) @@ -2142,10 +2132,9 @@ public void testH5Dread_compound_of_vlen_preallocated() (seq0obj == null ? "null" : seq0obj.getClass().getName()), seq0obj instanceof ArrayList); ArrayList seq0_read = (ArrayList)seq0obj; - assertEquals("testH5Dread_compound_of_vlen_preallocated: row 0 seq length", 3, - seq0_read.size()); - assertEquals("testH5Dread_compound_of_vlen_preallocated: row 0 seq[0]", - Integer.valueOf(1), seq0_read.get(0)); + assertEquals("testH5Dread_compound_of_vlen_preallocated: row 0 seq length", 3, seq0_read.size()); + assertEquals("testH5Dread_compound_of_vlen_preallocated: row 0 seq[0]", Integer.valueOf(1), + seq0_read.get(0)); } catch (Throwable err) { err.printStackTrace(); @@ -2190,29 +2179,20 @@ public void testH5Dwrite_compound_of_vlen() assertNotNull("testH5Dwrite_compound_of_vlen: read_data[0] not null", read_data[0]); assertNotNull("testH5Dwrite_compound_of_vlen: read_data[1] not null", read_data[1]); - assertEquals("testH5Dwrite_compound_of_vlen: row 0 record has 2 members", 2, - read_data[0].size()); - assertEquals("testH5Dwrite_compound_of_vlen: row 1 record has 2 members", 2, - read_data[1].size()); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 record has 2 members", 2, read_data[0].size()); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 record has 2 members", 2, read_data[1].size()); ArrayList seq0_read = (ArrayList)read_data[0].get(0); ArrayList seq1_read = (ArrayList)read_data[1].get(0); assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq length", 3, seq0_read.size()); assertEquals("testH5Dwrite_compound_of_vlen: row 1 seq length", 2, seq1_read.size()); - assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[0]", Integer.valueOf(1), - seq0_read.get(0)); - assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[1]", Integer.valueOf(2), - seq0_read.get(1)); - assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[2]", Integer.valueOf(3), - seq0_read.get(2)); - assertEquals("testH5Dwrite_compound_of_vlen: row 1 seq[0]", Integer.valueOf(5), - seq1_read.get(0)); - assertEquals("testH5Dwrite_compound_of_vlen: row 1 seq[1]", Integer.valueOf(6), - seq1_read.get(1)); - assertEquals("testH5Dwrite_compound_of_vlen: row 0 n", Integer.valueOf(4), - read_data[0].get(1)); - assertEquals("testH5Dwrite_compound_of_vlen: row 1 n", Integer.valueOf(7), - read_data[1].get(1)); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[0]", Integer.valueOf(1), seq0_read.get(0)); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[1]", Integer.valueOf(2), seq0_read.get(1)); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 seq[2]", Integer.valueOf(3), seq0_read.get(2)); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 seq[0]", Integer.valueOf(5), seq1_read.get(0)); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 seq[1]", Integer.valueOf(6), seq1_read.get(1)); + assertEquals("testH5Dwrite_compound_of_vlen: row 0 n", Integer.valueOf(4), read_data[0].get(1)); + assertEquals("testH5Dwrite_compound_of_vlen: row 1 n", Integer.valueOf(7), read_data[1].get(1)); } catch (Throwable err) { err.printStackTrace(); @@ -2243,10 +2223,10 @@ public void testH5Dwrite_compound_of_vlen() @Test public void testH5Dread_vlen_of_compound_nullslots() { - long cmpd_tid = HDF5Constants.H5I_INVALID_HID; - long vlen_tid = HDF5Constants.H5I_INVALID_HID; - long dspace_id = HDF5Constants.H5I_INVALID_HID; - long dset_id = HDF5Constants.H5I_INVALID_HID; + long cmpd_tid = HDF5Constants.H5I_INVALID_HID; + long vlen_tid = HDF5Constants.H5I_INVALID_HID; + long dspace_id = HDF5Constants.H5I_INVALID_HID; + long dset_id = HDF5Constants.H5I_INVALID_HID; final int N_ROWS = 2; try { @@ -2257,13 +2237,12 @@ public void testH5Dread_vlen_of_compound_nullslots() H5.H5Tinsert(cmpd_tid, "B", intSize, HDF5Constants.H5T_NATIVE_INT); H5.H5Tpack(cmpd_tid); - vlen_tid = H5.H5Tvlen_create(cmpd_tid); + vlen_tid = H5.H5Tvlen_create(cmpd_tid); long[] dims = {N_ROWS}; - dspace_id = H5.H5Screate_simple(1, dims, null); + dspace_id = H5.H5Screate_simple(1, dims, null); - dset_id = H5.H5Dcreate(H5fid, "vlen_of_cmpd_rd", vlen_tid, dspace_id, - HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, - HDF5Constants.H5P_DEFAULT); + dset_id = H5.H5Dcreate(H5fid, "vlen_of_cmpd_rd", vlen_tid, dspace_id, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT); assertTrue("testH5Dread_vlen_of_compound_nullslots: H5Dcreate: ", dset_id >= 0); // row 0: 1 element [ { A=1, B=2 } ] @@ -2358,11 +2337,11 @@ public void testH5Dread_vlen_of_compound_nullslots() @Test public void testH5Dread_vlen_of_nested_compound() { - long inner_tid = HDF5Constants.H5I_INVALID_HID; - long outer_tid = HDF5Constants.H5I_INVALID_HID; - long vlen_tid = HDF5Constants.H5I_INVALID_HID; - long dspace_id = HDF5Constants.H5I_INVALID_HID; - long dset_id = HDF5Constants.H5I_INVALID_HID; + long inner_tid = HDF5Constants.H5I_INVALID_HID; + long outer_tid = HDF5Constants.H5I_INVALID_HID; + long vlen_tid = HDF5Constants.H5I_INVALID_HID; + long dspace_id = HDF5Constants.H5I_INVALID_HID; + long dset_id = HDF5Constants.H5I_INVALID_HID; final int N_ROWS = 2; try { @@ -2386,9 +2365,9 @@ public void testH5Dread_vlen_of_nested_compound() long[] dims = {N_ROWS}; dspace_id = H5.H5Screate_simple(1, dims, null); - dset_id = H5.H5Dcreate(H5fid, "vlen_of_nested_cmpd", vlen_tid, dspace_id, - HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, - HDF5Constants.H5P_DEFAULT); + dset_id = + H5.H5Dcreate(H5fid, "vlen_of_nested_cmpd", vlen_tid, dspace_id, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT); assertTrue("testH5Dread_vlen_of_nested_compound: H5Dcreate: ", dset_id >= 0); // row 0: 1 element [ { id=1, sub={ P=2, Q=3 } } ] From 5ef4411811ad51c690f7ce17f054dc8778e352c9 Mon Sep 17 00:00:00 2001 From: Matthew Larson Date: Wed, 27 May 2026 11:43:28 -0500 Subject: [PATCH 8/9] Fix readVL/writeVL crash on malformed buffer --- java/src-jni/jni/h5util.c | 55 +++++- java/src-jni/test/TestH5D.java | 160 ++++++++++++++++++ java/src-jni/test/testfiles/JUnit-TestH5D.txt | 4 +- java/test/testfiles/JUnit-TestH5D.txt | 15 +- 4 files changed, 222 insertions(+), 12 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index 71b5bcc06a12..8a375bf4107d 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -4476,7 +4476,14 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo /* Convert each element */ if (is_variable) { char **var_str_buf = (char **)raw_buf; - if (NULL == (jobj = ENVPTR->NewStringUTF(ENVONLY, *var_str_buf))) { + /* Passing NULL to NewStringUTF leads to a crash. A NULL pointer here + * usually means H5Dread did not fill this slot (e.g., if + * caller passed an over-sized buffer). If this happens, return null + * rather than dereferencing. */ + if (*var_str_buf == NULL) { + jobj = NULL; + } + else if (NULL == (jobj = ENVPTR->NewStringUTF(ENVONLY, *var_str_buf))) { CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE); H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_atomic_rbuf: out of memory - unable to " "construct string from UTF characters"); @@ -4886,6 +4893,9 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t if (i < (size_t)ret_buflen) jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); + if (jList != NULL && !ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) + H5_BAD_ARGUMENT_ERROR(ENVONLY, + "translate_rbuf: VLEN slot is not a java.util.ArrayList"); if (NULL == jList) { if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) @@ -4915,6 +4925,9 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t if (i < (size_t)ret_buflen) { jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); } + if (jList != NULL && !ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) + H5_BAD_ARGUMENT_ERROR(ENVONLY, + "translate_rbuf: COMPOUND slot is not a java.util.ArrayList"); if (NULL == jList) { if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) @@ -4981,9 +4994,15 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t /* Get the object element */ memcpy((char *)objBuf, char_buf + i * typeSize, typeSize); - /* Reuse a pre-allocated slot if the caller supplied one. */ - if (i < (size_t)ret_buflen) - jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); + /* The list we're going to return: */ + if (i < (size_t)ret_buflen) { + if (NULL == + (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i))) + found_jList = JNI_FALSE; + } + if (jList != NULL && !ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) + H5_BAD_ARGUMENT_ERROR(ENVONLY, + "translate_rbuf: ARRAY slot is not a java.util.ArrayList"); if (NULL == jList) { if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) @@ -5052,9 +5071,15 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t /* Get the object element */ memcpy((char *)objBuf, char_buf + i * typeSize, typeSize); - /* Reuse a pre-allocated slot if the caller supplied one. */ - if (i < (size_t)ret_buflen) - jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); + /* The list we're going to return: */ + if (i < (size_t)ret_buflen) { + if (NULL == + (jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i))) + found_jList = JNI_FALSE; + } + if (jList != NULL && !ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) + H5_BAD_ARGUMENT_ERROR(ENVONLY, + "translate_rbuf: COMPLEX slot is not a java.util.ArrayList"); if (NULL == jList) { if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) @@ -5130,6 +5155,10 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); } + if (!ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) + H5_BAD_ARGUMENT_ERROR(ENVONLY, + "translate_wbuf: VLEN slot is not a java.util.ArrayList"); + /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); @@ -5165,6 +5194,10 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); } + if (!ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) + H5_BAD_ARGUMENT_ERROR(ENVONLY, + "translate_wbuf: COMPOUND slot is not a java.util.ArrayList"); + int nmembs = H5Tget_nmembers(mem_type_id); /* invoke the toArray method */ @@ -5223,6 +5256,10 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); } + if (!ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) + H5_BAD_ARGUMENT_ERROR(ENVONLY, + "translate_wbuf: ARRAY slot is not a java.util.ArrayList"); + /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); @@ -5278,6 +5315,10 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t H5_NULL_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: in_buf element is NULL"); } + if (!ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) + H5_BAD_ARGUMENT_ERROR(ENVONLY, + "translate_wbuf: COMPLEX slot is not a java.util.ArrayList"); + /* invoke the toArray method */ if (mToArray == NULL) CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE); diff --git a/java/src-jni/test/TestH5D.java b/java/src-jni/test/TestH5D.java index d8f5ce2b2411..a7e3757e6f71 100644 --- a/java/src-jni/test/TestH5D.java +++ b/java/src-jni/test/TestH5D.java @@ -1757,6 +1757,166 @@ public void testH5DArray_string_buffer() throws Throwable arr_str_data[3].get(0).equals(arr_readbuf[3].get(0))); } + /* + * Verify H5DreadVL safe throws a Java exception + * when called with a malformed buffer shape for an ARRAY-of-varstr dataset. + */ + @Test + public void testH5DArray_string_buffer_flat_StringArray() throws Throwable + { + String dset_str_name = "ArrayStringdata_flat"; + long dset_str_id = HDF5Constants.H5I_INVALID_HID; + long dtype_str_id = HDF5Constants.H5I_INVALID_HID; + long varstr_id = HDF5Constants.H5I_INVALID_HID; + long dspace_id = HDF5Constants.H5I_INVALID_HID; + long[] strdims = {3}; + long[] dims = {2}; + long lsize = 1; + + String[] row0 = {"a", "bb", "ccc"}; + String[] row1 = {"dd", "ee", "fff"}; + + ArrayList[] arr_str_data = new ArrayList[2]; + arr_str_data[0] = new ArrayList(Arrays.asList(row0)); + arr_str_data[1] = new ArrayList(Arrays.asList(row1)); + + try { + varstr_id = H5.H5Tcopy(HDF5Constants.H5T_C_S1); + H5.H5Tset_size(varstr_id, HDF5Constants.H5T_VARIABLE); + dtype_str_id = H5.H5Tarray_create(varstr_id, 1, strdims); + + dspace_id = H5.H5Screate_simple(1, dims, null); + dset_str_id = H5.H5Dcreate(H5fid, dset_str_name, dtype_str_id, dspace_id, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT); + H5.H5DwriteVL(dset_str_id, dtype_str_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, arr_str_data); + H5.H5Fflush(H5fid, HDF5Constants.H5F_SCOPE_LOCAL); + + for (int j = 0; j < dims.length; j++) + lsize *= dims[j]; + + // Malformed buffer: a flat String[dims*array_dim], not + // ArrayList[dims] of array_dim Strings. + int flatLen = (int)lsize * (int)strdims[0]; + String[] badBuf = new String[flatLen]; + for (int j = 0; j < flatLen; j++) + badBuf[j] = ""; + + // The JNI must not segfault here regardless of what badBuf looks + // like. Either it correctly populates the slots or it throws. + try { + H5.H5DreadVL(dset_str_id, dtype_str_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, badBuf); + } + catch (Exception ex) { + // Accepted outcome: graceful Java-level error. + return; + } + + assertNotNull("badBuf[0] should not be null after H5DreadVL", badBuf[0]); + } + finally { + if (dset_str_id > 0) + try { + H5.H5Dclose(dset_str_id); + } + catch (Exception ex) { + } + if (dspace_id > 0) + try { + H5.H5Sclose(dspace_id); + } + catch (Exception ex) { + } + if (dtype_str_id > 0) + try { + H5.H5Tclose(dtype_str_id); + } + catch (Exception ex) { + } + if (varstr_id > 0) + try { + H5.H5Tclose(varstr_id); + } + catch (Exception ex) { + } + } + } + + /* + * Verify H5DwriteVL safely throws a Java exception + * when called with a malformed buffer shape for an ARRAY-of-varstr dataset. + */ + @Test + public void testH5DArray_string_buffer_flat_StringArray_write() throws Throwable + { + String dset_str_name = "ArrayStringdata_flat_write"; + long dset_str_id = HDF5Constants.H5I_INVALID_HID; + long dtype_str_id = HDF5Constants.H5I_INVALID_HID; + long varstr_id = HDF5Constants.H5I_INVALID_HID; + long dspace_id = HDF5Constants.H5I_INVALID_HID; + long[] strdims = {3}; + long[] dims = {2}; + + try { + varstr_id = H5.H5Tcopy(HDF5Constants.H5T_C_S1); + H5.H5Tset_size(varstr_id, HDF5Constants.H5T_VARIABLE); + dtype_str_id = H5.H5Tarray_create(varstr_id, 1, strdims); + + dspace_id = H5.H5Screate_simple(1, dims, null); + dset_str_id = H5.H5Dcreate(H5fid, dset_str_name, dtype_str_id, dspace_id, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT); + + // Malformed buffer: a flat String[dims*array_dim], not + // ArrayList[dims] of array_dim Strings. + int flatLen = (int)dims[0] * (int)strdims[0]; + String[] flatBuf = new String[flatLen]; + for (int j = 0; j < flatLen; j++) + flatBuf[j] = "s" + j; + + try { + H5.H5DwriteVL(dset_str_id, dtype_str_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, + HDF5Constants.H5P_DEFAULT, flatBuf); + } + catch (Exception ex) { + // Accepted outcome: graceful Java-level error. + return; + } + + // If we reach here, no exception was thrown. Worst case is the JVM + // segfaults before this line. If it accepted the write silently, + // that itself indicates the JNI doesn't validate the buffer shape. + } + finally { + if (dset_str_id > 0) + try { + H5.H5Dclose(dset_str_id); + } + catch (Exception ex) { + } + if (dspace_id > 0) + try { + H5.H5Sclose(dspace_id); + } + catch (Exception ex) { + } + if (dtype_str_id > 0) + try { + H5.H5Tclose(dtype_str_id); + } + catch (Exception ex) { + } + if (varstr_id > 0) + try { + H5.H5Tclose(varstr_id); + } + catch (Exception ex) { + } + } + } + @Test public void testH5DArrayenum_rw() { diff --git a/java/src-jni/test/testfiles/JUnit-TestH5D.txt b/java/src-jni/test/testfiles/JUnit-TestH5D.txt index 95bc0eb874dc..484395c54269 100644 --- a/java/src-jni/test/testfiles/JUnit-TestH5D.txt +++ b/java/src-jni/test/testfiles/JUnit-TestH5D.txt @@ -16,7 +16,9 @@ JUnit version 4.13.2 .testH5Diterate .testH5Dget_access_plist .testH5Dread_vlen_of_nested_compound +.testH5DArray_string_buffer_flat_StringArray .testH5Dget_space_closed +.testH5DArray_string_buffer_flat_StringArray_write .testH5DArray_string_buffer .testH5Dread_vlen_of_compound_nullslots .testH5Dread_compound_of_vlen @@ -29,5 +31,5 @@ JUnit version 4.13.2 Time: XXXX -OK (27 tests) +OK (29 tests) diff --git a/java/test/testfiles/JUnit-TestH5D.txt b/java/test/testfiles/JUnit-TestH5D.txt index a451d9671841..484395c54269 100644 --- a/java/test/testfiles/JUnit-TestH5D.txt +++ b/java/test/testfiles/JUnit-TestH5D.txt @@ -3,26 +3,33 @@ JUnit version 4.13.2 .testH5DVLwrVL .testH5Dget_storage_size .testH5DArraywr +.testH5Diterate_write .testH5Dcreate .testH5Dget_offset -I.testH5Dget_type +.testH5Dget_type .testH5DVLwr .testH5Dfill .testH5Dopen .testH5Dcreate_anon .testH5Dfill_null .testH5Dget_storage_size_empty +.testH5Diterate .testH5Dget_access_plist -.testH5Dvlen_get_buf_size +.testH5Dread_vlen_of_nested_compound +.testH5DArray_string_buffer_flat_StringArray .testH5Dget_space_closed +.testH5DArray_string_buffer_flat_StringArray_write .testH5DArray_string_buffer +.testH5Dread_vlen_of_compound_nullslots +.testH5Dread_compound_of_vlen .testH5Dget_space_status .testH5Dvlen_write_read +.testH5Dread_compound_of_vlen_preallocated .testH5Dget_space .testH5Dget_type_closed -.testH5Dwrite_readCompound +.testH5Dwrite_compound_of_vlen Time: XXXX -OK (22 tests) +OK (29 tests) From e13ea89befae35b7d52c6c6186700effdf643193 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 May 2026 16:15:13 +0000 Subject: [PATCH 9/9] Committing clang-format changes --- java/src-jni/jni/h5util.c | 14 +++++--------- java/src-jni/test/TestH5D.java | 16 ++++++++-------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/java/src-jni/jni/h5util.c b/java/src-jni/jni/h5util.c index 8a375bf4107d..1f2525b6f7d4 100644 --- a/java/src-jni/jni/h5util.c +++ b/java/src-jni/jni/h5util.c @@ -4478,7 +4478,7 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo char **var_str_buf = (char **)raw_buf; /* Passing NULL to NewStringUTF leads to a crash. A NULL pointer here * usually means H5Dread did not fill this slot (e.g., if - * caller passed an over-sized buffer). If this happens, return null + * caller passed an over-sized buffer). If this happens, return null * rather than dereferencing. */ if (*var_str_buf == NULL) { jobj = NULL; @@ -4894,8 +4894,7 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t if (i < (size_t)ret_buflen) jList = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)ret_buf, (jsize)i); if (jList != NULL && !ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) - H5_BAD_ARGUMENT_ERROR(ENVONLY, - "translate_rbuf: VLEN slot is not a java.util.ArrayList"); + H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_rbuf: VLEN slot is not a java.util.ArrayList"); if (NULL == jList) { if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) @@ -5001,8 +5000,7 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t found_jList = JNI_FALSE; } if (jList != NULL && !ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) - H5_BAD_ARGUMENT_ERROR(ENVONLY, - "translate_rbuf: ARRAY slot is not a java.util.ArrayList"); + H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_rbuf: ARRAY slot is not a java.util.ArrayList"); if (NULL == jList) { if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0))) @@ -5156,8 +5154,7 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t } if (!ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) - H5_BAD_ARGUMENT_ERROR(ENVONLY, - "translate_wbuf: VLEN slot is not a java.util.ArrayList"); + H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: VLEN slot is not a java.util.ArrayList"); /* invoke the toArray method */ if (mToArray == NULL) @@ -5257,8 +5254,7 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t } if (!ENVPTR->IsInstanceOf(ENVONLY, jList, arrCList)) - H5_BAD_ARGUMENT_ERROR(ENVONLY, - "translate_wbuf: ARRAY slot is not a java.util.ArrayList"); + H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_wbuf: ARRAY slot is not a java.util.ArrayList"); /* invoke the toArray method */ if (mToArray == NULL) diff --git a/java/src-jni/test/TestH5D.java b/java/src-jni/test/TestH5D.java index a7e3757e6f71..e8a58589a3c3 100644 --- a/java/src-jni/test/TestH5D.java +++ b/java/src-jni/test/TestH5D.java @@ -1785,10 +1785,10 @@ public void testH5DArray_string_buffer_flat_StringArray() throws Throwable H5.H5Tset_size(varstr_id, HDF5Constants.H5T_VARIABLE); dtype_str_id = H5.H5Tarray_create(varstr_id, 1, strdims); - dspace_id = H5.H5Screate_simple(1, dims, null); - dset_str_id = H5.H5Dcreate(H5fid, dset_str_name, dtype_str_id, dspace_id, - HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, - HDF5Constants.H5P_DEFAULT); + dspace_id = H5.H5Screate_simple(1, dims, null); + dset_str_id = + H5.H5Dcreate(H5fid, dset_str_name, dtype_str_id, dspace_id, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT); H5.H5DwriteVL(dset_str_id, dtype_str_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL, HDF5Constants.H5P_DEFAULT, arr_str_data); H5.H5Fflush(H5fid, HDF5Constants.H5F_SCOPE_LOCAL); @@ -1864,10 +1864,10 @@ public void testH5DArray_string_buffer_flat_StringArray_write() throws Throwable H5.H5Tset_size(varstr_id, HDF5Constants.H5T_VARIABLE); dtype_str_id = H5.H5Tarray_create(varstr_id, 1, strdims); - dspace_id = H5.H5Screate_simple(1, dims, null); - dset_str_id = H5.H5Dcreate(H5fid, dset_str_name, dtype_str_id, dspace_id, - HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT, - HDF5Constants.H5P_DEFAULT); + dspace_id = H5.H5Screate_simple(1, dims, null); + dset_str_id = + H5.H5Dcreate(H5fid, dset_str_name, dtype_str_id, dspace_id, HDF5Constants.H5P_DEFAULT, + HDF5Constants.H5P_DEFAULT, HDF5Constants.H5P_DEFAULT); // Malformed buffer: a flat String[dims*array_dim], not // ArrayList[dims] of array_dim Strings.