Fix bad reads of nested cmpd/vlen types in JNI (#6413)

* Fix H5DreadVL failing for pre-allocate cmpd-of-seq dsets

* Fix bad vlen of cmpd with null slot read

* 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`.

* Add exception checks

* Update NULL checks in translate_wbuf

* Correct potentially bad array length check

* Clang format

* Fix readVL/writeVL crash on malformed buffer

* Committing clang-format changes

* Add bufSize checks to wbuf/rbuf translation

* Remove vlen pre-allocation support

* Harden JNI buffer interface

* Handle opaque types as byte[] and document JNI buffer data model

Opaque elements were grouped with H5T_INTEGER in the nested-type
translation path, which boxed them as Integer/Long and rejected
arbitrary-sized opaque blobs. Treat H5T_OPAQUE like H5T_REFERENCE
(a byte[] per element) in translate_atomic_rbuf, translate_atomic_wbuf,
and h5validate_atomic_wbuf so nested opaque round-trips correctly.

Also add "Buffer data model" header comments on translate_rbuf() and
translate_wbuf() and note the reference/opaque byte[] leaves in the
H5.java javadocv.

* Initialize typeSize to fix -Werror=maybe-uninitialized

typeSize was assigned only inside the vl_data_class branch but read in
a second, separate vl_data_class branch, which gcc -O2 flags as
maybe-uninitialized under -Werror. Initialize it to 0 at declaration in
H5Aread/H5Awrite/H5Dread/H5Dwrite, matching the existing vl_array_len
pattern.

* Port nested cmpd/vlen tests to java/test and sync reference

The legacy java/test tree's JUnit-TestH5D.txt reference listed the new
nested compound/vlen tests, but the corresponding @Test methods existed
only in java/src-jni/test/TestH5D.java. Port the 10 tests and the
writeCompoundOfVlenDataset helper into java/test/TestH5D.java, remove
debug prints, and
regenerate the reference to match the actual JUnit output.

* Support nested vlen/compound datatypes in Java FFM compat layer

The FFM compatibility layer (java/hdf) lacked the vlen/compound read and
write support that the JNI interface gained, so the nested cmpd/vlen tests
ported into java/test (TestH5D) failed and leaked an id.

VLDataConverter now has recursive encodeValue/decodeValue helpers that pack
and unpack any member class (integer, float, fixed/vl string, nested
compound, and VLEN) in the native HDF5 in-memory layout. These are wired
into convertCompoundDatatype, readCompoundDatatype and convertRawDataToArrayList,
and a type-aware convertToHVLAuto handles top-level VLEN-of-compound writes.
Compound reads now reclaim VL memory, and type/count mismatches raise
IllegalArgumentException instead of silently corrupting data.

H5DwriteVL rejects an undersized buffer up front and routes VLEN writes
through convertToHVLAuto. The JUnit-TestH5D reference regains its trailing
blank line to match the actual JUnit output.

* Committing clang-format changes

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Matt L
2026-07-06 09:52:29 -05:00
committed by GitHub
co-authored by github-actions
parent f44ad7d57e
commit b58ab3cc1f
13 changed files with 3083 additions and 363 deletions
+28 -2
View File
@@ -5740,6 +5740,30 @@ public class H5 implements java.io.Serializable {
ArrayList[] arrayData = (ArrayList[])buf;
// Reject an undersized buffer up front: the native H5Dwrite uses the
// selection to decide how many elements to read from the buffer, so
// a buffer shorter than the selection would read past the end.
long expected_elems = -1;
try {
if (mem_space_id != HDF5Constants.H5S_ALL)
expected_elems = org.hdfgroup.javahdf5.hdf5_h.H5Sget_select_npoints(mem_space_id);
else if (file_space_id != HDF5Constants.H5S_ALL)
expected_elems = org.hdfgroup.javahdf5.hdf5_h.H5Sget_select_npoints(file_space_id);
else {
long all_space = org.hdfgroup.javahdf5.hdf5_h.H5Dget_space(dataset_id);
if (all_space >= 0) {
expected_elems = org.hdfgroup.javahdf5.hdf5_h.H5Sget_simple_extent_npoints(all_space);
org.hdfgroup.javahdf5.hdf5_h.H5Sclose(all_space);
}
}
}
catch (Exception e) {
expected_elems = -1;
}
if (expected_elems >= 0 && buf.length < expected_elems)
throw new IllegalArgumentException("H5DwriteVL: data buffer has " + buf.length +
" elements but the selection requires " + expected_elems);
// Check the datatype class to determine conversion strategy
int typeClass = H5Tget_class(mem_type_id);
@@ -5762,8 +5786,10 @@ public class H5 implements java.io.Serializable {
file_space_id, xfer_plist_id, stringArray);
}
else {
// For VL datatypes, convert to hvl_t structures
hvlArray = VLDataConverter.convertToHVL(arrayData, arena);
// For VL datatypes, convert to hvl_t structures. convertToHVLAuto uses
// mem_type_id so a VLEN whose base type is a compound is packed in the
// native HDF5 layout instead of being misread as a nested VLEN.
hvlArray = VLDataConverter.convertToHVLAuto(arrayData, mem_type_id, arena);
status = org.hdfgroup.javahdf5.hdf5_h.H5Dwrite(dataset_id, mem_type_id, mem_space_id,
file_space_id, xfer_plist_id, hvlArray);
}
+435 -134
View File
@@ -69,6 +69,81 @@ public class VLDataConverter {
return hvlArray;
}
/**
* Convert an ArrayList array into an hvl_t array for a top-level VLEN datatype,
* using {@code vlenTypeId} to drive the element encoding. When the VLEN base
* type is a compound (or otherwise needs native-layout packing) each element is
* encoded with {@link #encodeValue}; for primitive/string base types the
* type-blind {@link #convertToHVL} path is used unchanged.
*
* @param javaData rows, one ArrayList per VLEN element list
* @param vlenTypeId the H5T_VLEN datatype identifier
* @param arena arena for memory allocation
* @return MemorySegment containing the hvl_t array
* @throws HDF5JavaException if conversion fails
*/
public static MemorySegment convertToHVLAuto(ArrayList[] javaData, long vlenTypeId, Arena arena)
throws HDF5JavaException
{
long base = HDF5Constants.H5I_INVALID_HID;
boolean useTyped = false;
try {
if (org.hdfgroup.javahdf5.hdf5_h.H5Tget_class(vlenTypeId) == HDF5Constants.H5T_VLEN) {
base = org.hdfgroup.javahdf5.hdf5_h.H5Tget_super(vlenTypeId);
useTyped = (org.hdfgroup.javahdf5.hdf5_h.H5Tget_class(base) == HDF5Constants.H5T_COMPOUND);
}
}
catch (Exception e) {
useTyped = false;
}
try {
if (!useTyped) {
return convertToHVL(javaData, arena);
}
if (javaData == null || javaData.length == 0)
throw new HDF5JavaException("Input data array is null or empty");
long baseSize = org.hdfgroup.javahdf5.hdf5_h.H5Tget_size(base);
MemorySegment hvlArray = hvl_t.allocateArray(javaData.length, arena);
for (int i = 0; i < javaData.length; i++) {
MemorySegment hvlElement = hvl_t.asSlice(hvlArray, i);
ArrayList<?> row = javaData[i];
if (row == null) {
hvl_t.len(hvlElement, 0);
hvl_t.p(hvlElement, MemorySegment.NULL);
continue;
}
int len = row.size();
hvl_t.len(hvlElement, len);
if (len == 0) {
hvl_t.p(hvlElement, MemorySegment.NULL);
continue;
}
MemorySegment elemBuf = arena.allocate(baseSize * len);
for (int j = 0; j < len; j++)
encodeValue(elemBuf, j * baseSize, base, row.get(j), arena);
hvl_t.p(hvlElement, elemBuf);
}
return hvlArray;
}
finally {
if (base >= 0) {
try {
org.hdfgroup.javahdf5.hdf5_h.H5Tclose(base);
}
catch (Exception e) {
}
}
}
}
/**
* Convert HDF5 hvl_t MemorySegment array back to Java ArrayList array.
* Uses two-phase approach: immediately extract all raw data from HDF5 memory,
@@ -1028,6 +1103,9 @@ public class VLDataConverter {
else if (isVLType(elementType)) {
return convertRawDataToNestedVLList(rawData, elementType);
}
else if (isCompoundType(elementType)) {
return convertRawDataToCompoundList(rawData, elementType);
}
else {
return detectAndConvertUnknownType(rawData, elementType);
}
@@ -1244,6 +1322,32 @@ public class VLDataConverter {
return result;
}
/**
* Decode raw bytes holding {@code rawData.length} packed compound elements (the
* element type of a VLEN-of-compound) into a list of per-element ArrayLists.
* Each element is decoded with {@link #decodeValue}, so nested compounds and
* compound members of any class are handled recursively.
*/
private static ArrayList<Object> convertRawDataToCompoundList(RawVLData rawData, long compoundType)
throws HDF5JavaException
{
ArrayList<Object> result = new ArrayList<>(rawData.length);
long compoundSize = org.hdfgroup.javahdf5.hdf5_h.H5Tget_size(compoundType);
if (compoundSize <= 0)
throw new HDF5JavaException("Invalid compound element size: " + compoundSize);
MemorySegment seg = MemorySegment.ofArray(rawData.data);
for (int i = 0; i < rawData.length; i++) {
long elemOff = (long)i * compoundSize;
if (elemOff + compoundSize > rawData.data.length) {
result.add(null);
continue;
}
result.add(decodeValue(seg, elemOff, compoundType));
}
return result;
}
/**
* Detect and convert unknown HDF5 datatypes by examining the raw data
*/
@@ -1848,6 +1952,285 @@ public class VLDataConverter {
}
}
/**
* @return true if the datatype's class is H5T_COMPOUND
*/
private static boolean isCompoundType(long datatype)
{
try {
return H5.H5Tget_class(datatype) == HDF5Constants.H5T_COMPOUND;
}
catch (Exception e) {
return false;
}
}
/**
* Encode a single Java value into {@code dst} at byte offset {@code off}, laid
* out exactly as HDF5 represents a value of {@code typeId} in memory. Handles
* integers, floats, fixed/variable-length strings, nested compounds and VLEN
* members (recursively), which is what allows compounds containing VLEN fields
* and VLEN-of-compound elements to round-trip through the native library.
*
* <p>A value whose Java type does not match the HDF5 member type (for example a
* String supplied for an integer member, or a non-ArrayList supplied for a VLEN
* or compound member) is rejected with {@link IllegalArgumentException} instead
* of silently writing garbage.</p>
*/
private static void encodeValue(MemorySegment dst, long off, long typeId, Object value, Arena arena)
throws HDF5JavaException
{
int cls = org.hdfgroup.javahdf5.hdf5_h.H5Tget_class(typeId);
long size = org.hdfgroup.javahdf5.hdf5_h.H5Tget_size(typeId);
if (cls == HDF5Constants.H5T_INTEGER) {
long v;
if (value instanceof Integer)
v = ((Integer)value).longValue();
else if (value instanceof Long)
v = (Long)value;
else if (value instanceof Short)
v = ((Short)value).longValue();
else if (value instanceof Byte)
v = ((Byte)value).longValue();
else
throw new IllegalArgumentException("integer member requires an Integer/Long value, got " +
(value == null ? "null" : value.getClass().getName()));
for (int b = 0; b < size; b++)
dst.set(ValueLayout.JAVA_BYTE, off + b, (byte)((v >> (b * 8)) & 0xFF));
}
else if (cls == HDF5Constants.H5T_FLOAT) {
if (!(value instanceof Double) && !(value instanceof Float))
throw new IllegalArgumentException("float member requires a Double/Float value, got " +
(value == null ? "null" : value.getClass().getName()));
double d = (value instanceof Double) ? (Double)value : ((Float)value).doubleValue();
if (size == 4) {
int bits = Float.floatToRawIntBits((float)d);
for (int b = 0; b < 4; b++)
dst.set(ValueLayout.JAVA_BYTE, off + b, (byte)((bits >> (b * 8)) & 0xFF));
}
else {
long bits = Double.doubleToRawLongBits(d);
for (int b = 0; b < size; b++)
dst.set(ValueLayout.JAVA_BYTE, off + b, (byte)((bits >> (b * 8)) & 0xFF));
}
}
else if (cls == HDF5Constants.H5T_STRING) {
String strValue;
if (value instanceof String)
strValue = (String)value;
else if (value == null)
strValue = "";
else
throw new IllegalArgumentException("string member requires a String value, got " +
value.getClass().getName());
byte[] strBytes = strValue.getBytes(StandardCharsets.UTF_8);
if (org.hdfgroup.javahdf5.hdf5_h.H5Tis_variable_str(typeId) > 0) {
// Variable-length string - store as a char* allocated by HDF5.
MemorySegment hdf5StringMem =
org.hdfgroup.javahdf5.hdf5_h.H5allocate_memory(strBytes.length + 1, false);
if (hdf5StringMem == null || hdf5StringMem.equals(MemorySegment.NULL))
throw new HDF5JavaException("Failed to allocate HDF5 memory for string: " + strValue);
MemorySegment boundedMem =
hdf5StringMem.reinterpret(strBytes.length + 1, Arena.global(), null);
boundedMem.copyFrom(MemorySegment.ofArray(strBytes));
boundedMem.set(ValueLayout.JAVA_BYTE, strBytes.length, (byte)0);
writeAddress(dst, off, boundedMem.address());
}
else {
// Fixed-length string - copy bytes directly and zero-pad.
int copyLen = (int)Math.min(strBytes.length, size);
for (int j = 0; j < copyLen; j++)
dst.set(ValueLayout.JAVA_BYTE, off + j, strBytes[j]);
for (long j = copyLen; j < size; j++)
dst.set(ValueLayout.JAVA_BYTE, off + j, (byte)0);
}
}
else if (cls == HDF5Constants.H5T_VLEN) {
if (!(value instanceof ArrayList))
throw new IllegalArgumentException("VLEN member requires an ArrayList value, got " +
(value == null ? "null" : value.getClass().getName()));
ArrayList<?> list = (ArrayList<?>)value;
long base = org.hdfgroup.javahdf5.hdf5_h.H5Tget_super(typeId);
try {
long baseSize = org.hdfgroup.javahdf5.hdf5_h.H5Tget_size(base);
int len = list.size();
// hvl_t { size_t len; void *p; } written field-by-field to tolerate
// unaligned compound member offsets.
for (int b = 0; b < 8; b++)
dst.set(ValueLayout.JAVA_BYTE, off + b, (byte)(((long)len >> (b * 8)) & 0xFF));
if (len == 0) {
writeAddress(dst, off + 8, 0L);
}
else {
MemorySegment elemBuf = arena.allocate(baseSize * len);
for (int i = 0; i < len; i++)
encodeValue(elemBuf, i * baseSize, base, list.get(i), arena);
writeAddress(dst, off + 8, elemBuf.address());
}
}
finally {
try {
org.hdfgroup.javahdf5.hdf5_h.H5Tclose(base);
}
catch (Exception e) {
}
}
}
else if (cls == HDF5Constants.H5T_COMPOUND) {
if (!(value instanceof ArrayList))
throw new IllegalArgumentException("compound member requires an ArrayList value, got " +
(value == null ? "null" : value.getClass().getName()));
ArrayList<?> rec = (ArrayList<?>)value;
int nm = org.hdfgroup.javahdf5.hdf5_h.H5Tget_nmembers(typeId);
if (rec.size() != nm)
throw new IllegalArgumentException("compound value has " + rec.size() +
" members, expected " + nm);
for (int i = 0; i < nm; i++) {
long mt = org.hdfgroup.javahdf5.hdf5_h.H5Tget_member_type(typeId, i);
long mo = org.hdfgroup.javahdf5.hdf5_h.H5Tget_member_offset(typeId, i);
try {
encodeValue(dst, off + mo, mt, rec.get(i), arena);
}
finally {
try {
org.hdfgroup.javahdf5.hdf5_h.H5Tclose(mt);
}
catch (Exception e) {
}
}
}
}
else {
throw new HDF5JavaException("Unsupported compound/VLEN member type class: " + cls);
}
}
/**
* Decode a single value of HDF5 type {@code typeId} from {@code src} at byte
* offset {@code off}. Inverse of {@link #encodeValue}. Returns Integer, Double,
* String, or (for VLEN/compound members) a nested ArrayList.
*/
private static Object decodeValue(MemorySegment src, long off, long typeId) throws HDF5JavaException
{
int cls = org.hdfgroup.javahdf5.hdf5_h.H5Tget_class(typeId);
long size = org.hdfgroup.javahdf5.hdf5_h.H5Tget_size(typeId);
if (cls == HDF5Constants.H5T_INTEGER) {
long v = 0;
for (int b = 0; b < size && b < 8; b++)
v |= ((long)(src.get(ValueLayout.JAVA_BYTE, off + b) & 0xFF)) << (b * 8);
if (size <= 4) {
// sign-extend from the most-significant decoded byte
int iv = (int)v;
if (size < 4) {
int shift = (int)(8 * (4 - size));
iv = (iv << shift) >> shift;
}
return Integer.valueOf(iv);
}
return Long.valueOf(v);
}
else if (cls == HDF5Constants.H5T_FLOAT) {
if (size == 4) {
int bits = 0;
for (int b = 0; b < 4; b++)
bits |= (src.get(ValueLayout.JAVA_BYTE, off + b) & 0xFF) << (b * 8);
return Double.valueOf(Float.intBitsToFloat(bits));
}
long bits = 0;
for (int b = 0; b < size && b < 8; b++)
bits |= ((long)(src.get(ValueLayout.JAVA_BYTE, off + b) & 0xFF)) << (b * 8);
return Double.valueOf(Double.longBitsToDouble(bits));
}
else if (cls == HDF5Constants.H5T_STRING) {
if (org.hdfgroup.javahdf5.hdf5_h.H5Tis_variable_str(typeId) > 0) {
long addr = readAddress(src, off);
if (addr == 0L)
return "";
try {
MemorySegment p = MemorySegment.ofAddress(addr).reinterpret(Long.MAX_VALUE);
return p.getString(0, StandardCharsets.UTF_8);
}
catch (Exception e) {
return "";
}
}
byte[] strBytes = new byte[(int)size];
for (int j = 0; j < size; j++)
strBytes[j] = src.get(ValueLayout.JAVA_BYTE, off + j);
String strValue = new String(strBytes, StandardCharsets.UTF_8);
int nullIdx = strValue.indexOf('\0');
if (nullIdx >= 0)
strValue = strValue.substring(0, nullIdx);
return strValue;
}
else if (cls == HDF5Constants.H5T_VLEN) {
long len = 0;
for (int b = 0; b < 8; b++)
len |= ((long)(src.get(ValueLayout.JAVA_BYTE, off + b) & 0xFF)) << (b * 8);
long addr = readAddress(src, off + 8);
ArrayList<Object> list = new ArrayList<>((int)len);
if (len == 0 || addr == 0L)
return list;
long base = org.hdfgroup.javahdf5.hdf5_h.H5Tget_super(typeId);
try {
long baseSize = org.hdfgroup.javahdf5.hdf5_h.H5Tget_size(base);
MemorySegment el = MemorySegment.ofAddress(addr).reinterpret(baseSize * len);
for (long i = 0; i < len; i++)
list.add(decodeValue(el, i * baseSize, base));
}
finally {
try {
org.hdfgroup.javahdf5.hdf5_h.H5Tclose(base);
}
catch (Exception e) {
}
}
return list;
}
else if (cls == HDF5Constants.H5T_COMPOUND) {
int nm = org.hdfgroup.javahdf5.hdf5_h.H5Tget_nmembers(typeId);
ArrayList<Object> rec = new ArrayList<>(nm);
for (int i = 0; i < nm; i++) {
long mt = org.hdfgroup.javahdf5.hdf5_h.H5Tget_member_type(typeId, i);
long mo = org.hdfgroup.javahdf5.hdf5_h.H5Tget_member_offset(typeId, i);
try {
rec.add(decodeValue(src, off + mo, mt));
}
finally {
try {
org.hdfgroup.javahdf5.hdf5_h.H5Tclose(mt);
}
catch (Exception e) {
}
}
}
return rec;
}
else {
throw new HDF5JavaException("Unsupported compound/VLEN member type class for read: " + cls);
}
}
/** Write an 8-byte native pointer value little-endian, tolerating unaligned offsets. */
private static void writeAddress(MemorySegment dst, long off, long addr)
{
for (int b = 0; b < 8; b++)
dst.set(ValueLayout.JAVA_BYTE, off + b, (byte)((addr >> (b * 8)) & 0xFF));
}
/** Read an 8-byte native pointer value little-endian, tolerating unaligned offsets. */
private static long readAddress(MemorySegment src, long off)
{
long addr = 0;
for (int b = 0; b < 8; b++)
addr |= ((long)(src.get(ValueLayout.JAVA_BYTE, off + b) & 0xFF)) << (b * 8);
return addr;
}
/**
* Convert ArrayList array with heterogeneous types to compound datatype buffer
* Used for H5T_COMPOUND datatypes where each ArrayList contains mixed field types
@@ -1899,83 +2282,20 @@ public class VLDataConverter {
ArrayList<?> record = data[structIdx];
if (record == null || record.size() != nmembers) {
throw new HDF5JavaException("ArrayList at index " + structIdx + " has " +
(record == null ? "null" : record.size()) +
" elements, expected " + nmembers);
throw new IllegalArgumentException("compound record at index " + structIdx + " has " +
(record == null ? "null" : record.size()) +
" members, expected " + nmembers);
}
long structOffset = structIdx * compoundSize;
// Pack each field into the compound structure
// Pack each field into the compound structure. encodeValue handles
// every member class (including nested compounds and VLEN members)
// and matches the native HDF5 in-memory layout.
for (int fieldIdx = 0; fieldIdx < nmembers; fieldIdx++) {
Object fieldValue = record.get(fieldIdx);
long fieldOffset = structOffset + memberOffsets[fieldIdx];
int memberClass = memberClasses[fieldIdx];
long memberSize = memberSizes[fieldIdx];
boolean isVLString = isVLStrings[fieldIdx];
if (memberClass == HDF5Constants.H5T_INTEGER) {
// Integer field - write bytes for unaligned HDF5 compound offsets
int intValue = (fieldValue instanceof Integer) ? (Integer)fieldValue : 0;
buffer.set(ValueLayout.JAVA_BYTE, fieldOffset, (byte)(intValue & 0xFF));
buffer.set(ValueLayout.JAVA_BYTE, fieldOffset + 1,
(byte)((intValue >> 8) & 0xFF));
buffer.set(ValueLayout.JAVA_BYTE, fieldOffset + 2,
(byte)((intValue >> 16) & 0xFF));
buffer.set(ValueLayout.JAVA_BYTE, fieldOffset + 3,
(byte)((intValue >> 24) & 0xFF));
}
else if (memberClass == HDF5Constants.H5T_FLOAT) {
// Double field - write bytes for unaligned HDF5 compound offsets
double doubleValue = (fieldValue instanceof Double) ? (Double)fieldValue : 0.0;
long longBits = Double.doubleToRawLongBits(doubleValue);
for (int byteIdx = 0; byteIdx < 8; byteIdx++) {
buffer.set(ValueLayout.JAVA_BYTE, fieldOffset + byteIdx,
(byte)((longBits >> (byteIdx * 8)) & 0xFF));
}
}
else if (memberClass == HDF5Constants.H5T_STRING) {
if (isVLString) {
// Variable-length string - store as pointer
String strValue = (fieldValue instanceof String) ? (String)fieldValue : "";
byte[] strBytes = strValue.getBytes(StandardCharsets.UTF_8);
// Allocate with HDF5's memory allocator for VL strings
MemorySegment hdf5StringMem = org.hdfgroup.javahdf5.hdf5_h.H5allocate_memory(
strBytes.length + 1, false);
if (hdf5StringMem == null || hdf5StringMem.equals(MemorySegment.NULL)) {
throw new HDF5JavaException(
"Failed to allocate HDF5 memory for string: " + strValue);
}
MemorySegment boundedMem =
hdf5StringMem.reinterpret(strBytes.length + 1, Arena.global(), null);
boundedMem.copyFrom(MemorySegment.ofArray(strBytes));
boundedMem.set(ValueLayout.JAVA_BYTE, strBytes.length, (byte)0);
// Store pointer
buffer.set(ValueLayout.ADDRESS, fieldOffset, boundedMem);
}
else {
// Fixed-length string - copy bytes directly
String strValue = (fieldValue instanceof String) ? (String)fieldValue : "";
byte[] strBytes = strValue.getBytes(StandardCharsets.UTF_8);
int copyLen = (int)Math.min(strBytes.length, memberSize);
// Copy string bytes
for (int j = 0; j < copyLen; j++) {
buffer.set(ValueLayout.JAVA_BYTE, fieldOffset + j, strBytes[j]);
}
// Pad with zeros
for (int j = copyLen; j < memberSize; j++) {
buffer.set(ValueLayout.JAVA_BYTE, fieldOffset + j, (byte)0);
}
}
}
else {
throw new HDF5JavaException("Unsupported compound member type class: " +
memberClass);
}
Object fieldValue = record.get(fieldIdx);
long fieldOffset = structOffset + memberOffsets[fieldIdx];
encodeValue(buffer, fieldOffset, memberTypeIds[fieldIdx], fieldValue, arena);
}
}
}
@@ -1999,6 +2319,11 @@ public class VLDataConverter {
if (e instanceof HDF5JavaException) {
throw e;
}
// Let validation failures (wrong member count/type) surface as
// IllegalArgumentException to the caller instead of being masked.
if (e instanceof RuntimeException) {
throw (RuntimeException)e;
}
throw new HDF5JavaException("Compound datatype conversion failed: " + e.getMessage());
}
}
@@ -2077,75 +2402,51 @@ public class VLDataConverter {
ArrayList<Object> record = new ArrayList<>();
long structOffset = structIdx * compoundSize;
// Read each field from the compound structure
// Read each field from the compound structure. decodeValue handles
// every member class (including nested compounds and VLEN members)
// and matches the native HDF5 in-memory layout.
for (int fieldIdx = 0; fieldIdx < nmembers; fieldIdx++) {
long fieldOffset = structOffset + memberOffsets[fieldIdx];
int memberClass = memberClasses[fieldIdx];
long memberSize = memberSizes[fieldIdx];
boolean isVLString = isVLStrings[fieldIdx];
if (memberClass == HDF5Constants.H5T_INTEGER) {
// Read integer field (little-endian byte order)
int intValue =
(buffer.get(ValueLayout.JAVA_BYTE, fieldOffset) & 0xFF) |
((buffer.get(ValueLayout.JAVA_BYTE, fieldOffset + 1) & 0xFF) << 8) |
((buffer.get(ValueLayout.JAVA_BYTE, fieldOffset + 2) & 0xFF) << 16) |
(buffer.get(ValueLayout.JAVA_BYTE, fieldOffset + 3) << 24);
record.add(intValue);
}
else if (memberClass == HDF5Constants.H5T_FLOAT) {
// Read double field (8 bytes, little-endian)
long longBits = 0;
for (int byteIdx = 0; byteIdx < 8; byteIdx++) {
long byteVal =
buffer.get(ValueLayout.JAVA_BYTE, fieldOffset + byteIdx) & 0xFFL;
longBits |= (byteVal << (byteIdx * 8));
}
double doubleValue = Double.longBitsToDouble(longBits);
record.add(doubleValue);
}
else if (memberClass == HDF5Constants.H5T_STRING) {
if (isVLString) {
// Variable-length string - read pointer
MemorySegment stringPtr = buffer.get(ValueLayout.ADDRESS, fieldOffset);
if (stringPtr != null && !stringPtr.equals(MemorySegment.NULL)) {
try {
String str = stringPtr.getString(0, StandardCharsets.UTF_8);
record.add(str);
}
catch (Exception e) {
record.add("");
}
}
else {
record.add("");
}
}
else {
// Fixed-length string - read bytes
byte[] strBytes = new byte[(int)memberSize];
for (int j = 0; j < memberSize; j++) {
strBytes[j] = buffer.get(ValueLayout.JAVA_BYTE, fieldOffset + j);
}
// Convert to string and trim null terminators
String strValue = new String(strBytes, StandardCharsets.UTF_8);
int nullIdx = strValue.indexOf('\0');
if (nullIdx >= 0) {
strValue = strValue.substring(0, nullIdx);
}
record.add(strValue);
}
}
else {
throw new HDF5JavaException("Unsupported compound member type class for read: " +
memberClass);
}
long fieldOffset = structOffset + memberOffsets[fieldIdx];
record.add(decodeValue(buffer, fieldOffset, memberTypeIds[fieldIdx]));
}
result[structIdx] = record;
}
}
finally {
// If the compound contains any VLEN data, HDF5 allocated memory for it
// while filling the read buffer; reclaim it now that the values have
// been copied into the returned ArrayLists.
try {
if (hdf.hdf5lib.H5.H5Tdetect_class(mem_type_id, HDF5Constants.H5T_VLEN)) {
long reclaim_space = HDF5Constants.H5I_INVALID_HID;
try {
reclaim_space =
isDataset ? org.hdfgroup.javahdf5.hdf5_h.H5Dget_space(attr_or_dataset_id)
: org.hdfgroup.javahdf5.hdf5_h.H5Aget_space(attr_or_dataset_id);
if (reclaim_space >= 0) {
org.hdfgroup.javahdf5.hdf5_h.H5Treclaim(
mem_type_id, reclaim_space, org.hdfgroup.javahdf5.hdf5_h.H5P_DEFAULT(),
buffer);
}
}
finally {
if (reclaim_space >= 0) {
try {
org.hdfgroup.javahdf5.hdf5_h.H5Sclose(reclaim_space);
}
catch (Exception e) {
// Ignore close errors
}
}
}
}
}
catch (Exception reclaimEx) {
System.err.println("Warning: H5Treclaim failed in readCompoundDatatype: " +
reclaimEx.getMessage());
}
// Close member type IDs
for (int i = 0; i < nmembers; i++) {
if (memberTypeIds[i] >= 0) {
+26 -4
View File
@@ -194,6 +194,22 @@ import org.slf4j.LoggerFactory;
* and the parameter <i>data</i> can be any multi-dimensional array of numbers, such as float[][], or
* int[][][], or Double[][].
* <p>
* <b>Buffer data model</b>
* <p>
* Read/write buffers must match the memory datatype. The JNI verifies this at the API boundary and throws
* IllegalArgumentException on a mismatch instead of crashing. The expected Java buffer per datatype class is:
* <ul>
* <li>integer/enum/bitfield, float: a primitive array (byte/short/int/long/float/double[]) or byte[]; it must
* be large enough to hold one element of the memory type per selected point.</li>
* <li>fixed- or variable-length string, reference: a String[] (byte[] for references) with one slot per
* selected point.</li>
* <li>compound, variable-length sequence, array, complex: an Object[] of nested java.util.ArrayLists. Each
* element is an ArrayList; a compound is an ArrayList of its members in order, a VLEN/array/complex is an
* ArrayList of its elements, and scalar leaves are the boxed type (Integer, Double, String, ...) except
* reference and opaque leaves, which are a byte[] holding the element's raw bytes. Slots are
* not pre-allocated by the caller on read.</li>
* </ul>
* <p>
* <b>@ref HDF5CONST</b>
* <p>
* The HDF5 API defines a set of constants and enumerated values. Most of these values are available to Java
@@ -1681,7 +1697,8 @@ public class H5 implements java.io.Serializable {
* @param mem_type_id
* IN: Identifier of the attribute datatype (in memory).
* @param buf
* Buffer of variable-lenght to store data read from the file.
* Object[] (one slot per element) to store the data read; each slot is filled with a nested
* ArrayList structure matching mem_type_id (see "Buffer data model" in the class description).
*
* @return a non-negative value if successful
*
@@ -2263,7 +2280,8 @@ public class H5 implements java.io.Serializable {
* @param mem_type_id
* IN: Identifier of the attribute datatype (in memory).
* @param buf
* IN: Buffer of variable-lenght with data to be written to the file.
* IN: Object[] (one slot per element) holding the data to write; each slot must be a nested
* ArrayList structure matching mem_type_id (see "Buffer data model" in the class description).
*
* @return a non-negative value if successful
*
@@ -3425,7 +3443,9 @@ public class H5 implements java.io.Serializable {
* @param xfer_plist_id
* Identifier of a transfer property list for this I/O operation.
* @param buf
* Buffer of variable-lenght to store data read from the file.
* Object[] (one slot per selected point) to store the data read; each slot is filled with a
* nested ArrayList structure matching mem_type_id (see "Buffer data model" in the class
* description). Slots need not be pre-allocated.
*
* @return a non-negative value if successful
*
@@ -4145,7 +4165,9 @@ public class H5 implements java.io.Serializable {
* @param xfer_plist_id
* Identifier of a transfer property list for this I/O operation.
* @param buf
* Buffer of variable-length with data to be written to the file.
* Object[] (one slot per selected point) holding the data to write; each slot must be a nested
* ArrayList structure matching mem_type_id (see "Buffer data model" in the class description).
* A structural mismatch raises IllegalArgumentException.
*
* @return a non-negative value if successful
*
+119 -11
View File
@@ -152,8 +152,8 @@ Java_hdf_hdf5lib_H5_H5Aread(JNIEnv *env, jclass clss, jlong attr_id, jlong mem_t
jboolean readBufIsCopy;
jbyte *readBuf = NULL;
hsize_t dims[H5S_MAX_RANK];
hid_t sid = H5I_INVALID_HID;
size_t typeSize;
hid_t sid = H5I_INVALID_HID;
size_t typeSize = 0; // Only used by vl_data_class types
H5T_class_t type_class;
jsize vl_array_len = 0; // Only used by vl_data_class types
htri_t vl_data_class;
@@ -167,6 +167,12 @@ Java_hdf_hdf5lib_H5_H5Aread(JNIEnv *env, jclass clss, jlong attr_id, jlong mem_t
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* For fixed-length data the byte buffer must cover the attribute. */
if (!vl_data_class &&
h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
sizeof(jbyte), "H5Aread") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((vl_array_len = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0)
@@ -195,7 +201,8 @@ Java_hdf_hdf5lib_H5_H5Aread(JNIEnv *env, jclass clss, jlong attr_id, jlong mem_t
if ((type_class = H5Tget_class((hid_t)mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
translate_rbuf(env, buf, mem_type_id, type_class, vl_array_len, readBuf);
translate_rbuf(env, buf, mem_type_id, type_class, vl_array_len, readBuf,
(size_t)vl_array_len * typeSize);
}
done:
@@ -239,8 +246,8 @@ Java_hdf_hdf5lib_H5_H5Awrite(JNIEnv *env, jclass clss, jlong attr_id, jlong mem_
jboolean writeBufIsCopy;
jbyte *writeBuf = NULL;
hsize_t dims[H5S_MAX_RANK];
hid_t sid = H5I_INVALID_HID;
size_t typeSize;
hid_t sid = H5I_INVALID_HID;
size_t typeSize = 0; // Only used by vl_data_class types
H5T_class_t type_class;
jsize vl_array_len = 0; // Only used by vl_data_class types
htri_t vl_data_class;
@@ -254,6 +261,12 @@ Java_hdf_hdf5lib_H5_H5Awrite(JNIEnv *env, jclass clss, jlong attr_id, jlong mem_
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* For fixed-length data the byte buffer must cover the attribute. */
if (!vl_data_class &&
h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
sizeof(jbyte), "H5Awrite") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((vl_array_len = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0) {
@@ -281,7 +294,8 @@ Java_hdf_hdf5lib_H5_H5Awrite(JNIEnv *env, jclass clss, jlong attr_id, jlong mem_
if ((type_class = H5Tget_class((hid_t)mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
translate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len, writeBuf);
translate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len, writeBuf,
(size_t)vl_array_len * typeSize);
}
if ((status = H5Awrite((hid_t)attr_id, (hid_t)mem_type_id, writeBuf)) < 0)
@@ -338,6 +352,12 @@ Java_hdf_hdf5lib_H5_H5Aread_1short(JNIEnv *env, jclass clss, jlong attr_id, jlon
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* Verify the buffer is large enough for the attribute. */
if (!vl_data_class &&
h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
sizeof(jshort), "H5Aread_short") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((n = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0) {
@@ -408,6 +428,11 @@ Java_hdf_hdf5lib_H5_H5Awrite_1short(JNIEnv *env, jclass clss, jlong attr_id, jlo
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread: readBuf length < 0");
}
/* Verify the buffer is large enough for the attribute. */
if (h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, n, sizeof(jshort), "H5Awrite_short") <
0)
goto done;
dims[0] = (hsize_t)n;
if ((sid = H5Screate_simple(1, dims, NULL)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -467,6 +492,12 @@ Java_hdf_hdf5lib_H5_H5Aread_1int(JNIEnv *env, jclass clss, jlong attr_id, jlong
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* Verify the buffer is large enough for the attribute. */
if (!vl_data_class &&
h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
sizeof(jint), "H5Aread_int") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((n = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0) {
@@ -537,6 +568,10 @@ Java_hdf_hdf5lib_H5_H5Awrite_1int(JNIEnv *env, jclass clss, jlong attr_id, jlong
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread: readBuf length < 0");
}
/* Verify the buffer is large enough for the attribute. */
if (h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, n, sizeof(jint), "H5Awrite_int") < 0)
goto done;
dims[0] = (hsize_t)n;
if ((sid = H5Screate_simple(1, dims, NULL)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -596,6 +631,12 @@ Java_hdf_hdf5lib_H5_H5Aread_1long(JNIEnv *env, jclass clss, jlong attr_id, jlong
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* Verify the buffer is large enough for the attribute. */
if (!vl_data_class &&
h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
sizeof(jlong), "H5Aread_long") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((n = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0) {
@@ -666,6 +707,10 @@ Java_hdf_hdf5lib_H5_H5Awrite_1long(JNIEnv *env, jclass clss, jlong attr_id, jlon
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread: readBuf length < 0");
}
/* Verify the buffer is large enough for the attribute. */
if (h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, n, sizeof(jlong), "H5Awrite_long") < 0)
goto done;
dims[0] = (hsize_t)n;
if ((sid = H5Screate_simple(1, dims, NULL)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -725,6 +770,12 @@ Java_hdf_hdf5lib_H5_H5Aread_1float(JNIEnv *env, jclass clss, jlong attr_id, jlon
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* Verify the buffer is large enough for the attribute. */
if (!vl_data_class &&
h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
sizeof(jfloat), "H5Aread_float") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((n = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0) {
@@ -795,6 +846,11 @@ Java_hdf_hdf5lib_H5_H5Awrite_1float(JNIEnv *env, jclass clss, jlong attr_id, jlo
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread: readBuf length < 0");
}
/* Verify the buffer is large enough for the attribute. */
if (h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, n, sizeof(jfloat), "H5Awrite_float") <
0)
goto done;
dims[0] = (hsize_t)n;
if ((sid = H5Screate_simple(1, dims, NULL)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -854,6 +910,12 @@ Java_hdf_hdf5lib_H5_H5Aread_1double(JNIEnv *env, jclass clss, jlong attr_id, jlo
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* Verify the buffer is large enough for the attribute. */
if (!vl_data_class &&
h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
sizeof(jdouble), "H5Aread_double") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((n = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0) {
@@ -923,6 +985,11 @@ Java_hdf_hdf5lib_H5_H5Awrite_1double(JNIEnv *env, jclass clss, jlong attr_id, jl
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Awrite_double: buf length < 0");
}
/* Verify the buffer is large enough for the attribute. */
if (h5a_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)attr_id, n, sizeof(jdouble), "H5Awrite_double") <
0)
goto done;
dims[0] = (hsize_t)n;
if ((sid = H5Screate_simple(1, dims, NULL)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -984,6 +1051,10 @@ Java_hdf_hdf5lib_H5_H5Aread_1string(JNIEnv *env, jclass clss, jlong attr_id, jlo
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread_string: read buffer length <= 0");
}
/* The buffer must have one slot per element in the attribute. */
if (h5a_validate_slot_buf(env, (hid_t)attr_id, n, "H5Aread_string") < 0)
goto done;
if (!(str_len = H5Tget_size((hid_t)mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
@@ -1049,6 +1120,10 @@ Java_hdf_hdf5lib_H5_H5Awrite_1string(JNIEnv *env, jclass clss, jlong attr_id, jl
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Awrite_string: write buffer length <= 0");
}
/* The buffer must have one slot per element in the attribute. */
if (h5a_validate_slot_buf(env, (hid_t)attr_id, n, "H5Awrite_string") < 0)
goto done;
if (!(str_len = H5Tget_size((hid_t)mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
@@ -1107,6 +1182,7 @@ Java_hdf_hdf5lib_H5_H5AreadVL(JNIEnv *env, jclass clss, jlong attr_id, jlong mem
size_t typeSize;
H5T_class_t type_class;
jsize vl_array_len = 0;
hssize_t npoints;
htri_t vl_data_class;
herr_t status = FAIL;
htri_t is_variable = 0;
@@ -1124,6 +1200,12 @@ Java_hdf_hdf5lib_H5_H5AreadVL(JNIEnv *env, jclass clss, jlong attr_id, jlong mem
if ((is_variable = H5Tis_variable_str(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* The buffer must hold at least one slot per point in the attribute. */
if ((npoints = h5a_io_npoints(env, (hid_t)attr_id)) < 0)
goto done;
if ((hssize_t)vl_array_len < npoints)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5AreadVL: read buffer is smaller than the attribute");
if (!(typeSize = H5Tget_size(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
@@ -1135,7 +1217,7 @@ Java_hdf_hdf5lib_H5_H5AreadVL(JNIEnv *env, jclass clss, jlong attr_id, jlong mem
if ((type_class = H5Tget_class((hid_t)mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
translate_rbuf(env, buf, mem_type_id, type_class, vl_array_len, readBuf);
translate_rbuf(env, buf, mem_type_id, type_class, vl_array_len, readBuf, (size_t)vl_array_len * typeSize);
done:
if (readBuf) {
@@ -1173,6 +1255,7 @@ Java_hdf_hdf5lib_H5_H5AwriteVL(JNIEnv *env, jclass clss, jlong attr_id, jlong me
size_t typeSize;
H5T_class_t type_class;
jsize vl_array_len = 0;
hssize_t npoints;
htri_t vl_data_class;
herr_t status = FAIL;
htri_t is_variable = 0;
@@ -1193,16 +1276,27 @@ Java_hdf_hdf5lib_H5_H5AwriteVL(JNIEnv *env, jclass clss, jlong attr_id, jlong me
if ((is_variable = H5Tis_variable_str(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* Verify the buffer holds at least one element per point in the attribute. */
if ((npoints = h5a_io_npoints(env, (hid_t)attr_id)) < 0)
goto done;
if ((hssize_t)vl_array_len < npoints)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5AwriteVL: write buffer is smaller than the attribute");
if (!(typeSize = H5Tget_size(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
if (NULL == (writeBuf = calloc((size_t)vl_array_len, typeSize)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "H5Awrite: failed to allocate raw VL write buffer");
if ((type_class = H5Tget_class((hid_t)mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
translate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len, writeBuf);
/* Verify the buffer structure matches mem_type_id before converting it. */
if (h5validate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len) < 0)
goto done;
if (NULL == (writeBuf = calloc((size_t)vl_array_len, typeSize)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "H5Awrite: failed to allocate raw VL write buffer");
translate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len, writeBuf,
(size_t)vl_array_len * typeSize);
if ((status = H5Awrite((hid_t)attr_id, (hid_t)mem_type_id, writeBuf)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -1250,6 +1344,11 @@ Java_hdf_hdf5lib_H5_H5Aread_1VLStrings(JNIEnv *env, jclass clss, jlong attr_id,
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Aread_VLStrings: read buffer is NULL");
/* The buffer must have one slot per element in the attribute. */
if (h5a_validate_slot_buf(env, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
"H5Aread_VLStrings") < 0)
goto done;
if ((isStr = H5Tdetect_class((hid_t)mem_type_id, H5T_STRING)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -1455,6 +1554,11 @@ Java_hdf_hdf5lib_H5_H5Awrite_1VLStrings(JNIEnv *env, jclass clss, jlong attr_id,
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Awrite_VLStrings: write buffer is NULL");
/* The buffer must have one slot per element in the attribute. */
if (h5a_validate_slot_buf(env, (hid_t)attr_id, ENVPTR->GetArrayLength(ENVONLY, buf),
"H5Awrite_VLStrings") < 0)
goto done;
if ((isStr = H5Tdetect_class((hid_t)mem_type_id, H5T_STRING)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -1690,6 +1794,10 @@ Java_hdf_hdf5lib_H5_H5Aread_1reg_1ref(JNIEnv *env, jclass clss, jlong attr_id, j
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread_reg_ref: buf length < 0");
}
/* The buffer must have one slot per element in the attribute. */
if (h5a_validate_slot_buf(env, (hid_t)attr_id, n, "H5Aread_reg_ref") < 0)
goto done;
if (NULL == (ref_data = (H5R_ref_t *)calloc(1, (size_t)n * sizeof(H5R_ref_t))))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "H5Aread_reg_ref: failed to allocate read buffer");
+121 -59
View File
@@ -176,8 +176,8 @@ Java_hdf_hdf5lib_H5_H5Dread(JNIEnv *env, jclass clss, jlong dataset_id, jlong me
jboolean isCriticalPinning)
{
jboolean readBufIsCopy;
jbyte *readBuf = NULL;
size_t typeSize;
jbyte *readBuf = NULL;
size_t typeSize = 0; // Only used by vl_data_class types
H5T_class_t type_class;
jsize vl_array_len = 0; // Only used by vl_data_class types
htri_t vl_data_class;
@@ -191,6 +191,13 @@ Java_hdf_hdf5lib_H5_H5Dread(JNIEnv *env, jclass clss, jlong dataset_id, jlong me
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* For fixed-length data the byte buffer must cover the selection. */
if (!vl_data_class &&
h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jbyte),
"H5Dread") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((vl_array_len = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0) {
@@ -222,7 +229,8 @@ Java_hdf_hdf5lib_H5_H5Dread(JNIEnv *env, jclass clss, jlong dataset_id, jlong me
if ((type_class = H5Tget_class((hid_t)mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
translate_rbuf(env, buf, mem_type_id, type_class, vl_array_len, readBuf);
translate_rbuf(env, buf, mem_type_id, type_class, vl_array_len, readBuf,
(size_t)vl_array_len * typeSize);
}
done:
@@ -258,7 +266,7 @@ Java_hdf_hdf5lib_H5_H5Dwrite(JNIEnv *env, jclass clss, jlong dataset_id, jlong m
{
jboolean writeBufIsCopy;
jbyte *writeBuf = NULL;
size_t typeSize;
size_t typeSize = 0; // Only used by vl_data_class types
H5T_class_t type_class;
jsize vl_array_len = 0; // Only used by vl_data_class types
htri_t vl_data_class;
@@ -272,6 +280,13 @@ Java_hdf_hdf5lib_H5_H5Dwrite(JNIEnv *env, jclass clss, jlong dataset_id, jlong m
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* For fixed-length data the byte buffer must cover the selection. */
if (!vl_data_class &&
h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jbyte),
"H5Dwrite") < 0)
goto done;
if (vl_data_class) {
/* Get size of data array */
if ((vl_array_len = ENVPTR->GetArrayLength(ENVONLY, buf)) < 0) {
@@ -299,7 +314,8 @@ Java_hdf_hdf5lib_H5_H5Dwrite(JNIEnv *env, jclass clss, jlong dataset_id, jlong m
if ((type_class = H5Tget_class((hid_t)mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
translate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len, writeBuf);
translate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len, writeBuf,
(size_t)vl_array_len * typeSize);
}
if ((status = H5Dwrite((hid_t)dataset_id, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
@@ -430,11 +446,11 @@ Java_hdf_hdf5lib_H5_H5Dread_1short(JNIEnv *env, jclass clss, jlong dataset_id, j
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dread_short: read buffer is NULL");
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jshort),
"H5Dread_short") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -487,11 +503,11 @@ Java_hdf_hdf5lib_H5_H5Dwrite_1short(JNIEnv *env, jclass clss, jlong dataset_id,
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dwrite_short: write buffer is NULL");
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jshort),
"H5Dwrite_short") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -544,11 +560,11 @@ Java_hdf_hdf5lib_H5_H5Dread_1int(JNIEnv *env, jclass clss, jlong dataset_id, jlo
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dread_int: read buffer is NULL");
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jint),
"H5Dread_int") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -601,11 +617,11 @@ Java_hdf_hdf5lib_H5_H5Dwrite_1int(JNIEnv *env, jclass clss, jlong dataset_id, jl
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dwrite_int: write buffer is NULL");
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jint),
"H5Dwrite_int") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -658,11 +674,11 @@ Java_hdf_hdf5lib_H5_H5Dread_1long(JNIEnv *env, jclass clss, jlong dataset_id, jl
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dread_long: read buffer is NULL");
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jlong),
"H5Dread_long") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -715,11 +731,11 @@ Java_hdf_hdf5lib_H5_H5Dwrite_1long(JNIEnv *env, jclass clss, jlong dataset_id, j
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dwrite_long: write buffer is NULL");
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jlong),
"H5Dwrite_long") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -772,11 +788,11 @@ Java_hdf_hdf5lib_H5_H5Dread_1float(JNIEnv *env, jclass clss, jlong dataset_id, j
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dread_float: read buffer is NULL");
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jfloat),
"H5Dread_float") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -832,11 +848,11 @@ Java_hdf_hdf5lib_H5_H5Dwrite_1float(JNIEnv *env, jclass clss, jlong dataset_id,
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jfloat),
"H5Dwrite_float") < 0)
goto done;
if (isCriticalPinning) {
PIN_FLOAT_ARRAY_CRITICAL(ENVONLY, buf, writeBuf, &writeBufIsCopy,
@@ -886,11 +902,11 @@ Java_hdf_hdf5lib_H5_H5Dread_1double(JNIEnv *env, jclass clss, jlong dataset_id,
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dread_double: read buffer is NULL");
/* Get size of data array */
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Aread: readBuf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jdouble),
"H5Dread_double") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -943,10 +959,11 @@ Java_hdf_hdf5lib_H5_H5Dwrite_1double(JNIEnv *env, jclass clss, jlong dataset_id,
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Dwrite_double: write buffer is NULL");
if (ENVPTR->GetArrayLength(ENVONLY, buf) < 0) {
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dwrite_double: buf length < 0");
}
/* Verify the buffer is large enough for the selection. */
if (h5d_validate_raw_buf(env, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)dataset_id, ENVPTR->GetArrayLength(ENVONLY, buf), sizeof(jdouble),
"H5Dwrite_double") < 0)
goto done;
if ((vl_data_class = h5str_detect_vlen(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -1007,6 +1024,11 @@ Java_hdf_hdf5lib_H5_H5Dread_1string(JNIEnv *env, jclass clss, jlong dataset_id,
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dread_string: read buffer length <= 0");
}
/* The buffer must have one slot per selected point. */
if (h5d_validate_slot_buf(env, (hid_t)mem_space_id, (hid_t)file_space_id, (hid_t)dataset_id, n,
"H5Dread_string") < 0)
goto done;
if (!(str_len = H5Tget_size((hid_t)mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
@@ -1074,6 +1096,11 @@ Java_hdf_hdf5lib_H5_H5Dwrite_1string(JNIEnv *env, jclass clss, jlong dataset_id,
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dwrite_string: write buffer length <= 0");
}
/* The buffer must have one slot per selected point. */
if (h5d_validate_slot_buf(env, (hid_t)mem_space_id, (hid_t)file_space_id, (hid_t)dataset_id, n,
"H5Dwrite_string") < 0)
goto done;
if (!(str_len = H5Tget_size((hid_t)mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
@@ -1132,6 +1159,7 @@ Java_hdf_hdf5lib_H5_H5DreadVL(JNIEnv *env, jclass clss, jlong dataset_id, jlong
size_t typeSize;
H5T_class_t type_class;
jsize vl_array_len;
hssize_t npoints;
htri_t vl_data_class;
herr_t status = FAIL;
htri_t is_variable = 0;
@@ -1151,6 +1179,13 @@ Java_hdf_hdf5lib_H5_H5DreadVL(JNIEnv *env, jclass clss, jlong dataset_id, jlong
if ((is_variable = H5Tis_variable_str(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* The buffer must hold at least one slot per selected point; otherwise the
* H5Dread below would overrun the raw read buffer. */
if ((npoints = h5d_io_npoints(env, (hid_t)mem_space_id, (hid_t)file_space_id, (hid_t)dataset_id)) < 0)
goto done;
if ((hssize_t)vl_array_len < npoints)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5DreadVL: read buffer is smaller than the selection");
if (!(typeSize = H5Tget_size(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
@@ -1163,7 +1198,7 @@ Java_hdf_hdf5lib_H5_H5DreadVL(JNIEnv *env, jclass clss, jlong dataset_id, jlong
if ((type_class = H5Tget_class((hid_t)mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
translate_rbuf(env, buf, mem_type_id, type_class, vl_array_len, readBuf);
translate_rbuf(env, buf, mem_type_id, type_class, vl_array_len, readBuf, (size_t)vl_array_len * typeSize);
done:
if (readBuf) {
@@ -1192,6 +1227,7 @@ Java_hdf_hdf5lib_H5_H5DwriteVL(JNIEnv *env, jclass clss, jlong dataset_id, jlong
size_t typeSize;
H5T_class_t type_class;
jsize vl_array_len; // Only used by vl_data_class types
hssize_t npoints;
htri_t vl_data_class;
herr_t status = FAIL;
htri_t is_variable = 0;
@@ -1212,16 +1248,27 @@ Java_hdf_hdf5lib_H5_H5DwriteVL(JNIEnv *env, jclass clss, jlong dataset_id, jlong
if ((is_variable = H5Tis_variable_str(mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
/* Verify the buffer holds at least one element per selected point. */
if ((npoints = h5d_io_npoints(env, (hid_t)mem_space_id, (hid_t)file_space_id, (hid_t)dataset_id)) < 0)
goto done;
if ((hssize_t)vl_array_len < npoints)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5DwriteVL: write buffer is smaller than the selection");
if (!(typeSize = H5Tget_size(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
if (NULL == (writeBuf = calloc((size_t)vl_array_len, typeSize)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "H5DwriteVL: failed to allocate raw VL write buffer");
if ((type_class = H5Tget_class((hid_t)mem_type_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
translate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len, writeBuf);
/* Verify the buffer structure matches mem_type_id before converting it. */
if (h5validate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len) < 0)
goto done;
if (NULL == (writeBuf = calloc((size_t)vl_array_len, typeSize)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "H5DwriteVL: failed to allocate raw VL write buffer");
translate_wbuf(ENVONLY, buf, mem_type_id, type_class, vl_array_len, writeBuf,
(size_t)vl_array_len * typeSize);
if ((status = H5Dwrite((hid_t)dataset_id, (hid_t)mem_type_id, (hid_t)mem_space_id, (hid_t)file_space_id,
(hid_t)xfer_plist_id, writeBuf)) < 0)
@@ -1266,6 +1313,11 @@ Java_hdf_hdf5lib_H5_H5Dread_1VLStrings(JNIEnv *env, jclass clss, jlong dataset_i
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5DreadVLStrings: read buffer is NULL");
/* The buffer must have one slot per selected point. */
if (h5d_validate_slot_buf(env, (hid_t)mem_space_id, (hid_t)file_space_id, (hid_t)dataset_id,
ENVPTR->GetArrayLength(ENVONLY, buf), "H5Dread_VLStrings") < 0)
goto done;
if ((isStr = H5Tdetect_class((hid_t)mem_type_id, H5T_STRING)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -1492,6 +1544,11 @@ Java_hdf_hdf5lib_H5_H5Dwrite_1VLStrings(JNIEnv *env, jclass clss, jlong dataset_
if (NULL == buf)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5DwriteVLStrings: write buffer is NULL");
/* The buffer must have one slot per selected point. */
if (h5d_validate_slot_buf(env, (hid_t)mem_space_id, (hid_t)file_space_id, (hid_t)dataset_id,
ENVPTR->GetArrayLength(ENVONLY, buf), "H5Dwrite_VLStrings") < 0)
goto done;
if ((isStr = H5Tdetect_class((hid_t)mem_type_id, H5T_STRING)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
@@ -1739,6 +1796,11 @@ Java_hdf_hdf5lib_H5_H5Dread_1reg_1ref(JNIEnv *env, jclass clss, jlong dataset_id
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Dread_reg_ref: buf length < 0");
}
/* The buffer must have one slot per selected point. */
if (h5d_validate_slot_buf(env, (hid_t)mem_space_id, (hid_t)file_space_id, (hid_t)dataset_id, n,
"H5Dread_reg_ref") < 0)
goto done;
if (NULL == (ref_data = (H5R_ref_t *)calloc(1, (size_t)n * sizeof(H5R_ref_t))))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "H5Dread_reg_ref: failed to allocate read buffer");
+602 -139
View File
@@ -71,9 +71,23 @@ static int render_bin_output_region_data_points(FILE *stream, hid_t region_sp
hsize_t *ptdata);
static int render_bin_output_region_points(FILE *stream, hid_t region_space, hid_t region_id,
hid_t container);
jobject translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, void *raw_buf);
jobject translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, void *raw_buf,
size_t buf_size);
void translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_t type_class,
void *raw_buf);
void *raw_buf, size_t buf_size);
static herr_t h5validate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_t type_class);
/*
* Raises an error if reading/writing LEN bytes at offset OFF would
* overrun a buffer of size BUFSZ. OFF and LEN are byte values,
* BUFSZ is the number of valid bytes at the buffer base.
* WHERE is a string literal naming the calling routine.
* ENV is the JNI environment pointer used to raise the error. */
#define CHECK_RAWBUF_BOUNDS(env, off, len, bufsz, where) \
do { \
if ((size_t)(len) > (size_t)(bufsz) || (size_t)(off) > (size_t)(bufsz) - (size_t)(len)) \
H5_BAD_ARGUMENT_ERROR(env, where ": raw buffer access out of bounds"); \
} while (0)
/* Strings for output */
#define H5_TOOLS_GROUP "GROUP"
@@ -4237,7 +4251,7 @@ done:
} /* end Java_hdf_hdf5lib_H5_H5export_1attribute */
jobject
translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, void *raw_buf)
translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, void *raw_buf, size_t buf_size)
{
jobject jobj = NULL;
hid_t memb = H5I_INVALID_HID;
@@ -4252,6 +4266,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, "<init>", "(I)V");
jmethodID arrAddMethod = ENVPTR->GetMethodID(ENVONLY, arrCList, "add", "(Ljava/lang/Object;)Z");
/* Cache class types */
/* jclass cBool = ENVPTR->FindClass(ENVONLY, "java/lang/Boolean"); */
@@ -4276,6 +4291,10 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo
if (!(typeSize = H5Tget_size(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
/* The element occupies [0, typeSize) of raw_buf, verify it fits. */
if (typeSize > buf_size)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_atomic_rbuf: raw buffer smaller than element");
switch (type_class) {
case H5T_VLEN: {
if (!(memb = H5Tget_super(mem_type_id)))
@@ -4301,22 +4320,21 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo
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");
translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)nelmts, vl_elem.p);
translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)nelmts, vl_elem.p, (size_t)nelmts * vlSize);
jobj = jList;
break;
} /* H5T_VLEN */
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 +4344,14 @@ 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);
CHECK_RAWBUF_BOUNDS(ENVONLY, memb_offset, memb_vlSize, buf_size, "translate_atomic_rbuf");
memb_jobj =
translate_atomic_rbuf(ENVONLY, memb, memb_vlClass, char_buf + memb_offset, memb_vlSize);
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;
@@ -4358,7 +4383,7 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo
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");
translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)typeCount, objBuf);
translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)typeCount, objBuf, typeSize);
jobj = jList;
if (objBuf)
@@ -4368,7 +4393,6 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo
} /* H5T_ARRAY */
case H5T_ENUM:
case H5T_BITFIELD:
case H5T_OPAQUE:
case H5T_INTEGER: {
/* Convert each element */
switch (typeSize) {
@@ -4441,7 +4465,11 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo
}
break;
} /* H5T_FLOAT */
case H5T_OPAQUE:
case H5T_REFERENCE: {
/* Opaque elements are arbitrary-sized byte blobs with no matching
* Java scalar type, so they use the same byte[]-per-element model
* as references. */
/* Convert each element to a list */
jboolean bb;
jbyte *barray = NULL;
@@ -4471,7 +4499,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");
@@ -4512,7 +4547,7 @@ translate_atomic_rbuf(JNIEnv *env, jlong mem_type_id, H5T_class_t type_class, vo
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");
translate_rbuf(ENVONLY, jList, memb, base_class, (jsize)typeCount, objBuf);
translate_rbuf(ENVONLY, jList, memb, base_class, (jsize)typeCount, objBuf, typeSize);
jobj = jList;
if (objBuf)
@@ -4534,7 +4569,8 @@ done:
}
void
translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_t type_class, void *raw_buf)
translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_t type_class, void *raw_buf,
size_t buf_size)
{
hid_t memb = H5I_INVALID_HID;
H5T_class_t vlClass;
@@ -4567,6 +4603,10 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_
if (!(typeSize = H5Tget_size(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
/* The element occupies [0, typeSize) of raw_buf, verify it fits. */
if (typeSize > buf_size)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "translate_atomic_wbuf: raw buffer smaller than element");
switch (type_class) {
case H5T_VLEN: {
if (!(memb = H5Tget_super(mem_type_id)))
@@ -4576,22 +4616,33 @@ 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);
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");
/* 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,
(size_t)jnelmts * vlSize);
memcpy(char_buf, &vl_elem, sizeof(hvl_t));
ENVPTR->DeleteLocalRef(ENVONLY, array);
break;
} /* H5T_VLEN */
case H5T_COMPOUND: {
@@ -4601,8 +4652,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(
@@ -4623,11 +4679,14 @@ 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);
CHECK_RAWBUF_BOUNDS(ENVONLY, memb_offset, memb_vlSize, buf_size, "translate_atomic_wbuf");
translate_atomic_wbuf(ENVONLY, arr_obj, memb, memb_vlClass, char_buf + memb_offset,
memb_vlSize);
ENVPTR->DeleteLocalRef(ENVONLY, arr_obj);
H5Tclose(memb);
}
ENVPTR->DeleteLocalRef(ENVONLY, array);
break;
} /* H5T_COMPOUND */
case H5T_ARRAY: {
@@ -4644,8 +4703,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");
@@ -4653,14 +4717,15 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_
if (NULL == (objBuf = malloc((size_t)jnelmts * vlSize)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_atomic_wbuf: failed to allocate buffer");
translate_wbuf(ENVONLY, array, memb, vlClass, (jsize)jnelmts, objBuf);
translate_wbuf(ENVONLY, array, memb, vlClass, (jsize)jnelmts, objBuf, (size_t)jnelmts * vlSize);
memcpy(char_buf, (char *)objBuf, vlSize * (size_t)jnelmts);
ENVPTR->DeleteLocalRef(ENVONLY, array);
break;
} /* H5T_ARRAY */
case H5T_ENUM:
case H5T_BITFIELD:
case H5T_OPAQUE:
case H5T_INTEGER: {
/* Convert each element */
switch (typeSize) {
@@ -4709,7 +4774,10 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_
}
break;
} /* H5T_FLOAT */
case H5T_OPAQUE:
case H5T_REFERENCE: {
/* Opaque elements are arbitrary-sized byte blobs and use the same
* byte[]-per-element model as references. */
/* Convert each array element */
jbyte *barray = (jbyte *)ENVPTR->GetByteArrayElements(ENVONLY, in_obj, 0);
memcpy(char_buf, ((char *)barray), typeSize);
@@ -4761,8 +4829,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");
@@ -4770,13 +4843,15 @@ translate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_
if (NULL == (objBuf = malloc((size_t)jnelmts * base_size)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_atomic_wbuf: failed to allocate buffer");
translate_wbuf(ENVONLY, array, memb, base_class, (jsize)jnelmts, objBuf);
translate_wbuf(ENVONLY, array, memb, base_class, (jsize)jnelmts, objBuf,
(size_t)jnelmts * base_size);
memcpy(char_buf, (char *)objBuf, base_size * (size_t)jnelmts);
if (objBuf)
free(objBuf);
ENVPTR->DeleteLocalRef(ENVONLY, array);
break;
}
case H5T_TIME:
@@ -4792,15 +4867,37 @@ done:
return;
}
/*
* Buffer data model (see also the "Buffer data model" section in H5.java).
*
* translate_rbuf()/translate_wbuf() convert between the packed ("raw") C buffer
* that the HDF5 library reads into / writes from and the Java object tree that
* the public API exposes. The expected Java representation per memory-datatype
* class is:
*
* - integer/enum/bitfield, float: the boxed scalar type (Byte/Short/Integer/
* Long for integers sized 1/2/4/8, Float/Double for floats).
* - fixed- or variable-length string: a java.lang.String (null permitted on
* write, where it is written as a zeroed element).
* - reference, opaque: a Java byte[] holding one element's raw bytes. Opaque
* elements are arbitrary-sized blobs with no matching Java scalar type, so
* they follow the same model as references.
* - compound, variable-length sequence, array, complex: a java.util.ArrayList.
* A compound is an ArrayList of its members in declaration order; a VLEN/
* array/complex is an ArrayList of its elements; scalar leaves are the boxed
* types above. Nesting is handled by recursing through translate_atomic_*.
*
* On read the top-level container is the caller's Java Object[]; recursive calls
* append to ArrayLists. Per-element slots are not pre-allocated by the caller.
*/
void
translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t type_class, jsize count,
void *raw_buf)
void *raw_buf, size_t buf_size)
{
hid_t memb = H5I_INVALID_HID;
int ret_buflen = -1;
jboolean found_jList = JNI_TRUE;
jobjectArray jList = NULL;
jobject jobj = NULL;
hid_t memb = H5I_INVALID_HID;
jboolean retIsList = JNI_FALSE;
jobjectArray jList = NULL;
jobject jobj = NULL;
H5T_class_t vlClass;
size_t vlSize;
size_t i, x;
@@ -4816,9 +4913,12 @@ 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 the append-vs-set decision is unambiguous.
*
* Caller-side pre-allocation of per-element slots is not supported.
* Any object already present in a Java array slot is overwritten. */
retIsList = ENVPTR->IsInstanceOf(ENVONLY, ret_buf, arrCList);
switch (type_class) {
case H5T_VLEN: {
@@ -4829,68 +4929,41 @@ 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;
found_jList = JNI_TRUE;
jList = NULL;
/* Get the number of sequence elements */
CHECK_RAWBUF_BOUNDS(ENVONLY, i * sizeof(hvl_t), sizeof(hvl_t), buf_size, "translate_rbuf");
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) {
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");
}
if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_rbuf: failed to allocate list read buffer");
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 {
translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)nelmts, vl_elem.p,
(size_t)nelmts * vlSize);
/* 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);
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
}
CHECK_JNI_EXCEPTION(ENVONLY, JNI_TRUE);
ENVPTR->DeleteLocalRef(ENVONLY, jList);
}
break;
} /* H5T_VLEN */
case H5T_COMPOUND: {
/* Convert each compound element to a list */
/* Convert each compound element to a fresh ArrayList of its members. */
for (i = 0; i < (size_t)count; i++) {
found_jList = JNI_TRUE;
jList = NULL;
if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_rbuf: failed to allocate list read buffer");
/* 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;
@@ -4905,20 +4978,19 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t
if (!(memb_vlSize = H5Tget_size(memb)))
H5_LIBRARY_ERROR(ENVONLY);
CHECK_RAWBUF_BOUNDS(ENVONLY, i * typeSize + memb_offset, memb_vlSize, buf_size,
"translate_rbuf");
jobj = translate_atomic_rbuf(ENVONLY, memb, memb_vlClass,
char_buf + i * typeSize + memb_offset);
char_buf + i * typeSize + memb_offset, memb_vlSize);
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);
}
H5Tclose(memb);
}
if (ret_buflen == 0)
if (retIsList)
ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, jList);
else
ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, jList);
@@ -4945,29 +5017,18 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t
if (NULL == (objBuf = malloc(typeSize)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_rbuf: failed to allocate buffer");
/* Convert each element to a list */
/* Convert each element to a fresh list */
for (i = 0; i < (size_t)count; i++) {
found_jList = JNI_TRUE;
jList = NULL;
/* Get the object element */
CHECK_RAWBUF_BOUNDS(ENVONLY, i * typeSize, typeSize, buf_size, "translate_rbuf");
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;
}
if (NULL == jList) {
if (NULL ==
(jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY,
"translate_rbuf: failed to allocate list read buffer");
}
if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_rbuf: failed to allocate list read buffer");
translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)typeCount, objBuf);
if (found_jList == JNI_FALSE)
translate_rbuf(ENVONLY, jList, memb, vlClass, (jsize)typeCount, objBuf, typeSize);
if (retIsList)
ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, jList);
else
ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, jList);
@@ -4989,9 +5050,11 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t
case H5T_STRING: {
/* Convert each element to a list */
for (i = 0; i < (size_t)count; i++) {
jobj = translate_atomic_rbuf(ENVONLY, mem_type_id, type_class, char_buf + i * typeSize);
CHECK_RAWBUF_BOUNDS(ENVONLY, i * typeSize, typeSize, buf_size, "translate_rbuf");
jobj = translate_atomic_rbuf(ENVONLY, mem_type_id, type_class, char_buf + i * typeSize,
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);
@@ -5020,29 +5083,18 @@ translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t
if (NULL == (objBuf = malloc(typeSize)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_atomic_rbuf: failed to allocate buffer");
/* Convert each element to a list of 2 floating-point elements */
/* Convert each element to a fresh list of 2 floating-point elements */
for (i = 0; i < (size_t)count; i++) {
found_jList = JNI_TRUE;
jList = NULL;
/* Get the object element */
CHECK_RAWBUF_BOUNDS(ENVONLY, i * typeSize, typeSize, buf_size, "translate_rbuf");
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;
}
if (NULL == jList) {
if (NULL ==
(jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY,
"translate_rbuf: failed to allocate list read buffer");
}
if (NULL == (jList = (jobjectArray)ENVPTR->NewObject(ENVONLY, arrCList, arrListMethod, 0)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_rbuf: failed to allocate list read buffer");
translate_rbuf(ENVONLY, jList, memb, base_class, (jsize)typeCount, objBuf);
if (found_jList == JNI_FALSE)
translate_rbuf(ENVONLY, jList, memb, base_class, (jsize)typeCount, objBuf, typeSize);
if (retIsList)
ENVPTR->CallBooleanMethod(ENVONLY, ret_buf, arrAddMethod, jList);
else
ENVPTR->SetObjectArrayElement(ENVONLY, ret_buf, (jsize)i, jList);
@@ -5068,9 +5120,16 @@ done:
return;
}
/*
* Write counterpart to translate_rbuf(); see the "Buffer data model" comment on
* translate_rbuf() (and in H5.java) for the Java-to-C representation of each
* datatype class. On write the Java buffer is validated up front by
* h5validate_wbuf() so a mismatch raises IllegalArgumentException at the API
* boundary instead of corrupting the packed buffer here.
*/
void
translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t type_class, jsize count,
void *raw_buf)
void *raw_buf, size_t buf_size)
{
hid_t memb = H5I_INVALID_HID;
jobjectArray jList = NULL;
@@ -5103,14 +5162,23 @@ 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");
}
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);
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");
@@ -5120,10 +5188,13 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t
if (NULL == (vl_elem.p = malloc((size_t)jnelmts * vlSize)))
H5_OUT_OF_MEMORY_ERROR(ENVONLY, "translate_wbuf: failed to allocate vlen ptr buffer");
translate_wbuf(ENVONLY, array, memb, vlClass, (jsize)jnelmts, vl_elem.p);
translate_wbuf(ENVONLY, array, memb, vlClass, (jsize)jnelmts, vl_elem.p,
(size_t)jnelmts * vlSize);
CHECK_RAWBUF_BOUNDS(ENVONLY, i * sizeof(hvl_t), sizeof(hvl_t), buf_size, "translate_wbuf");
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;
@@ -5131,16 +5202,26 @@ 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");
}
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 */
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(
@@ -5162,12 +5243,15 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t
H5_LIBRARY_ERROR(ENVONLY);
jobject arr_obj = ENVPTR->GetObjectArrayElement(ENVONLY, array, (jsize)x);
CHECK_RAWBUF_BOUNDS(ENVONLY, i * typeSize + memb_offset, memb_vlSize, buf_size,
"translate_wbuf");
translate_atomic_wbuf(ENVONLY, arr_obj, memb, memb_vlClass,
char_buf + i * typeSize + memb_offset);
char_buf + i * typeSize + memb_offset, memb_vlSize);
ENVPTR->DeleteLocalRef(ENVONLY, arr_obj);
H5Tclose(memb);
}
ENVPTR->DeleteLocalRef(ENVONLY, array);
ENVPTR->DeleteLocalRef(ENVONLY, jList);
} /* end for (i = 0; i < count; i++) */
break;
@@ -5182,21 +5266,33 @@ 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");
}
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);
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");
CHECK_RAWBUF_BOUNDS(ENVONLY, i * vlSize * (size_t)jnelmts, vlSize * (size_t)jnelmts, buf_size,
"translate_wbuf");
translate_wbuf(ENVONLY, array, memb, vlClass, jnelmts,
char_buf + i * vlSize * (size_t)jnelmts);
char_buf + i * vlSize * (size_t)jnelmts, vlSize * (size_t)jnelmts);
ENVPTR->DeleteLocalRef(ENVONLY, array);
ENVPTR->DeleteLocalRef(ENVONLY, jList);
} /* end for (i = 0; i < count; i++) */
break;
@@ -5212,7 +5308,9 @@ translate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t
for (i = 0; i < (size_t)count; i++) {
if (NULL == (jobj = ENVPTR->GetObjectArrayElement(ENVONLY, (jobjectArray)in_buf, (jsize)i)))
CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE);
translate_atomic_wbuf(ENVONLY, jobj, mem_type_id, type_class, char_buf + i * typeSize);
CHECK_RAWBUF_BOUNDS(ENVONLY, i * typeSize, typeSize, buf_size, "translate_wbuf");
translate_atomic_wbuf(ENVONLY, jobj, mem_type_id, type_class, char_buf + i * typeSize,
typeSize);
ENVPTR->DeleteLocalRef(ENVONLY, jobj);
}
break;
@@ -5230,21 +5328,34 @@ 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");
}
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);
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");
CHECK_RAWBUF_BOUNDS(ENVONLY, i * base_size * (size_t)jnelmts, base_size * (size_t)jnelmts,
buf_size, "translate_wbuf");
translate_wbuf(ENVONLY, array, memb, base_class, jnelmts,
char_buf + i * base_size * (size_t)jnelmts);
char_buf + i * base_size * (size_t)jnelmts, base_size * (size_t)jnelmts);
ENVPTR->DeleteLocalRef(ENVONLY, array);
ENVPTR->DeleteLocalRef(ENVONLY, jList);
} /* end for (i = 0; i < count; i++) */
@@ -5263,6 +5374,358 @@ done:
return;
}
hssize_t
h5d_io_npoints(JNIEnv *env, hid_t mem_space_id, hid_t file_space_id, hid_t dataset_id)
{
hssize_t ret_value = -1;
hid_t sid = H5I_INVALID_HID;
if (mem_space_id != H5S_ALL) {
if ((ret_value = H5Sget_select_npoints(mem_space_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
}
else if (file_space_id != H5S_ALL) {
if ((ret_value = H5Sget_select_npoints(file_space_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
}
else {
if ((sid = H5Dget_space(dataset_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
if ((ret_value = H5Sget_simple_extent_npoints(sid)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
}
done:
if (sid >= 0)
H5Sclose(sid);
return ret_value;
}
hssize_t
h5a_io_npoints(JNIEnv *env, hid_t attr_id)
{
hssize_t ret_value = -1;
hid_t sid = H5I_INVALID_HID;
if ((sid = H5Aget_space(attr_id)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
if ((ret_value = H5Sget_simple_extent_npoints(sid)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
done:
if (sid >= 0)
H5Sclose(sid);
return ret_value;
}
/*
* Verify that a single Java object matches the structure expected when writing
* one element of type mem_type_id. Mirrors the per-element handling of
* translate_atomic_wbuf/translate_wbuf, but only inspects types -- it converts
* nothing and writes nothing. Composite classes recurse through h5validate_wbuf.
*/
static herr_t
h5validate_atomic_wbuf(JNIEnv *env, jobject in_obj, jlong mem_type_id, H5T_class_t type_class)
{
herr_t ret_value = FAIL;
hid_t memb = H5I_INVALID_HID;
H5T_class_t vlClass;
size_t typeSize;
size_t i;
jclass arrCList = ENVPTR->FindClass(ENVONLY, "java/util/ArrayList");
jmethodID mToArray = ENVPTR->GetMethodID(ENVONLY, arrCList, "toArray", "()[Ljava/lang/Object;");
if (!(typeSize = H5Tget_size(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
switch (type_class) {
case H5T_VLEN:
case H5T_ARRAY:
case H5T_COMPLEX: {
jobjectArray array;
jsize nelmts;
if (NULL == in_obj)
H5_BAD_ARGUMENT_ERROR(ENVONLY,
"h5validate_wbuf: expected a java.util.ArrayList element, got null");
if (!ENVPTR->IsInstanceOf(ENVONLY, in_obj, arrCList))
H5_BAD_ARGUMENT_ERROR(ENVONLY, "h5validate_wbuf: expected a java.util.ArrayList element");
if (!(memb = H5Tget_super(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
if ((vlClass = H5Tget_class(memb)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray);
CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE);
if (NULL == array)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "h5validate_wbuf: ArrayList.toArray returned NULL");
nelmts = ENVPTR->GetArrayLength(ENVONLY, array);
if (type_class == H5T_COMPLEX && nelmts != 2)
H5_BAD_ARGUMENT_ERROR(ENVONLY,
"h5validate_wbuf: complex element must contain exactly 2 values");
if (type_class == H5T_ARRAY) {
size_t baseSize;
if (!(baseSize = H5Tget_size(memb)))
H5_LIBRARY_ERROR(ENVONLY);
if ((size_t)nelmts != typeSize / baseSize)
H5_BAD_ARGUMENT_ERROR(
ENVONLY, "h5validate_wbuf: array element count does not match array datatype");
}
if (h5validate_wbuf(ENVONLY, array, memb, vlClass, nelmts) < 0)
goto done;
ENVPTR->DeleteLocalRef(ENVONLY, array);
break;
}
case H5T_COMPOUND: {
jobjectArray array;
jsize nelmts;
int nmembs = H5Tget_nmembers(mem_type_id);
if (nmembs < 0)
H5_LIBRARY_ERROR(ENVONLY);
if (NULL == in_obj)
H5_BAD_ARGUMENT_ERROR(
ENVONLY, "h5validate_wbuf: expected a java.util.ArrayList compound element, got null");
if (!ENVPTR->IsInstanceOf(ENVONLY, in_obj, arrCList))
H5_BAD_ARGUMENT_ERROR(ENVONLY,
"h5validate_wbuf: expected a java.util.ArrayList compound element");
array = (jobjectArray)ENVPTR->CallObjectMethod(ENVONLY, in_obj, mToArray);
CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE);
if (NULL == array)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "h5validate_wbuf: ArrayList.toArray returned NULL");
nelmts = ENVPTR->GetArrayLength(ENVONLY, array);
if (nelmts != nmembs)
H5_BAD_ARGUMENT_ERROR(
ENVONLY, "h5validate_wbuf: compound element member count does not match datatype");
for (i = 0; i < (size_t)nmembs; i++) {
H5T_class_t memb_class;
jobject memb_obj;
if ((memb = H5Tget_member_type(mem_type_id, (unsigned)i)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
if ((memb_class = H5Tget_class(memb)) < 0)
H5_LIBRARY_ERROR(ENVONLY);
memb_obj = ENVPTR->GetObjectArrayElement(ENVONLY, array, (jsize)i);
CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE);
if (h5validate_atomic_wbuf(ENVONLY, memb_obj, memb, memb_class) < 0)
goto done;
ENVPTR->DeleteLocalRef(ENVONLY, memb_obj);
H5Tclose(memb);
memb = H5I_INVALID_HID;
}
ENVPTR->DeleteLocalRef(ENVONLY, array);
break;
}
case H5T_INTEGER:
case H5T_ENUM:
case H5T_BITFIELD: {
const char *clsname;
switch (typeSize) {
case 1:
clsname = "java/lang/Byte";
break;
case 2:
clsname = "java/lang/Short";
break;
case 4:
clsname = "java/lang/Integer";
break;
case 8:
clsname = "java/lang/Long";
break;
default:
H5_BAD_ARGUMENT_ERROR(ENVONLY,
"h5validate_wbuf: no matching boxed type for integer element size");
}
if (NULL == in_obj || !ENVPTR->IsInstanceOf(ENVONLY, in_obj, ENVPTR->FindClass(ENVONLY, clsname)))
H5_BAD_ARGUMENT_ERROR(ENVONLY,
"h5validate_wbuf: integer element is not the expected boxed type");
break;
}
case H5T_FLOAT: {
const char *clsname;
switch (typeSize) {
case 4:
clsname = "java/lang/Float";
break;
case 8:
clsname = "java/lang/Double";
break;
default:
H5_BAD_ARGUMENT_ERROR(ENVONLY,
"h5validate_wbuf: no matching boxed type for float element size");
}
if (NULL == in_obj || !ENVPTR->IsInstanceOf(ENVONLY, in_obj, ENVPTR->FindClass(ENVONLY, clsname)))
H5_BAD_ARGUMENT_ERROR(ENVONLY,
"h5validate_wbuf: float element is not the expected boxed type");
break;
}
case H5T_STRING: {
/* A null element is permitted; translate_atomic_wbuf writes a zeroed value. */
if (NULL != in_obj &&
!ENVPTR->IsInstanceOf(ENVONLY, in_obj, ENVPTR->FindClass(ENVONLY, "java/lang/String")))
H5_BAD_ARGUMENT_ERROR(ENVONLY, "h5validate_wbuf: string element is not a java.lang.String");
break;
}
case H5T_OPAQUE:
case H5T_REFERENCE: {
/* translate_atomic_wbuf reads a Java byte[] for references and for
* opaque elements (opaque is an arbitrary-sized byte blob). */
if (NULL == in_obj || !ENVPTR->IsInstanceOf(ENVONLY, in_obj, ENVPTR->FindClass(ENVONLY, "[B")))
H5_BAD_ARGUMENT_ERROR(ENVONLY, "h5validate_wbuf: reference/opaque element is not a byte[]");
break;
}
case H5T_TIME:
case H5T_NO_CLASS:
case H5T_NCLASSES:
default:
H5_UNIMPLEMENTED(ENVONLY, "h5validate_wbuf: invalid class type");
} /* switch(type_class) */
ret_value = SUCCEED;
done:
if (memb >= 0)
H5Tclose(memb);
return ret_value;
}
herr_t
h5validate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t type_class, jsize count)
{
herr_t ret_value = FAIL;
size_t i;
for (i = 0; i < (size_t)count; i++) {
jobject elem = ENVPTR->GetObjectArrayElement(ENVONLY, in_buf, (jsize)i);
CHECK_JNI_EXCEPTION(ENVONLY, JNI_FALSE);
if (h5validate_atomic_wbuf(ENVONLY, elem, mem_type_id, type_class) < 0) {
if (elem)
ENVPTR->DeleteLocalRef(ENVONLY, elem);
goto done;
}
if (elem)
ENVPTR->DeleteLocalRef(ENVONLY, elem);
}
ret_value = SUCCEED;
done:
return ret_value;
}
/*
* Capacity check for a packed ("raw") read/write buffer: a Java array of
* `buf_len` elements of `elem_size` bytes must be large enough to hold
* `npoints` values of type `mem_type_id`. This is the invariant that keeps
* H5Dread/H5Dwrite from running past the pinned Java array when the buffer kind
* or length does not match the datatype/selection. Raises a descriptive
* IllegalArgumentException and returns a negative value on mismatch.
*/
static herr_t
h5validate_raw_capacity(JNIEnv *env, hid_t mem_type_id, hssize_t npoints, jsize buf_len, size_t elem_size,
const char *where)
{
herr_t ret_value = FAIL;
size_t typeSize;
size_t have, need;
char msg[192];
if (buf_len < 0) {
snprintf(msg, sizeof(msg), "%s: buffer length < 0", where);
H5_BAD_ARGUMENT_ERROR(ENVONLY, msg);
}
if (!(typeSize = H5Tget_size(mem_type_id)))
H5_LIBRARY_ERROR(ENVONLY);
have = (size_t)buf_len * elem_size;
need = (size_t)npoints * typeSize;
if (have < need) {
snprintf(msg, sizeof(msg), "%s: buffer holds %zu bytes but the selection requires %zu bytes", where,
have, need);
H5_BAD_ARGUMENT_ERROR(ENVONLY, msg);
}
ret_value = SUCCEED;
done:
return ret_value;
}
/*
* Slot-count check for a String[]-style read/write buffer (fixed/variable
* strings and references): the array must have at least one slot per selected
* point. The library fills/consumes one Java String (or byte[]) per element.
*/
static herr_t
h5validate_slot_count(JNIEnv *env, hssize_t npoints, jsize buf_len, const char *where)
{
herr_t ret_value = FAIL;
char msg[160];
if (buf_len < 0 || (hssize_t)buf_len < npoints) {
snprintf(msg, sizeof(msg), "%s: buffer has %d slots but the selection requires %lld", where,
(int)buf_len, (long long)npoints);
H5_BAD_ARGUMENT_ERROR(ENVONLY, msg);
}
ret_value = SUCCEED;
done:
return ret_value;
}
herr_t
h5d_validate_raw_buf(JNIEnv *env, hid_t mem_type_id, hid_t mem_space_id, hid_t file_space_id,
hid_t dataset_id, jsize buf_len, size_t elem_size, const char *where)
{
hssize_t npoints;
if ((npoints = h5d_io_npoints(env, mem_space_id, file_space_id, dataset_id)) < 0)
return FAIL;
return h5validate_raw_capacity(env, mem_type_id, npoints, buf_len, elem_size, where);
}
herr_t
h5a_validate_raw_buf(JNIEnv *env, hid_t mem_type_id, hid_t attr_id, jsize buf_len, size_t elem_size,
const char *where)
{
hssize_t npoints;
if ((npoints = h5a_io_npoints(env, attr_id)) < 0)
return FAIL;
return h5validate_raw_capacity(env, mem_type_id, npoints, buf_len, elem_size, where);
}
herr_t
h5d_validate_slot_buf(JNIEnv *env, hid_t mem_space_id, hid_t file_space_id, hid_t dataset_id, jsize buf_len,
const char *where)
{
hssize_t npoints;
if ((npoints = h5d_io_npoints(env, mem_space_id, file_space_id, dataset_id)) < 0)
return FAIL;
return h5validate_slot_count(env, npoints, buf_len, where);
}
herr_t
h5a_validate_slot_buf(JNIEnv *env, hid_t attr_id, jsize buf_len, const char *where)
{
hssize_t npoints;
if ((npoints = h5a_io_npoints(env, attr_id)) < 0)
return FAIL;
return h5validate_slot_count(env, npoints, buf_len, where);
}
#ifdef __cplusplus
}
#endif
+35 -2
View File
@@ -46,9 +46,42 @@ extern int h5str_dump_simple_mem(JNIEnv *env, FILE *stream, hid_t attr, int b
extern htri_t H5Tdetect_variable_str(hid_t tid);
extern void translate_rbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t type_class,
jsize count, void *raw_buf);
jsize count, void *raw_buf, size_t buf_size);
extern void translate_wbuf(JNIEnv *env, jobjectArray ret_buf, jlong mem_type_id, H5T_class_t type_class,
jsize count, void *raw_buf);
jsize count, void *raw_buf, size_t buf_size);
/*
* API-level buffer-contract verification helpers. These let the JNI entry
* points reject a Java buffer whose structure does not match the documented
* data-model for mem_type_id BEFORE any conversion or native I/O is attempted,
* turning crashes/corruption into clean HDF5LibraryExceptions.
*/
/* Number of elements selected for a dataset read/write (mem space, else file
* space, else the dataset extent for H5S_ALL), or -1 on error. */
extern hssize_t h5d_io_npoints(JNIEnv *env, hid_t mem_space_id, hid_t file_space_id, hid_t dataset_id);
/* Number of elements in an attribute's dataspace, or -1 on error. */
extern hssize_t h5a_io_npoints(JNIEnv *env, hid_t attr_id);
/* Recursively verify that a write buffer (top-level Java Object[] of `count`
* elements) matches the nested ArrayList/boxed structure expected for
* mem_type_id. Raises a descriptive H5_BAD_ARGUMENT_ERROR and returns a
* negative value on the first mismatch; returns 0 if the structure is valid. */
extern herr_t h5validate_wbuf(JNIEnv *env, jobjectArray in_buf, jlong mem_type_id, H5T_class_t type_class,
jsize count);
/* Capacity check for a packed (primitive-array or byte[]) dataset/attribute
* buffer: `buf_len` elements of `elem_size` bytes must cover the selection of
* mem_type_id. Raises IllegalArgumentException and returns negative on failure. */
extern herr_t h5d_validate_raw_buf(JNIEnv *env, hid_t mem_type_id, hid_t mem_space_id, hid_t file_space_id,
hid_t dataset_id, jsize buf_len, size_t elem_size, const char *where);
extern herr_t h5a_validate_raw_buf(JNIEnv *env, hid_t mem_type_id, hid_t attr_id, jsize buf_len,
size_t elem_size, const char *where);
/* Slot-count check for a String[]-style buffer (fixed/variable strings, refs):
* the array must have at least one slot per selected point. */
extern herr_t h5d_validate_slot_buf(JNIEnv *env, hid_t mem_space_id, hid_t file_space_id, hid_t dataset_id,
jsize buf_len, const char *where);
extern herr_t h5a_validate_slot_buf(JNIEnv *env, hid_t attr_id, jsize buf_len, const char *where);
/*
* Symbols used to format the output of h5str_sprintf and
+1 -1
View File
@@ -460,7 +460,7 @@ public class TestH5Arw {
@Test
public void testH5Aread_128bit_floats()
{
byte[][][] attr_data = new byte[DIM_X][DIM128_Y][8];
byte[][][] attr_data = new byte[DIM_X][DIM128_Y][16];
try {
openH5file(H5_FLTS_FILE, DATASETF128);
+848 -1
View File
@@ -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;
@@ -434,7 +436,7 @@ public class TestH5D {
try {
if (H5did >= 0)
H5.H5Dwrite(H5did, HDF5Constants.H5T_NATIVE_INT, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, dset_data[0]);
HDF5Constants.H5P_DEFAULT, dset_data);
}
catch (Exception e) {
e.printStackTrace();
@@ -1755,6 +1757,166 @@ public class TestH5D {
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<String>(Arrays.asList(row0));
arr_str_data[1] = new ArrayList<String>(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()
{
@@ -1947,4 +2109,689 @@ public class TestH5D {
}
}
}
/*
* 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<Integer> seq0 = new ArrayList<>();
seq0.add(1);
seq0.add(2);
seq0.add(3);
ArrayList<Integer> seq1 = new ArrayList<>();
seq1.add(5);
seq1.add(6);
ArrayList[] write_data = new ArrayList[2];
ArrayList<Object> rec0 = new ArrayList<>();
rec0.add(seq0);
rec0.add(Integer.valueOf(4));
write_data[0] = rec0;
ArrayList<Object> 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) {
}
}
}
/*
* 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) {
}
}
}
/*
* Negative tests for the API-level buffer-contract verification in H5DwriteVL.
* Each supplies a malformed buffer for a COMPOUND { seq: VLEN int, n: int }
* dataset and asserts a clean IllegalArgumentException is raised before any
* native write, rather than a SIGSEGV or silent corruption.
*/
/* Buffer shorter than the selection must be rejected (would otherwise overrun
* the raw write buffer in H5Dwrite). */
@Test
public void testH5DwriteVL_undersized_buffer()
{
long dset_id = HDF5Constants.H5I_INVALID_HID;
long file_type_id = HDF5Constants.H5I_INVALID_HID;
try {
dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr_undersized");
file_type_id = H5.H5Dget_type(dset_id);
// Dataset has 2 rows; supply only 1.
ArrayList<Integer> seq = new ArrayList<>();
seq.add(1);
ArrayList<Object> rec = new ArrayList<>();
rec.add(seq);
rec.add(Integer.valueOf(2));
ArrayList[] bad = new ArrayList[1];
bad[0] = rec;
try {
H5.H5DwriteVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, bad);
fail("testH5DwriteVL_undersized_buffer: expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// expected
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5DwriteVL_undersized_buffer: " + 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) {
}
}
}
/* A compound member whose Java type is wrong (String where an Integer is
* expected) must be rejected instead of crashing in translate_atomic_wbuf. */
@Test
public void testH5DwriteVL_compound_wrong_member_type()
{
long dset_id = HDF5Constants.H5I_INVALID_HID;
long file_type_id = HDF5Constants.H5I_INVALID_HID;
try {
dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr_badmember");
file_type_id = H5.H5Dget_type(dset_id);
ArrayList[] bad = new ArrayList[2];
ArrayList<Integer> seq0 = new ArrayList<>();
seq0.add(1);
ArrayList<Object> rec0 = new ArrayList<>();
rec0.add(seq0);
rec0.add(Integer.valueOf(4));
bad[0] = rec0;
// row 1: the 'n' member is a String, not an Integer.
ArrayList<Integer> seq1 = new ArrayList<>();
seq1.add(5);
ArrayList<Object> rec1 = new ArrayList<>();
rec1.add(seq1);
rec1.add("not an int");
bad[1] = rec1;
try {
H5.H5DwriteVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, bad);
fail("testH5DwriteVL_compound_wrong_member_type: expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// expected
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5DwriteVL_compound_wrong_member_type: " + 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) {
}
}
}
/* A VLEN member that is not an ArrayList (here a String) must be rejected
* instead of being mis-read as a list. */
@Test
public void testH5DwriteVL_vlen_element_not_arraylist()
{
long dset_id = HDF5Constants.H5I_INVALID_HID;
long file_type_id = HDF5Constants.H5I_INVALID_HID;
try {
dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr_notlist");
file_type_id = H5.H5Dget_type(dset_id);
ArrayList[] bad = new ArrayList[2];
// row 0: the 'seq' member should be an ArrayList but is a String.
ArrayList<Object> rec0 = new ArrayList<>();
rec0.add("not a list");
rec0.add(Integer.valueOf(4));
bad[0] = rec0;
ArrayList<Integer> seq1 = new ArrayList<>();
seq1.add(5);
ArrayList<Object> rec1 = new ArrayList<>();
rec1.add(seq1);
rec1.add(Integer.valueOf(7));
bad[1] = rec1;
try {
H5.H5DwriteVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, bad);
fail("testH5DwriteVL_vlen_element_not_arraylist: expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// expected
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5DwriteVL_vlen_element_not_arraylist: " + 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) {
}
}
}
/* A compound row with the wrong number of members must be rejected. */
@Test
public void testH5DwriteVL_compound_wrong_member_count()
{
long dset_id = HDF5Constants.H5I_INVALID_HID;
long file_type_id = HDF5Constants.H5I_INVALID_HID;
try {
dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr_badcount");
file_type_id = H5.H5Dget_type(dset_id);
ArrayList[] bad = new ArrayList[2];
// row 0: only one member instead of two ('n' is missing).
ArrayList<Integer> seq0 = new ArrayList<>();
seq0.add(1);
ArrayList<Object> rec0 = new ArrayList<>();
rec0.add(seq0);
bad[0] = rec0;
ArrayList<Integer> seq1 = new ArrayList<>();
seq1.add(5);
ArrayList<Object> rec1 = new ArrayList<>();
rec1.add(seq1);
rec1.add(Integer.valueOf(7));
bad[1] = rec1;
try {
H5.H5DwriteVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, bad);
fail("testH5DwriteVL_compound_wrong_member_count: expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// expected
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5DwriteVL_compound_wrong_member_count: " + 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) {
}
}
}
/*
* Read a 1-D dataset whose type is VLEN { COMPOUND { A: int, B: int } }. The
* caller passes a freshly allocated array with null row slots, which is the
* canonical read pattern: the JNI installs a freshly allocated ArrayList into
* each slot. Exercises the translate_rbuf H5T_VLEN top-level case for a
* compound element type.
*/
@Test
public void testH5Dread_vlen_of_compound()
{
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: 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<Object> row0_elem0 = new ArrayList<>();
row0_elem0.add(Integer.valueOf(1));
row0_elem0.add(Integer.valueOf(2));
ArrayList<Object> row0 = new ArrayList<>();
row0.add(row0_elem0);
ArrayList<Object> row1_elem0 = new ArrayList<>();
row1_elem0.add(Integer.valueOf(3));
row1_elem0.add(Integer.valueOf(4));
ArrayList<Object> row1_elem1 = new ArrayList<>();
row1_elem1.add(Integer.valueOf(5));
row1_elem1.add(Integer.valueOf(6));
ArrayList<Object> 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);
// Freshly allocated array with null row slots; the JNI fills each slot.
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: row 0 not null", read_data[0]);
assertNotNull("testH5Dread_vlen_of_compound: row 1 not null", read_data[1]);
assertEquals("testH5Dread_vlen_of_compound: row 0 has 1 element", 1, read_data[0].size());
assertEquals("testH5Dread_vlen_of_compound: 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: " + 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) {
}
}
}
/*
* 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<Object> row0_elem0_sub = new ArrayList<>();
row0_elem0_sub.add(Integer.valueOf(2));
row0_elem0_sub.add(Integer.valueOf(3));
ArrayList<Object> row0_elem0 = new ArrayList<>();
row0_elem0.add(Integer.valueOf(1));
row0_elem0.add(row0_elem0_sub);
ArrayList<Object> row0 = new ArrayList<>();
row0.add(row0_elem0);
ArrayList<Object> row1_elem0_sub = new ArrayList<>();
row1_elem0_sub.add(Integer.valueOf(5));
row1_elem0_sub.add(Integer.valueOf(6));
ArrayList<Object> row1_elem0 = new ArrayList<>();
row1_elem0.add(Integer.valueOf(4));
row1_elem0.add(row1_elem0_sub);
ArrayList<Object> row1_elem1_sub = new ArrayList<>();
row1_elem1_sub.add(Integer.valueOf(8));
row1_elem1_sub.add(Integer.valueOf(9));
ArrayList<Object> row1_elem1 = new ArrayList<>();
row1_elem1.add(Integer.valueOf(7));
row1_elem1.add(row1_elem1_sub);
ArrayList<Object> 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) {
}
}
}
}
+1 -1
View File
@@ -380,7 +380,7 @@ public class TestH5Drw {
@Test
public void testH5Dread_128bit_floats()
{
byte[][][] dset_data = new byte[DIM_X][DIM128_Y][8];
byte[][][] dset_data = new byte[DIM_X][DIM128_Y][16];
try {
openH5file(H5_FLTS_FILE, DATASETF128);
+11 -1
View File
@@ -14,15 +14,25 @@ JUnit version 4.13.2
.testH5Dfill_null
.testH5Dget_storage_size_empty
.testH5Diterate
.testH5DwriteVL_compound_wrong_member_type
.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
.testH5Dread_compound_of_vlen
.testH5Dget_space_status
.testH5DwriteVL_compound_wrong_member_count
.testH5Dvlen_write_read
.testH5Dget_space
.testH5DwriteVL_vlen_element_not_arraylist
.testH5Dget_type_closed
.testH5Dwrite_compound_of_vlen
.testH5DwriteVL_undersized_buffer
Time: XXXX
OK (22 tests)
OK (32 tests)
+845 -7
View File
@@ -870,7 +870,6 @@ public class TestH5D {
// Check Coordinates
long[] fill_coords = new long[ndim];
long fill_curr_coord = (long)curr_coordHandle.get(operator_data, 0);
System.out.println("fill_curr_coord = " + fill_curr_coord);
for (int i = 0; i < ndim; i++)
fill_coords[i] = (long)coordsHandle.get(operator_data, 0L, 2 * fill_curr_coord + i);
@@ -2221,8 +2220,6 @@ public class TestH5D {
for (int i = 0; i < 6; i++) {
assertEquals("String " + i + " mismatch", writeData[i], readData[i]);
}
System.out.println("testH5D_VLStrings_write_read_roundtrip: PASSED");
}
catch (Exception ex) {
ex.printStackTrace();
@@ -2287,8 +2284,6 @@ public class TestH5D {
assertEquals("Second should match", "Not empty", readData[1]);
assertEquals("Third should be empty", "", readData[2]);
assertEquals("Fourth should match", "Also not empty", readData[3]);
System.out.println("testH5D_VLStrings_roundtrip_empty: PASSED");
}
catch (Exception ex) {
ex.printStackTrace();
@@ -2358,8 +2353,6 @@ public class TestH5D {
assertEquals("Large string " + i + " mismatch", writeData[i], readData[i]);
assertTrue("String should be > 1KB", readData[i].length() > 1024);
}
System.out.println("testH5D_VLStrings_roundtrip_large: PASSED");
}
catch (Exception ex) {
ex.printStackTrace();
@@ -2386,4 +2379,849 @@ public class TestH5D {
}
}
}
/*
* 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<String>(Arrays.asList(row0));
arr_str_data[1] = new ArrayList<String>(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) {
}
}
}
/*
* 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<Integer> seq0 = new ArrayList<>();
seq0.add(1);
seq0.add(2);
seq0.add(3);
ArrayList<Integer> seq1 = new ArrayList<>();
seq1.add(5);
seq1.add(6);
ArrayList[] write_data = new ArrayList[2];
ArrayList<Object> rec0 = new ArrayList<>();
rec0.add(seq0);
rec0.add(Integer.valueOf(4));
write_data[0] = rec0;
ArrayList<Object> 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) {
}
}
}
/*
* 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) {
}
}
}
/*
* Negative tests for the API-level buffer-contract verification in H5DwriteVL.
* Each supplies a malformed buffer for a COMPOUND { seq: VLEN int, n: int }
* dataset and asserts a clean IllegalArgumentException is raised before any
* native write, rather than a SIGSEGV or silent corruption.
*/
/* Buffer shorter than the selection must be rejected (would otherwise overrun
* the raw write buffer in H5Dwrite). */
@Test
public void testH5DwriteVL_undersized_buffer()
{
long dset_id = HDF5Constants.H5I_INVALID_HID;
long file_type_id = HDF5Constants.H5I_INVALID_HID;
try {
dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr_undersized");
file_type_id = H5.H5Dget_type(dset_id);
// Dataset has 2 rows; supply only 1.
ArrayList<Integer> seq = new ArrayList<>();
seq.add(1);
ArrayList<Object> rec = new ArrayList<>();
rec.add(seq);
rec.add(Integer.valueOf(2));
ArrayList[] bad = new ArrayList[1];
bad[0] = rec;
try {
H5.H5DwriteVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, bad);
fail("testH5DwriteVL_undersized_buffer: expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// expected
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5DwriteVL_undersized_buffer: " + 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) {
}
}
}
/* A compound member whose Java type is wrong (String where an Integer is
* expected) must be rejected instead of crashing in translate_atomic_wbuf. */
@Test
public void testH5DwriteVL_compound_wrong_member_type()
{
long dset_id = HDF5Constants.H5I_INVALID_HID;
long file_type_id = HDF5Constants.H5I_INVALID_HID;
try {
dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr_badmember");
file_type_id = H5.H5Dget_type(dset_id);
ArrayList[] bad = new ArrayList[2];
ArrayList<Integer> seq0 = new ArrayList<>();
seq0.add(1);
ArrayList<Object> rec0 = new ArrayList<>();
rec0.add(seq0);
rec0.add(Integer.valueOf(4));
bad[0] = rec0;
// row 1: the 'n' member is a String, not an Integer.
ArrayList<Integer> seq1 = new ArrayList<>();
seq1.add(5);
ArrayList<Object> rec1 = new ArrayList<>();
rec1.add(seq1);
rec1.add("not an int");
bad[1] = rec1;
try {
H5.H5DwriteVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, bad);
fail("testH5DwriteVL_compound_wrong_member_type: expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// expected
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5DwriteVL_compound_wrong_member_type: " + 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) {
}
}
}
/* A VLEN member that is not an ArrayList (here a String) must be rejected
* instead of being mis-read as a list. */
@Test
public void testH5DwriteVL_vlen_element_not_arraylist()
{
long dset_id = HDF5Constants.H5I_INVALID_HID;
long file_type_id = HDF5Constants.H5I_INVALID_HID;
try {
dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr_notlist");
file_type_id = H5.H5Dget_type(dset_id);
ArrayList[] bad = new ArrayList[2];
// row 0: the 'seq' member should be an ArrayList but is a String.
ArrayList<Object> rec0 = new ArrayList<>();
rec0.add("not a list");
rec0.add(Integer.valueOf(4));
bad[0] = rec0;
ArrayList<Integer> seq1 = new ArrayList<>();
seq1.add(5);
ArrayList<Object> rec1 = new ArrayList<>();
rec1.add(seq1);
rec1.add(Integer.valueOf(7));
bad[1] = rec1;
try {
H5.H5DwriteVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, bad);
fail("testH5DwriteVL_vlen_element_not_arraylist: expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// expected
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5DwriteVL_vlen_element_not_arraylist: " + 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) {
}
}
}
/* A compound row with the wrong number of members must be rejected. */
@Test
public void testH5DwriteVL_compound_wrong_member_count()
{
long dset_id = HDF5Constants.H5I_INVALID_HID;
long file_type_id = HDF5Constants.H5I_INVALID_HID;
try {
dset_id = writeCompoundOfVlenDataset("cmpd_of_vlen_wr_badcount");
file_type_id = H5.H5Dget_type(dset_id);
ArrayList[] bad = new ArrayList[2];
// row 0: only one member instead of two ('n' is missing).
ArrayList<Integer> seq0 = new ArrayList<>();
seq0.add(1);
ArrayList<Object> rec0 = new ArrayList<>();
rec0.add(seq0);
bad[0] = rec0;
ArrayList<Integer> seq1 = new ArrayList<>();
seq1.add(5);
ArrayList<Object> rec1 = new ArrayList<>();
rec1.add(seq1);
rec1.add(Integer.valueOf(7));
bad[1] = rec1;
try {
H5.H5DwriteVL(dset_id, file_type_id, HDF5Constants.H5S_ALL, HDF5Constants.H5S_ALL,
HDF5Constants.H5P_DEFAULT, bad);
fail("testH5DwriteVL_compound_wrong_member_count: expected IllegalArgumentException");
}
catch (IllegalArgumentException expected) {
// expected
}
}
catch (Throwable err) {
err.printStackTrace();
fail("testH5DwriteVL_compound_wrong_member_count: " + 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) {
}
}
}
/*
* Read a 1-D dataset whose type is VLEN { COMPOUND { A: int, B: int } }. The
* caller passes a freshly allocated array with null row slots, which is the
* canonical read pattern: the JNI installs a freshly allocated ArrayList into
* each slot. Exercises the translate_rbuf H5T_VLEN top-level case for a
* compound element type.
*/
@Test
public void testH5Dread_vlen_of_compound()
{
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: 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<Object> row0_elem0 = new ArrayList<>();
row0_elem0.add(Integer.valueOf(1));
row0_elem0.add(Integer.valueOf(2));
ArrayList<Object> row0 = new ArrayList<>();
row0.add(row0_elem0);
ArrayList<Object> row1_elem0 = new ArrayList<>();
row1_elem0.add(Integer.valueOf(3));
row1_elem0.add(Integer.valueOf(4));
ArrayList<Object> row1_elem1 = new ArrayList<>();
row1_elem1.add(Integer.valueOf(5));
row1_elem1.add(Integer.valueOf(6));
ArrayList<Object> 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);
// Freshly allocated array with null row slots; the JNI fills each slot.
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: row 0 not null", read_data[0]);
assertNotNull("testH5Dread_vlen_of_compound: row 1 not null", read_data[1]);
assertEquals("testH5Dread_vlen_of_compound: row 0 has 1 element", 1, read_data[0].size());
assertEquals("testH5Dread_vlen_of_compound: 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: " + 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) {
}
}
}
/*
* 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<Object> row0_elem0_sub = new ArrayList<>();
row0_elem0_sub.add(Integer.valueOf(2));
row0_elem0_sub.add(Integer.valueOf(3));
ArrayList<Object> row0_elem0 = new ArrayList<>();
row0_elem0.add(Integer.valueOf(1));
row0_elem0.add(row0_elem0_sub);
ArrayList<Object> row0 = new ArrayList<>();
row0.add(row0_elem0);
ArrayList<Object> row1_elem0_sub = new ArrayList<>();
row1_elem0_sub.add(Integer.valueOf(5));
row1_elem0_sub.add(Integer.valueOf(6));
ArrayList<Object> row1_elem0 = new ArrayList<>();
row1_elem0.add(Integer.valueOf(4));
row1_elem0.add(row1_elem0_sub);
ArrayList<Object> row1_elem1_sub = new ArrayList<>();
row1_elem1_sub.add(Integer.valueOf(8));
row1_elem1_sub.add(Integer.valueOf(9));
ArrayList<Object> row1_elem1 = new ArrayList<>();
row1_elem1.add(Integer.valueOf(7));
row1_elem1.add(row1_elem1_sub);
ArrayList<Object> 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) {
}
}
}
}
+11 -1
View File
@@ -12,17 +12,27 @@ I.testH5Dget_type
.testH5Dcreate_anon
.testH5Dfill_null
.testH5Dget_storage_size_empty
.testH5DwriteVL_compound_wrong_member_type
.testH5Dget_access_plist
.testH5Dread_vlen_of_nested_compound
.testH5DArray_string_buffer_flat_StringArray
.testH5Dvlen_get_buf_size
.testH5Dget_space_closed
.testH5DArray_string_buffer_flat_StringArray_write
.testH5DArray_string_buffer
.testH5Dread_vlen_of_compound
.testH5Dread_compound_of_vlen
.testH5Dget_space_status
.testH5DwriteVL_compound_wrong_member_count
.testH5Dvlen_write_read
.testH5Dget_space
.testH5DwriteVL_vlen_element_not_arraylist
.testH5Dget_type_closed
.testH5Dwrite_compound_of_vlen
.testH5Dwrite_readCompound
.testH5DwriteVL_undersized_buffer
Time: XXXX
OK (22 tests)
OK (32 tests)