hdf5/test/tid.c
Dana Robinson 7f1e49206d
Renamed COPYING to LICENSE (#4978)
This is where most people will expect to find license information. The
COPYING_LBNL_HDF5 file has also been renamed to LICENSE_LBNL_HDF5.
The licenses are unchanged.
2024-10-18 21:13:04 -07:00

1520 lines
47 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the LICENSE file, which can be found at the root of the source code *
* distribution tree, or in https://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Test user-created identifiers (hid_t's) and identifier types. */
#include "testhdf5.h"
/* Include H5Ipkg.h to calculate max number of groups */
#define H5I_FRIEND /*suppress error about including H5Ipkg */
#include "H5Ipkg.h"
/* Defines used in test_appropriate_ids */
#define FILE_NAME "tid.h5"
#define DSET_NAME "Dataset 1"
static herr_t
free_wrapper(void *p, void H5_ATTR_UNUSED **_ctx)
{
free(p);
return SUCCEED;
}
/* Test basic functionality of registering and deleting types and IDs */
static int
basic_id_test(void)
{
H5I_type_t myType = H5I_BADID;
hid_t arrayID = H5I_INVALID_HID;
void *testObj = NULL;
void *testPtr = NULL;
char nameString[10];
hid_t testID;
ssize_t testSize = -1;
herr_t err;
int num_ref;
hsize_t num_members;
/* Try to register an ID with fictitious types */
H5E_BEGIN_TRY
arrayID = H5Iregister((H5I_type_t)420, testObj);
H5E_END_TRY
VERIFY(arrayID, H5I_INVALID_HID, "H5Iregister");
if (arrayID != H5I_INVALID_HID)
goto out;
H5E_BEGIN_TRY
arrayID = H5Iregister((H5I_type_t)-1, testObj);
H5E_END_TRY
VERIFY(arrayID, H5I_INVALID_HID, "H5Iregister");
if (arrayID != H5I_INVALID_HID)
goto out;
/* Try to access IDs with fictitious types */
H5E_BEGIN_TRY
testPtr = H5Iobject_verify((hid_t)100, (H5I_type_t)0);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
if (testPtr != NULL)
goto out;
H5E_BEGIN_TRY
testPtr = H5Iobject_verify((hid_t)700, (H5I_type_t)700);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
if (testPtr != NULL)
goto out;
/* Register a type */
myType = H5Iregister_type((size_t)64, 0, free_wrapper);
CHECK(myType, H5I_BADID, "H5Iregister_type");
if (myType == H5I_BADID)
goto out;
/* Register an ID and retrieve the object it points to.
* Once the ID has been registered, testObj will be freed when
* its ID type is destroyed.
*/
testObj = malloc(7 * sizeof(int));
arrayID = H5Iregister(myType, testObj);
CHECK(arrayID, H5I_INVALID_HID, "H5Iregister");
if (arrayID == H5I_INVALID_HID) {
free(testObj);
goto out;
}
testPtr = (int *)H5Iobject_verify(arrayID, myType);
CHECK_PTR_EQ(testPtr, testObj, "H5Iobject_verify");
if (testPtr != testObj)
goto out;
/* Ensure that H5Iget_file_id and H5Iget_name() fail, since this
* is an hid_t for the wrong kind of object
*/
H5E_BEGIN_TRY
testID = H5Iget_file_id(arrayID);
H5E_END_TRY
VERIFY(testID, H5I_INVALID_HID, "H5Iget_file_id");
if (testID != H5I_INVALID_HID)
goto out;
H5E_BEGIN_TRY
testSize = H5Iget_name(arrayID, nameString, (size_t)9);
H5E_END_TRY
VERIFY(testSize, -1, "H5Iget_name");
if (testSize != -1)
goto out;
/* Make sure H5Iremove_verify catches objects of the wrong type */
H5E_BEGIN_TRY
testPtr = (int *)H5Iremove_verify(arrayID, (H5I_type_t)0);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
if (testPtr != NULL)
goto out;
H5E_BEGIN_TRY
testPtr = (int *)H5Iremove_verify(arrayID, (H5I_type_t)((int)myType - 1));
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
if (testPtr != NULL)
goto out;
/* Remove an ID and make sure we can't access it */
testPtr = (int *)H5Iremove_verify(arrayID, myType);
CHECK_PTR(testPtr, "H5Iremove_verify");
if (testPtr == NULL)
goto out;
H5E_BEGIN_TRY
testPtr = (int *)H5Iobject_verify(arrayID, myType);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
if (testPtr != NULL)
goto out;
/* Delete the type and make sure we can't access objects within it */
arrayID = H5Iregister(myType, testObj);
err = H5Idestroy_type(myType);
VERIFY(err, 0, "H5Idestroy_type");
if (err != 0)
goto out;
VERIFY(H5Itype_exists(myType), 0, "H5Itype_exists");
if (H5Itype_exists(myType) != 0)
goto out;
H5E_BEGIN_TRY
VERIFY(H5Inmembers(myType, NULL), -1, "H5Inmembers");
if (H5Inmembers(myType, NULL) != -1)
goto out;
H5E_END_TRY
/* Register another type and another object in that type */
myType = H5Iregister_type((size_t)64, 0, free_wrapper);
CHECK(myType, H5I_BADID, "H5Iregister_type");
if (myType == H5I_BADID)
goto out;
/* The memory that testObj pointed to should already have been
* freed when the previous type was destroyed. Allocate new
* memory for it.
*/
testObj = malloc(7 * sizeof(int));
arrayID = H5Iregister(myType, testObj);
CHECK(arrayID, H5I_INVALID_HID, "H5Iregister");
if (arrayID == H5I_INVALID_HID) {
free(testObj);
goto out;
}
err = H5Inmembers(myType, &num_members);
CHECK(err, -1, "H5Inmembers");
if (err < 0)
goto out;
VERIFY(num_members, 1, "H5Inmembers");
if (num_members != 1)
goto out;
/* Increment references to type and ensure that dec_type_ref
* doesn't destroy the type
*/
num_ref = H5Iinc_type_ref(myType);
VERIFY(num_ref, 2, "H5Iinc_type_ref");
if (num_ref != 2)
goto out;
num_ref = H5Idec_type_ref(myType);
VERIFY(num_ref, 1, "H5Idec_type_ref");
if (num_ref != 1)
goto out;
err = H5Inmembers(myType, &num_members);
CHECK(err, -1, "H5Inmembers");
if (err < 0)
goto out;
VERIFY(num_members, 1, "H5Inmembers");
if (num_members != 1)
goto out;
/* This call to dec_type_ref should destroy the type */
num_ref = H5Idec_type_ref(myType);
VERIFY(num_ref, 0, "H5Idec_type_ref");
if (num_ref != 0)
goto out;
VERIFY(H5Itype_exists(myType), 0, "H5Itype_exists");
if (H5Itype_exists(myType) != 0)
goto out;
H5E_BEGIN_TRY
err = H5Inmembers(myType, &num_members);
if (err >= 0)
goto out;
H5E_END_TRY
return 0;
out:
/* Clean up type if it has been allocated and free memory used
* by testObj
*/
if (myType >= 0)
H5Idestroy_type(myType);
return -1;
}
/* A dummy search function for the next test */
static int
test_search_func(void H5_ATTR_UNUSED *ptr1, hid_t H5_ATTR_UNUSED id, void H5_ATTR_UNUSED *ptr2)
{
return 0;
}
/* Ensure that public functions cannot access "predefined" ID types */
static int
id_predefined_test(void)
{
void *testObj;
hid_t testID;
hid_t typeID = H5I_INVALID_HID;
void *testPtr;
herr_t testErr;
testObj = malloc(sizeof(int));
/*
* Attempt to perform public functions on various library types
*/
H5E_BEGIN_TRY
testID = H5Iregister(H5I_FILE, testObj);
H5E_END_TRY
VERIFY(testID, H5I_INVALID_HID, "H5Iregister");
if (testID != H5I_INVALID_HID)
goto out;
H5E_BEGIN_TRY
testPtr = H5Isearch(H5I_GENPROP_LST, test_search_func, testObj);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Isearch");
if (testPtr != NULL)
goto out;
H5E_BEGIN_TRY
testErr = H5Inmembers(H5I_ERROR_STACK, NULL);
H5E_END_TRY
VERIFY(testErr, -1, "H5Inmembers");
if (testErr != -1)
goto out;
H5E_BEGIN_TRY
testErr = H5Iclear_type(H5I_FILE, 0);
H5E_END_TRY
VERIFY((testErr >= 0), 0, "H5Iclear_type");
if (testErr >= 0)
goto out;
H5E_BEGIN_TRY
testErr = H5Idestroy_type(H5I_DATASET);
H5E_END_TRY
VERIFY((testErr >= 0), 0, "H5Idestroy_type");
if (testErr >= 0)
goto out;
H5E_BEGIN_TRY
testErr = H5Itype_exists(H5I_GROUP);
H5E_END_TRY
VERIFY(testErr, -1, "H5Itype_exists");
if (testErr != -1)
goto out;
H5E_BEGIN_TRY
testErr = H5Itype_exists(H5I_ATTR);
H5E_END_TRY
VERIFY(testErr, -1, "H5Itype_exists");
if (testErr != -1)
goto out;
/*
* Create a datatype ID and try to perform illegal functions on it
*/
typeID = H5Tcreate(H5T_OPAQUE, (size_t)42);
CHECK(typeID, H5I_INVALID_HID, "H5Tcreate");
if (typeID == H5I_INVALID_HID)
goto out;
H5E_BEGIN_TRY
testPtr = H5Iremove_verify(typeID, H5I_DATATYPE);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iremove_verify");
if (testPtr != NULL)
goto out;
H5E_BEGIN_TRY
testPtr = H5Iobject_verify(typeID, H5I_DATATYPE);
H5E_END_TRY
CHECK_PTR_NULL(testPtr, "H5Iobject_verify");
if (testPtr != NULL)
goto out;
H5Tclose(typeID);
/* testObj was never registered as an atom, so it will not be
* automatically freed. */
free(testObj);
return 0;
out:
if (typeID != H5I_INVALID_HID)
H5Tclose(typeID);
if (testObj != NULL)
free(testObj);
return -1;
}
/* Test the H5Iis_valid function */
static int
test_is_valid(void)
{
hid_t dtype; /* datatype id */
int64_t nmembs1; /* number of type memnbers */
int64_t nmembs2;
htri_t tri_ret; /* htri_t return value */
herr_t ret; /* return value */
/* Create a datatype id */
dtype = H5Tcopy(H5T_NATIVE_INT);
CHECK(dtype, FAIL, "H5Tcopy");
if (dtype < 0)
goto out;
/* Check that the ID is valid */
tri_ret = H5Iis_valid(dtype);
VERIFY(tri_ret, true, "H5Iis_valid");
if (tri_ret != true)
goto out;
/* Artificially manipulate the reference counts so app_count is 0, and dtype
* appears to be an internal id. This takes advantage of the fact that
* H5Ipkg is included.
*/
ret = H5I_inc_ref(dtype, false);
CHECK(ret, FAIL, "H5I_inc_ref");
if (ret < 0)
goto out;
ret = H5I_dec_app_ref(dtype);
CHECK(ret, FAIL, "H5I_dec_ref");
if (ret < 0)
goto out;
/* Check that dtype is invalid */
tri_ret = H5Iis_valid(dtype);
VERIFY(tri_ret, false, "H5Iis_valid");
if (tri_ret != false)
goto out;
/* Close dtype and verify that it has been closed */
nmembs1 = H5I_nmembers(H5I_DATATYPE);
CHECK(nmembs1, FAIL, "H5I_nmembers");
if (nmembs1 < 0)
goto out;
ret = H5I_dec_ref(dtype);
CHECK(ret, FAIL, "H5I_dec_ref");
if (ret < 0)
goto out;
nmembs2 = H5I_nmembers(H5I_DATATYPE);
VERIFY(nmembs2, nmembs1 - 1, "H5I_nmembers");
if (nmembs2 != nmembs1 - 1)
goto out;
/* Check that dtype is invalid */
tri_ret = H5Iis_valid(dtype);
VERIFY(tri_ret, false, "H5Iis_valid");
if (tri_ret != false)
goto out;
/* Check that an id of -1 is invalid */
tri_ret = H5Iis_valid((hid_t)H5I_INVALID_HID);
VERIFY(tri_ret, false, "H4Iis_valid");
if (tri_ret != false)
goto out;
return 0;
out:
/* Don't attempt to close dtype as we don't know the exact state of the
* reference counts. Every state in this function will be automatically
* closed at library exit anyways, as internal count is never > 1.
*/
return -1;
}
/* Test the H5Iget_type function */
static int
test_get_type(void)
{
hid_t dtype; /* datatype id */
H5I_type_t type_ret; /* return value */
/* Create a datatype id */
dtype = H5Tcopy(H5T_NATIVE_INT);
CHECK(dtype, FAIL, "H5Tcopy");
if (dtype < 0)
goto out;
/* Check that the ID is correct */
type_ret = H5Iget_type(dtype);
VERIFY(type_ret, H5I_DATATYPE, "H5Iget_type");
if (type_ret == H5I_BADID)
goto out;
/* Check that the ID is correct */
type_ret = H5Iget_type((hid_t)H5T_STRING);
VERIFY(type_ret, H5I_BADID, "H5Iget_type");
if (type_ret != H5I_BADID)
goto out;
/* Check that the ID is correct */
type_ret = H5Iget_type((hid_t)H5I_INVALID_HID);
VERIFY(type_ret, H5I_BADID, "H5Iget_type");
if (type_ret != H5I_BADID)
goto out;
H5Tclose(dtype);
return 0;
out:
if (dtype != H5I_INVALID_HID)
H5Tclose(dtype);
return -1;
}
/* Test boundary cases with lots of types */
/* Type IDs range from H5I_NTYPES to H5I_MAX_NUM_TYPES. The system will assign */
/* IDs in sequential order until H5I_MAX_NUM_TYPES IDs have been given out, at which */
/* point it will search for type IDs that were allocated but have since been */
/* deleted. */
/* This test will allocate IDs up to H5I_MAX_NUM_TYPES, ensure that IDs wrap around */
/* to low values successfully, ensure that an error is thrown when all possible */
/* type IDs are taken, then ensure that deleting types frees up their IDs. */
/* Note that this test depends on the implementation of IDs, so may break */
/* if the implementation changes. */
/* Also note that if someone else registered a user-defined type and forgot to */
/* destroy it, this test will mysteriously fail (because it will expect there to */
/* be one more "free" type ID than there is). */
/* H5I_NTYPES is defined in h5public.h, H5I_MAX_NUM_TYPES is defined in h5pkg.h */
static int
test_id_type_list(void)
{
H5I_type_t startType; /* The first type ID we were assigned in this test */
H5I_type_t currentType;
H5I_type_t testType;
int i; /* Just a counter variable */
startType = H5Iregister_type((size_t)8, 0, free_wrapper);
CHECK(startType, H5I_BADID, "H5Iregister_type");
if (startType == H5I_BADID)
goto out;
/* Sanity check */
if ((int)startType >= H5I_MAX_NUM_TYPES || startType < H5I_NTYPES) {
/* Error condition, throw an error */
ERROR("H5Iregister_type");
goto out;
}
/* Create types up to H5I_MAX_NUM_TYPES */
for (i = startType + 1; i < H5I_MAX_NUM_TYPES; i++) {
currentType = H5Iregister_type((size_t)8, 0, free_wrapper);
CHECK(currentType, H5I_BADID, "H5Iregister_type");
if (currentType == H5I_BADID)
goto out;
}
/* Wrap around to low type ID numbers */
for (i = H5I_NTYPES; i < startType; i++) {
currentType = H5Iregister_type((size_t)8, 0, free_wrapper);
CHECK(currentType, H5I_BADID, "H5Iregister_type");
if (currentType == H5I_BADID)
goto out;
}
/* There should be no room at the inn for a new ID type*/
H5E_BEGIN_TRY
testType = H5Iregister_type((size_t)8, 0, free_wrapper);
H5E_END_TRY
VERIFY(testType, H5I_BADID, "H5Iregister_type");
if (testType != H5I_BADID)
goto out;
/* Now delete a type and try to insert again */
H5Idestroy_type(H5I_NTYPES);
testType = H5Iregister_type((size_t)8, 0, free_wrapper);
VERIFY(testType, H5I_NTYPES, "H5Iregister_type");
if (testType != H5I_NTYPES)
goto out;
/* Cleanup. Destroy all types. */
for (i = H5I_NTYPES; i < H5I_MAX_NUM_TYPES; i++)
H5Idestroy_type((H5I_type_t)i);
return 0;
out:
/* Cleanup. For simplicity, just destroy all types and ignore errors. */
H5E_BEGIN_TRY
for (i = H5I_NTYPES; i < H5I_MAX_NUM_TYPES; i++)
H5Idestroy_type((H5I_type_t)i);
H5E_END_TRY
return -1;
}
/* Test removing ids in callback for H5Iclear_type */
/* There was a rare bug where, if an id free callback being called by
* H5I_clear_type() removed another id in that type, a segfault could occur.
* This test tests for that error (and freeing ids "out of order" within
* H5Iclear_type() in general).
*
* NB: RCT = "remove clear type"
*/
/* Macro definitions */
#define RCT_MAX_NOBJS 25 /* Maximum number of objects in the list */
#define RCT_MIN_NOBJS 5
#define RCT_NITER 50 /* Number of times we cycle through object creation and deletion */
/* Structure to hold the master list of objects */
typedef struct rct_obj_list_t {
/* Pointer to the objects */
struct rct_obj_t *objects;
/* The number of objects in the list */
long count;
/* The number of objects in the list that have not been freed */
long remaining;
} rct_obj_list_t;
/* Structure for an object */
typedef struct rct_obj_t {
/* The ID for this object */
hid_t id;
/* The number of times this object has been freed */
int nfrees;
/* Whether we are currently freeing this object directly
* through H5Idec_ref().
*/
bool freeing;
/* Pointer to the master list of all objects */
rct_obj_list_t *list;
} rct_obj_t;
/* Free callback passed to H5Iclear_type()
*
* When invoked on a closing object, frees a random unfreed ID in the
* master list of objects.
*/
static herr_t
rct_free_cb(void *_obj, void H5_ATTR_UNUSED **_ctx)
{
rct_obj_t *obj = (rct_obj_t *)_obj;
long remove_nth;
long i;
herr_t ret;
/* Mark this object as freed */
obj->nfrees++;
/* Decrement the number of objects in the list that have not been freed */
obj->list->remaining--;
/* If this object isn't already being freed by a callback free call and
* the master object list still contains objects to free, pick another
* object and free it.
*/
if (!obj->freeing && (obj->list->remaining > 0)) {
/* Pick a random object from the list. This is done by picking a
* random number between 0 and the # of remaining unfreed objects
* and then scanning through the list to find that nth unfreed
* object.
*/
remove_nth = rand() % obj->list->remaining;
for (i = 0; i < obj->list->count; i++)
if (obj->list->objects[i].nfrees == 0) {
if (remove_nth == 0)
break;
else
remove_nth--;
}
/* Badness if we scanned through the list and didn't manage to
* select one to delete (the list stats were probably updated
* incorrectly).
*/
if (i == obj->list->count) {
ERROR("invalid obj_list");
goto error;
}
/* Mark the object we're about to free so its own callback does
* not free another object. We don't want to recursively free the
* entire list when we free the first ID.
*/
obj->list->objects[i].freeing = true;
/* Decrement the reference count on the object */
ret = H5Idec_ref(obj->list->objects[i].id);
CHECK(ret, FAIL, "H5Idec_ref");
if (ret == FAIL)
goto error;
/* Unset the "freeing" flag */
obj->list->objects[i].freeing = false;
}
/* Verify the number of objects remaining in the master list is non-negative */
if (obj->list->remaining < 0) {
ERROR("invalid number of objects remaining");
goto error;
}
return 0;
error:
return -1;
} /* end rct_free_cb() */
/* Test function */
static int
test_remove_clear_type(void)
{
H5I_type_t obj_type;
rct_obj_list_t obj_list;
rct_obj_t *objects = NULL; /* Convenience pointer to objects stored in master list */
size_t list_size;
long i, j;
herr_t ret; /* return value */
/* Register a user-defined type with our custom ID-deleting callback */
obj_type = H5Iregister_type((size_t)8, 0, rct_free_cb);
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
if (obj_type == H5I_BADID)
goto error;
/* Create an array to hold the objects in the master list */
list_size = RCT_MAX_NOBJS * sizeof(rct_obj_t);
obj_list.objects = malloc(list_size);
CHECK_PTR(obj_list.objects, "calloc");
if (NULL == obj_list.objects)
goto error;
/* Set a convenience pointer to the object array */
objects = obj_list.objects;
for (i = 0; i < RCT_NITER; i++) {
/* The number of members in the type, according to the HDF5 library */
hsize_t nmembers = 1234567; /* (init to fake number) */
/* The number of objects found while scanning through the object list */
int found;
/*********************
* Build object list *
*********************/
memset(obj_list.objects, 0, list_size);
/* The number of objects used is a random number between the min and max */
obj_list.count = obj_list.remaining =
RCT_MIN_NOBJS + (rand() % (long)(RCT_MAX_NOBJS - RCT_MIN_NOBJS + 1));
/* Create the actual objects */
for (j = 0; j < obj_list.count; j++) {
/* Object setup */
objects[j].nfrees = 0;
objects[j].freeing = false;
objects[j].list = &obj_list;
/* Register an ID for it */
objects[j].id = H5Iregister(obj_type, &objects[j]);
CHECK(objects[j].id, FAIL, "H5Iregister");
if (objects[j].id == FAIL)
goto error;
/* Bump the reference count by 1 (to 2) 50% of the time */
if (rand() % 2) {
ret = H5Iinc_ref(objects[j].id);
CHECK(ret, FAIL, "H5Iinc_ref");
if (ret == FAIL)
goto error;
}
}
/******************************************
* Clear the type with force set to false *
******************************************/
/* Clear the type. Since force is false, only
* IDs with a reference count of 1 will be cleared.
*/
ret = H5Iclear_type(obj_type, false);
CHECK(ret, FAIL, "H5Iclear_type");
if (ret == FAIL)
goto error;
/* Verify that the object struct fields are sane and count the
* number of unfreed objects
*/
found = 0;
for (j = 0; j < obj_list.count; j++) {
if (objects[j].nfrees == 0) {
/* Count unfreed objects */
found++;
}
else {
/* Every freed object should have been freed exactly once */
VERIFY(objects[j].nfrees, 1, "object freed more than once");
if (objects[j].nfrees != 1)
goto error;
}
/* No object should still be marked as "freeing" */
VERIFY(objects[j].freeing, false, "object marked as freeing");
if (objects[j].freeing != false)
goto error;
}
/* Verify the number of unfreed objects we found during our scan
* matches the number stored in the list
*/
VERIFY(obj_list.remaining, found, "incorrect number of objects remaining");
if (obj_list.remaining != found)
goto error;
/* Make sure the HDF5 library confirms our count */
ret = H5Inmembers(obj_type, &nmembers);
CHECK(ret, FAIL, "H5Inmembers");
if (ret == FAIL)
goto error;
VERIFY(nmembers, found, "The number of members remaining in the type did not match our count");
if (nmembers != (hsize_t)found)
goto error;
/*****************************************
* Clear the type with force set to true *
*****************************************/
/* Clear the type. Since force is true, all IDs will be cleared. */
ret = H5Iclear_type(obj_type, true);
CHECK(ret, FAIL, "H5Iclear_type");
if (ret == FAIL)
goto error;
/* Verify that the object struct fields are sane */
for (j = 0; j < obj_list.count; j++) {
/* Every object should have been freed exactly once */
VERIFY(objects[j].nfrees, 1, "object freed more than once");
if (objects[j].nfrees != 1)
goto error;
/* No object should still be marked as "freeing" */
VERIFY(objects[j].freeing, false, "object marked as freeing");
if (objects[j].freeing != false)
goto error;
}
/* Verify the number of objects is 0 */
VERIFY(obj_list.remaining, 0, "objects remaining was not zero");
if (obj_list.remaining != 0)
goto error;
/* Make sure the HDF5 library confirms zero members in the type */
ret = H5Inmembers(obj_type, &nmembers);
CHECK(ret, FAIL, "H5Inmembers");
if (ret == FAIL)
goto error;
VERIFY(nmembers, 0, "The number of members remaining in the type was not zero");
if (nmembers != 0)
goto error;
}
/* Destroy the type */
ret = H5Idestroy_type(obj_type);
CHECK(ret, FAIL, "H5Idestroy_type");
if (ret == FAIL)
goto error;
/* Free the object array */
free(obj_list.objects);
return 0;
error:
/* Cleanup. For simplicity, just destroy the types and ignore errors. */
H5E_BEGIN_TRY
{
H5Idestroy_type(obj_type);
}
H5E_END_TRY
free(obj_list.objects);
return -1;
} /* end test_remove_clear_type() */
/* Typedef for future objects */
typedef struct {
H5I_type_t obj_type; /* ID type for actual object */
} future_obj_t;
/* Global (static) future ID object type */
static H5I_type_t future_obj_type_g = H5I_BADID;
/* Callback to free the actual object for future object test */
static herr_t
free_actual_object(void *_p, void H5_ATTR_UNUSED **_ctx)
{
int *p = (int *)_p;
if (7 != *p)
return FAIL;
free(p);
return SUCCEED;
}
/* Callback to realize a future object */
static herr_t
realize_future_cb(void *_future_obj, hid_t *actual_id)
{
future_obj_t *future_obj = (future_obj_t *)_future_obj; /* Future object */
int *actual_obj; /* Pointer to the actual object */
/* Check for bad future object */
if (NULL == future_obj)
return FAIL;
/* Determine type of object to realize */
if (H5I_DATASPACE == future_obj->obj_type) {
hsize_t dims = 13;
if ((*actual_id = H5Screate_simple(1, &dims, NULL)) < 0)
return FAIL;
}
else if (H5I_DATATYPE == future_obj->obj_type) {
if ((*actual_id = H5Tcopy(H5T_NATIVE_INT)) < 0)
return FAIL;
}
else if (H5I_GENPROP_LST == future_obj->obj_type) {
if ((*actual_id = H5Pcreate(H5P_DATASET_XFER)) < 0)
return FAIL;
}
else {
/* Create a new object (the 'actual object') of the correct type */
if (NULL == (actual_obj = malloc(sizeof(int))))
return FAIL;
*actual_obj = 7;
/* Register actual object of the user-defined type */
*actual_id = H5Iregister(future_obj->obj_type, actual_obj);
CHECK(*actual_id, FAIL, "H5Iregister");
if (*actual_id == FAIL)
return FAIL;
}
return SUCCEED;
}
/* Callback to discard a future object */
static herr_t
discard_future_cb(void *future_obj)
{
if (NULL == future_obj)
return FAIL;
free(future_obj);
return SUCCEED;
}
/* Callback to realize a future object when future objects are NULL*/
static herr_t
realize_future_generate_cb(void *_future_obj, hid_t *actual_id)
{
future_obj_t *future_obj = (future_obj_t *)_future_obj; /* Future object */
int *actual_obj; /* Pointer to the actual object */
if (NULL != future_obj)
return FAIL;
/* Create a new object (the 'actual object') of the correct type */
if (NULL == (actual_obj = malloc(sizeof(int))))
return FAIL;
*actual_obj = 7;
/* Register actual object without using future object info */
*actual_id = H5Iregister(future_obj_type_g, actual_obj);
CHECK(*actual_id, FAIL, "H5Iregister");
if (*actual_id == FAIL)
return FAIL;
return SUCCEED;
}
/* Callback to discard a future object when future objects are NULL */
static herr_t
discard_future_generate_cb(void *future_obj)
{
if (NULL != future_obj)
return FAIL;
return SUCCEED;
}
/* Test function */
static int
test_future_ids(void)
{
H5I_type_t obj_type; /* New user-defined ID type */
hid_t future_id; /* ID for future object */
int fake_future_obj; /* "Fake" future object for tests */
future_obj_t *future_obj; /* Future object */
int *actual_obj; /* Actual object */
int *actual_obj2; /* Another actual object */
H5I_type_t id_type; /* Type of ID */
H5T_class_t type_class; /* Datatype class */
herr_t ret; /* Return value */
/* Register a user-defined type with our custom ID-deleting callback */
obj_type = H5Iregister_type((size_t)15, 0, free_actual_object);
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
if (H5I_BADID == obj_type)
goto error;
/* Test basic error conditions */
fake_future_obj = 0;
H5E_BEGIN_TRY
{
future_id = H5Iregister_future(obj_type, &fake_future_obj, NULL, NULL);
}
H5E_END_TRY
VERIFY(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID != future_id)
goto error;
H5E_BEGIN_TRY
{
future_id = H5Iregister_future(obj_type, &fake_future_obj, realize_future_cb, NULL);
}
H5E_END_TRY
VERIFY(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID != future_id)
goto error;
H5E_BEGIN_TRY
{
future_id = H5Iregister_future(obj_type, &fake_future_obj, NULL, discard_future_cb);
}
H5E_END_TRY
VERIFY(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID != future_id)
goto error;
H5E_BEGIN_TRY
{
future_id = H5Iregister_future(H5I_BADID, &fake_future_obj, realize_future_cb, discard_future_cb);
}
H5E_END_TRY
VERIFY(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID != future_id)
goto error;
/* Test base use-case: create a future object and destroy type without
* realizing the future object.
*/
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = obj_type;
future_id = H5Iregister_future(obj_type, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Destroy the type */
ret = H5Idestroy_type(obj_type);
CHECK(ret, FAIL, "H5Idestroy_type");
if (FAIL == ret)
goto error;
/* Re-register a user-defined type with our custom ID-deleting callback */
obj_type = H5Iregister_type((size_t)15, 0, free_actual_object);
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
if (H5I_BADID == obj_type)
goto error;
/* Test base use-case: create a future object and realize the actual object. */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = obj_type;
future_id = H5Iregister_future(obj_type, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
actual_obj = H5Iobject_verify(future_id, obj_type);
CHECK_PTR(actual_obj, "H5Iobject_verify");
if (NULL == actual_obj)
goto error;
VERIFY(*actual_obj, 7, "H5Iobject_verify");
if (7 != *actual_obj)
goto error;
/* Retrieve the object again and verify that it's the same actual object */
actual_obj2 = H5Iobject_verify(future_id, obj_type);
CHECK_PTR(actual_obj2, "H5Iobject_verify");
if (NULL == actual_obj2)
goto error;
VERIFY(*actual_obj2, 7, "H5Iobject_verify");
if (7 != *actual_obj2)
goto error;
CHECK_PTR_EQ(actual_obj, actual_obj2, "H5Iobject_verify");
if (actual_obj != actual_obj2)
goto error;
/* Destroy the type */
ret = H5Idestroy_type(obj_type);
CHECK(ret, FAIL, "H5Idestroy_type");
if (FAIL == ret)
goto error;
/* Re-register a user-defined type with our custom ID-deleting callback */
obj_type = H5Iregister_type((size_t)15, 0, free_actual_object);
CHECK(obj_type, H5I_BADID, "H5Iregister_type");
if (H5I_BADID == obj_type)
goto error;
/* Set the global future object type */
future_obj_type_g = obj_type;
/* Test "actual object generator" use-case: create a future object with
* NULL object pointer, to create new object of predefined type when
* future object is realized.
*/
future_id = H5Iregister_future(obj_type, NULL, realize_future_generate_cb, discard_future_generate_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Realize the actual object, with will be dynamically allocated within
* the 'realize' callback.
*/
actual_obj = H5Iobject_verify(future_id, obj_type);
CHECK_PTR(actual_obj, "H5Iobject_verify");
if (NULL == actual_obj)
goto error;
VERIFY(*actual_obj, 7, "H5Iobject_verify");
if (7 != *actual_obj)
goto error;
/* Reset the global future object type */
future_obj_type_g = H5I_BADID;
/* Retrieve the object again and verify that it's the same actual object */
/* (Will fail if global future object type used) */
actual_obj2 = H5Iobject_verify(future_id, obj_type);
CHECK_PTR(actual_obj2, "H5Iobject_verify");
if (NULL == actual_obj2)
goto error;
VERIFY(*actual_obj2, 7, "H5Iobject_verify");
if (7 != *actual_obj2)
goto error;
CHECK_PTR_EQ(actual_obj, actual_obj2, "H5Iobject_verify");
if (actual_obj != actual_obj2)
goto error;
/* Destroy the type */
ret = H5Idestroy_type(obj_type);
CHECK(ret, FAIL, "H5Idestroy_type");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
/* (DATASPACE) */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATASPACE;
future_id = H5Iregister_future(H5I_DATASPACE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* (Can't verify the type of the future ID, because the library's current
* implementation realizes the object during sanity checks on the ID)
*/
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATASPACE;
future_id = H5Iregister_future(H5I_DATASPACE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Verify that the application believes the future ID is a dataspace */
/* (Currently realizes the object "implicitly" during a sanity check) */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_DATASPACE != id_type)
goto error;
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATASPACE;
future_id = H5Iregister_future(H5I_DATASPACE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Realize future dataspace by requesting its rank */
ret = H5Sget_simple_extent_ndims(future_id);
CHECK(ret, FAIL, "H5Sget_simple_extent_ndims");
if (FAIL == ret)
goto error;
if (1 != ret)
goto error;
/* Verify that the application believes the ID is still a dataspace */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_DATASPACE != id_type)
goto error;
/* Close future object for pre-defined type after realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
/* (DATATYPE) */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATATYPE;
future_id = H5Iregister_future(H5I_DATATYPE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* (Can't verify the type of the future ID, because the library's current
* implementation realizes the object during sanity checks on the ID)
*/
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATATYPE;
future_id = H5Iregister_future(H5I_DATATYPE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Verify that the application believes the future ID is a datatype */
/* (Currently realizes the object "implicitly" during a sanity check) */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_DATATYPE != id_type)
goto error;
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_DATATYPE;
future_id = H5Iregister_future(H5I_DATATYPE, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Realize future datatype by requesting its class */
type_class = H5Tget_class(future_id);
CHECK(ret, FAIL, "H5Tget_class");
if (FAIL == ret)
goto error;
if (H5T_INTEGER != type_class)
goto error;
/* Verify that the application believes the ID is still a datatype */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_DATATYPE != id_type)
goto error;
/* Close future object for pre-defined type after realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
/* (PROPERTY LIST) */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_GENPROP_LST;
future_id = H5Iregister_future(H5I_GENPROP_LST, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* (Can't verify the type of the future ID, because the library's current
* implementation realizes the object during sanity checks on the ID)
*/
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_GENPROP_LST;
future_id = H5Iregister_future(H5I_GENPROP_LST, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Verify that the application believes the future ID is a property list */
/* (Currently realizes the object "implicitly" during a sanity check) */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_GENPROP_LST != id_type)
goto error;
/* Close future object for pre-defined type without realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
/* Test base use-case: create a future object for a pre-defined type */
future_obj = malloc(sizeof(future_obj_t));
future_obj->obj_type = H5I_GENPROP_LST;
future_id = H5Iregister_future(H5I_GENPROP_LST, future_obj, realize_future_cb, discard_future_cb);
CHECK(future_id, H5I_INVALID_HID, "H5Iregister_future");
if (H5I_INVALID_HID == future_id)
goto error;
/* Realize future property list by verifying its class */
ret = H5Pisa_class(future_id, H5P_DATASET_XFER);
CHECK(ret, FAIL, "H5Pisa_class");
if (FAIL == ret)
goto error;
if (true != ret)
goto error;
/* Verify that the application believes the ID is still a property list */
id_type = H5Iget_type(future_id);
CHECK(id_type, H5I_BADID, "H5Iget_type");
if (H5I_BADID == id_type)
goto error;
if (H5I_GENPROP_LST != id_type)
goto error;
/* Close future object for pre-defined type after realizing it */
ret = H5Idec_ref(future_id);
CHECK(ret, FAIL, "H5Idec_ref");
if (FAIL == ret)
goto error;
return 0;
error:
/* Cleanup. For simplicity, just destroy the types and ignore errors. */
H5E_BEGIN_TRY
{
H5Idestroy_type(obj_type);
}
H5E_END_TRY
return -1;
} /* end test_future_ids() */
/*-------------------------------------------------------------------------
* Function: test_appropriate_ids
*
* Purpose: Tests several API functions on detecting inappropriate ID.
*
* Return: Success: 0
* Failure: number of errors
*
*-------------------------------------------------------------------------
*/
static int
test_appropriate_ids(void)
{
hid_t file_id = H5I_INVALID_HID;
hid_t fapl_id = H5I_INVALID_HID;
hid_t fcpl_id = H5I_INVALID_HID;
hid_t plist = H5I_INVALID_HID;
hid_t dset_id = H5I_INVALID_HID;
hid_t space_id = H5I_INVALID_HID;
hsize_t dims = 2;
hssize_t free_space;
herr_t ret = SUCCEED; /* Generic return value */
/* Create file create property list */
fcpl_id = H5Pcreate(H5P_FILE_CREATE);
CHECK(fcpl_id, H5I_INVALID_HID, "H5Pcreate");
file_id = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, fcpl_id, H5P_DEFAULT);
CHECK(file_id, H5I_INVALID_HID, "H5Fcreate");
/* Create a dataset in the file */
space_id = H5Screate_simple(1, &dims, NULL);
CHECK(space_id, H5I_INVALID_HID, "H5Screate_simple");
dset_id = H5Dcreate2(file_id, DSET_NAME, H5T_NATIVE_INT, space_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
CHECK(dset_id, H5I_INVALID_HID, "H5Dcreate2");
/* Close IDs */
ret = H5Pclose(fcpl_id);
CHECK(ret, FAIL, "H5Pclose");
ret = H5Sclose(space_id);
CHECK(ret, FAIL, "H5Sclose");
ret = H5Dclose(dset_id);
CHECK(ret, FAIL, "H5Dclose");
ret = H5Fclose(file_id);
CHECK(ret, FAIL, "H5Fclose");
file_id = H5Fopen(FILE_NAME, H5F_ACC_RDONLY, H5P_DEFAULT);
CHECK(file_id, H5I_INVALID_HID, "H5Fopen");
/* Get the file create property */
fcpl_id = H5Fget_create_plist(file_id);
CHECK(fcpl_id, H5I_INVALID_HID, "H5Fget_create_plist");
/* Get the file access property */
fapl_id = H5Fget_access_plist(file_id);
CHECK(fapl_id, H5I_INVALID_HID, "H5Fget_access_plist");
dset_id = H5Dopen2(file_id, DSET_NAME, H5P_DEFAULT);
CHECK(dset_id, H5I_INVALID_HID, "H5Dopen2");
/*-------------------------------------------------------------
* Try to call functions passing in a wrong ID
*-----------------------------------------------------------*/
H5E_BEGIN_TRY
{
plist = H5Fget_create_plist(dset_id); /* dset_id is not file ID */
}
H5E_END_TRY
VERIFY(plist, H5I_INVALID_HID, "H5Fget_create_plist");
H5E_BEGIN_TRY
{
plist = H5Fget_access_plist(fapl_id); /* fapl_id is not file ID */
}
H5E_END_TRY
VERIFY(plist, H5I_INVALID_HID, "H5Fget_access_plist");
H5E_BEGIN_TRY
{
unsigned intent; /* File access flags */
ret = H5Fget_intent(dset_id, &intent); /* dset_id is not file ID */
}
H5E_END_TRY
VERIFY(ret, FAIL, "H5Fget_intent");
H5E_BEGIN_TRY
{
unsigned long fileno = 0;
ret = H5Fget_fileno(dset_id, &fileno); /* dset_id is not file ID */
}
H5E_END_TRY
VERIFY(ret, FAIL, "H5Fget_fileno");
H5E_BEGIN_TRY
{
free_space = H5Fget_freespace(dset_id); /* dset_id is not file ID */
}
H5E_END_TRY
VERIFY(free_space, FAIL, "H5Fget_freespace");
H5E_BEGIN_TRY
{
void *os_file_handle = NULL; /* OS file handle */
ret = H5Fget_vfd_handle(fapl_id, H5P_DEFAULT, &os_file_handle); /* fapl_id is not file ID */
}
H5E_END_TRY
VERIFY(ret, FAIL, "H5Fget_vfd_handle");
/* Close IDs */
ret = H5Pclose(fapl_id);
CHECK(ret, FAIL, "H5Pclose");
ret = H5Pclose(fcpl_id);
CHECK(ret, FAIL, "H5Pclose");
ret = H5Dclose(dset_id);
CHECK(ret, FAIL, "H5Dclose");
ret = H5Fclose(file_id);
CHECK(ret, FAIL, "H5Fclose");
return 0;
}
void
test_ids(const void H5_ATTR_UNUSED *params)
{
/* Set the random # seed */
srand((unsigned)time(NULL));
if (basic_id_test() < 0)
TestErrPrintf("Basic ID test failed\n");
if (id_predefined_test() < 0)
TestErrPrintf("Predefined ID type test failed\n");
if (test_is_valid() < 0)
TestErrPrintf("H5Iis_valid test failed\n");
if (test_get_type() < 0)
TestErrPrintf("H5Iget_type test failed\n");
if (test_id_type_list() < 0)
TestErrPrintf("ID type list test failed\n");
if (test_remove_clear_type() < 0)
TestErrPrintf("ID remove during H5Iclear_type test failed\n");
if (test_future_ids() < 0)
TestErrPrintf("Future ID test failed\n");
if (test_appropriate_ids() < 0)
TestErrPrintf("Detection of inappropriate ID test failed\n");
}