diff options
-rw-r--r-- | src/H5T.c | 21 | ||||
-rw-r--r-- | src/H5Tcompound.c | 146 | ||||
-rw-r--r-- | test/cmpd_dset.c | 251 |
3 files changed, 384 insertions, 34 deletions
@@ -2973,8 +2973,10 @@ H5T_create(H5T_class_t type, size_t size) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); dt->shared->type = type; - if(type==H5T_COMPOUND) + if(type==H5T_COMPOUND) { dt->shared->u.compnd.packed=TRUE; /* Start out packed */ + dt->shared->u.compnd.sorted=H5T_SORT_VALUE; /* Start out sorted by value */ + } /* end if */ else if(type==H5T_OPAQUE) /* Initialize the tag in case it's not set later. A null tag will * cause problems for later operations. */ @@ -3173,13 +3175,16 @@ H5T_copy(const H5T_t *old_dt, H5T_copy_t method) * name and type fields of each new member with copied values. * That is, H5T_copy() is a deep copy. */ - new_dt->shared->u.compnd.memb = H5MM_malloc(new_dt->shared->u.compnd.nalloc * - sizeof(H5T_cmemb_t)); - if (NULL==new_dt->shared->u.compnd.memb) - HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); - - HDmemcpy(new_dt->shared->u.compnd.memb, old_dt->shared->u.compnd.memb, - new_dt->shared->u.compnd.nmembs * sizeof(H5T_cmemb_t)); + /* Only malloc if space has been allocated for members - NAF */ + if(new_dt->shared->u.compnd.nalloc > 0) { + new_dt->shared->u.compnd.memb = H5MM_malloc(new_dt->shared->u.compnd.nalloc * + sizeof(H5T_cmemb_t)); + if (NULL==new_dt->shared->u.compnd.memb) + HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed"); + + HDmemcpy(new_dt->shared->u.compnd.memb, old_dt->shared->u.compnd.memb, + new_dt->shared->u.compnd.nmembs * sizeof(H5T_cmemb_t)); + } /* end if */ for(i = 0; i < new_dt->shared->u.compnd.nmembs; i++) { unsigned j; diff --git a/src/H5Tcompound.c b/src/H5Tcompound.c index 68de174..02d6bd1 100644 --- a/src/H5Tcompound.c +++ b/src/H5Tcompound.c @@ -45,6 +45,11 @@ /******************/ /* Local Typedefs */ /******************/ +/* "Key" (+ user data) for bsearch callback */ +typedef struct{ + size_t offset; /* Offset of member to be added */ + const H5T_cmemb_t *max_under; /* Member with maximum offset seen that is not above "offset" */ +} H5T_insert_compar_t; /********************/ @@ -416,6 +421,48 @@ done: /*------------------------------------------------------------------------- + * Function: H5T_insert_compar + * + * Purpose: Callback function for bsearch called from H5T_insert. + * Reports whether obj has a lower of higher offset than + * that stored in key. Also keeps track of the highest + * offset seen that is not higher than that in key. + * + * Return: -1 if key < obj + * 0 if key == obj + * 1 if key > obj + * + * Programmer: Neil Fortner + * Wednesday, January 7, 1998 + * + * Modifications: + * + *------------------------------------------------------------------------- + */ +static int +H5T_insert_compar(const void *_key, const void *_obj) +{ + H5T_insert_compar_t *key = *(H5T_insert_compar_t * const *)_key; /* User data */ + const H5T_cmemb_t *memb = (const H5T_cmemb_t *)_obj; /* Compound member being examined */ + int ret_value; /* Return value */ + + FUNC_ENTER_NOAPI_NOINIT_NOFUNC(H5T_insert_compar) + + if(key->offset > memb->offset) { + if(key->max_under == NULL || memb->offset > key->max_under->offset) + key->max_under = memb; + ret_value = 1; + } /* end if */ + else if(key->offset < memb->offset) + ret_value = -1; + else + ret_value = 0; /* Should not happen */ + + FUNC_LEAVE_NOAPI(ret_value) +} /* end H5T_insert_compar() */ + + +/*------------------------------------------------------------------------- * Function: H5T_insert * * Purpose: Adds a new MEMBER to the compound datatype PARENT. The new @@ -435,6 +482,8 @@ H5T_insert(H5T_t *parent, const char *name, size_t offset, const H5T_t *member) { unsigned idx; /* Index of member to insert */ size_t total_size; + H5T_insert_compar_t key; /* Key for bsearch compare function */ + H5T_insert_compar_t *keyptr = &key; /* Pointer to key */ unsigned i; /* Local index variable */ herr_t ret_value = SUCCEED; /* Return value */ @@ -451,20 +500,39 @@ H5T_insert(H5T_t *parent, const char *name, size_t offset, const H5T_t *member) if(!HDstrcmp(parent->shared->u.compnd.memb[i].name, name)) HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINSERT, FAIL, "member name is not unique") - /* Does the new member overlap any existing member ? */ total_size = member->shared->size; - for(i = 0; i < parent->shared->u.compnd.nmembs; i++) - if((offset <= parent->shared->u.compnd.memb[i].offset && - (offset + total_size) > parent->shared->u.compnd.memb[i].offset) || - (parent->shared->u.compnd.memb[i].offset <= offset && - (parent->shared->u.compnd.memb[i].offset + - parent->shared->u.compnd.memb[i].size) > offset)) - HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINSERT, FAIL, "member overlaps with another member") /* Does the new member overlap the end of the compound type? */ if((offset + total_size) > parent->shared->size) HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINSERT, FAIL, "member extends past end of compound type") + if(parent->shared->u.compnd.sorted != H5T_SORT_VALUE) + if(H5T_sort_value(parent, NULL) < 0) + HGOTO_ERROR(H5E_INTERNAL, H5E_CANTCOMPARE, FAIL, "value sort failed") + + /* Find the position to insert the new member */ + if(parent->shared->u.compnd.nmembs == 0) + idx = 0; + else { + /* Key value (including user data) for compar callback */ + key.offset = offset; + key.max_under = NULL; + + /* Do a binary search on the offsets of the (now sorted) members. We do + * not expect to find an exact match (if we do it is an error), rely on + * the user data in the key to keep track of the closest member below + * the new member. */ + if(NULL != HDbsearch(&keyptr, parent->shared->u.compnd.memb, parent->shared->u.compnd.nmembs, + sizeof(parent->shared->u.compnd.memb[0]), H5T_insert_compar)) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINSERT, FAIL, "member overlaps with another member") + idx = (key.max_under == NULL) ? 0 : (unsigned) (key.max_under - parent->shared->u.compnd.memb + 1); + } /* end else */ + + /* Does the new member overlap any existing member ? */ + if((idx < parent->shared->u.compnd.nmembs && (offset + total_size) > parent->shared->u.compnd.memb[idx].offset) || + (idx && (parent->shared->u.compnd.memb[idx-1].offset + parent->shared->u.compnd.memb[idx-1].size) > offset)) + HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINSERT, FAIL, "member overlaps with another member") + /* Increase member array if necessary */ if(parent->shared->u.compnd.nmembs >= parent->shared->u.compnd.nalloc) { unsigned na = MAX(1, parent->shared->u.compnd.nalloc * 2); @@ -476,38 +544,68 @@ H5T_insert(H5T_t *parent, const char *name, size_t offset, const H5T_t *member) parent->shared->u.compnd.memb = x; } /* end if */ - /* Add member to end of member array */ - idx = parent->shared->u.compnd.nmembs; - parent->shared->u.compnd.memb[idx].name = H5MM_xstrdup(name); - parent->shared->u.compnd.memb[idx].offset = offset; - parent->shared->u.compnd.memb[idx].size = total_size; - parent->shared->u.compnd.memb[idx].type = H5T_copy(member, H5T_COPY_ALL); - - parent->shared->u.compnd.sorted = H5T_SORT_NONE; - parent->shared->u.compnd.nmembs++; - - /* Determine if the compound datatype stayed packed */ + /* Determine if the compound datatype stays packed */ if(parent->shared->u.compnd.packed) { /* Check if the member type is packed */ - if(H5T_is_packed(parent->shared->u.compnd.memb[idx].type) > 0) { + if(H5T_is_packed(member) > 0) { if(idx == 0) { /* If the is the first member, the datatype is not packed * if the first member isn't at offset 0 */ - if(parent->shared->u.compnd.memb[idx].offset > 0) + if(offset > 0) parent->shared->u.compnd.packed = FALSE; } /* end if */ else { /* If the is not the first member, the datatype is not * packed if the new member isn't adjoining the previous member */ - if(parent->shared->u.compnd.memb[idx].offset != (parent->shared->u.compnd.memb[idx - 1].offset + parent->shared->u.compnd.memb[idx - 1].size)) + if(offset != (parent->shared->u.compnd.memb[idx - 1].offset + parent->shared->u.compnd.memb[idx - 1].size)) parent->shared->u.compnd.packed = FALSE; } /* end else */ } /* end if */ else parent->shared->u.compnd.packed = FALSE; } /* end if */ + else + /* Check if inserting this member causes the parent to become packed */ + /* First check if it completely closes a gap */ + /* No need to check if it's being appended to the end */ + if(idx != parent->shared->u.compnd.nmembs + && (offset + total_size) == parent->shared->u.compnd.memb[idx].offset + && (idx == 0 ? offset == 0 : (parent->shared->u.compnd.memb[idx-1].offset + + parent->shared->u.compnd.memb[idx-1].size) == offset) + && H5T_is_packed(member) > 0) { + + /* Start out packed */ + parent->shared->u.compnd.packed = TRUE; + + /* Check if the entire type is now packed */ + if((idx != 0 && parent->shared->u.compnd.memb[0].offset != 0) + || !H5T_is_packed(parent->shared->u.compnd.memb[0].type)) + parent->shared->u.compnd.packed = FALSE; + else + for(i = 1; i < parent->shared->u.compnd.nmembs; i++) + if((i != idx && parent->shared->u.compnd.memb[i].offset + != (parent->shared->u.compnd.memb[i - 1].offset + + parent->shared->u.compnd.memb[i - 1].size)) + || !H5T_is_packed(parent->shared->u.compnd.memb[i].type)) { + parent->shared->u.compnd.packed = FALSE; + break; + } /* end if */ + } /* end if */ + + /* Reshape the memb array to accomodate the new member */ + if(idx != parent->shared->u.compnd.nmembs) + HDmemmove(&parent->shared->u.compnd.memb[idx+1], &parent->shared->u.compnd.memb[idx], + (parent->shared->u.compnd.nmembs - idx) * sizeof(parent->shared->u.compnd.memb[0])); + + /* Add member to member array */ + parent->shared->u.compnd.memb[idx].name = H5MM_xstrdup(name); + parent->shared->u.compnd.memb[idx].offset = offset; + parent->shared->u.compnd.memb[idx].size = total_size; + parent->shared->u.compnd.memb[idx].type = H5T_copy(member, H5T_COPY_ALL); + + parent->shared->u.compnd.nmembs++; /* Set the "force conversion" flag if the field's datatype indicates */ if(member->shared->force_conv == TRUE) @@ -633,11 +731,11 @@ H5T_is_packed(const H5T_t *dt) /* If this is a compound datatype, check if it is packed */ if(dt->shared->type == H5T_COMPOUND) { H5T_compnd_t *compnd = &(dt->shared->u.compnd); /* Convenience pointer to compound info */ - ret_value = (htri_t)(compnd->packed + ret_value = (htri_t)(compnd->packed && compnd->nmembs > 0 && compnd->memb[compnd->nmembs - 1].offset + compnd->memb[compnd->nmembs - 1].size == dt->shared->size); - } + } /* end if */ FUNC_LEAVE_NOAPI(ret_value) } /* end H5T_is_packed() */ diff --git a/test/cmpd_dset.c b/test/cmpd_dset.c index c6ba30b..c05844e 100644 --- a/test/cmpd_dset.c +++ b/test/cmpd_dset.c @@ -23,6 +23,7 @@ #define H5T_PACKAGE #include "H5Tpkg.h" /*to turn off hardware conversions*/ +#include "H5Iprivate.h" #include "h5test.h" @@ -120,6 +121,7 @@ typedef struct { #define NX 100u #define NY 2000u +#define PACK_NMEMBS 100 /*------------------------------------------------------------------------- @@ -600,7 +602,7 @@ test_compound (char *filename, hid_t fapl) if ((s8_m_sid = H5Screate_simple (2, h_size, NULL)) < 0) goto error; /* Read the dataset */ - s8 = calloc ((size_t)(h_size[0]*h_size[1]), sizeof(s1_t)); + s8 = (s1_t *) calloc ((size_t)(h_size[0]*h_size[1]), sizeof(s1_t)); assert (s8); if (H5Dread (dataset, s1_tid, s8_m_sid, s8_f_sid, H5P_DEFAULT, s8) < 0) { goto error; @@ -767,7 +769,7 @@ test_compound (char *filename, hid_t fapl) f_offset[1] = NY/3; h_size[0] = 2*NX/3 - f_offset[0]; h_size[1] = 2*NY/3 - f_offset[1]; - s11 = malloc ((size_t)h_size[0]*(size_t)h_size[1]*sizeof(s4_t)); + s11 = (s4_t *) malloc ((size_t)h_size[0]*(size_t)h_size[1]*sizeof(s4_t)); assert (s11); /* Initialize */ @@ -1717,6 +1719,248 @@ error: return 1; } +/* Error macro that outputs the state of the randomly generated variables so the + * failure can be reproduced */ +#define PACK_OOO_ERROR \ +{ \ + int _i; \ + H5_FAILED(); AT(); \ + printf(" Insertion order ="); \ + for(_i=0; _i<PACK_NMEMBS; _i++) \ + printf(" %d", order[_i]); \ + printf("\n Inner compound order = %d, location = %d\n", sub_cmpd_order, order[sub_cmpd_order]); \ + fflush(stdout); \ + goto error; \ +} + + +/*------------------------------------------------------------------------- + * Function: test_pack_ooo + * + * Purpose: Test inserting fields into a compound out of offset order. + * Verifies that the compound + * + * Return: Success: 0 + * + * Failure: 1 + * + * Programmer: Neil Fortner + * Thursday, 22 January 2009 + * + * Modifications: + *------------------------------------------------------------------------- + */ +static int +test_pack_ooo(void) +{ + hid_t cmpd, sub_cmpd; /* Datatype IDs */ + H5T_t *dt; /* Datatype pointer */ + unsigned order[PACK_NMEMBS]; /* Order of insertion */ + unsigned free_order[PACK_NMEMBS]; /* Index of remaining free slots in order */ + unsigned num_free; /* Number of free slots in order */ + unsigned sub_cmpd_order; /* Order to insert the inner compound */ + char name[6]; /* Member name */ + unsigned i, j; /* Indices */ + + HDsrand((unsigned) time(NULL)); + + /* Initialize "free_order" array to indicate that all slots in order are + * free */ + for(i=0; i<PACK_NMEMBS; i++) + free_order[i] = i; + + /* Create "order" array */ + for(i=0; i<PACK_NMEMBS; i++) { + /* Generate index into free_order array */ + num_free = PACK_NMEMBS - i; + j = HDrand() % num_free; + + /* Update order array at the randomly generated (but guaranteed to be + * free) location */ + order[free_order[j]] = i; + + /* Reshape free_order to remove j (which is no longer free) */ + if(j < (num_free - 1)) + HDmemmove(&free_order[j], &free_order[j+1], (num_free - j - 1) * sizeof(free_order[0])); + } /* end for */ + + /* Generate order to insert inner compound type */ + sub_cmpd_order = HDrand() % PACK_NMEMBS; + + TESTING("random member insertion with empty compound subtype"); + + /* Create inner compound type. It will be empty for the first run */ + if((sub_cmpd = H5Tcreate(H5T_COMPOUND, 4)) < 0) PACK_OOO_ERROR + + /* Create main compound type, with extra space at the end */ + if((cmpd = H5Tcreate(H5T_COMPOUND, (4 * PACK_NMEMBS) + 1)) < 0) PACK_OOO_ERROR + + /* Insert the compound members in the random order previously generated */ + for(i=0; i<PACK_NMEMBS; i++) { + sprintf(name, "%05d", i); + if(i == sub_cmpd_order) { + if(H5Tinsert(cmpd, name, 4 * order[i], sub_cmpd) < 0) PACK_OOO_ERROR + } else + if(H5Tinsert(cmpd, name, 4 * order[i], H5T_STD_I32BE) < 0) PACK_OOO_ERROR + } /* end for */ + + /* Verify that the compound is not packed */ + if(NULL == (dt = (H5T_t *) H5I_object_verify(cmpd, H5I_DATATYPE))) PACK_OOO_ERROR + if(dt->shared->u.compnd.packed) PACK_OOO_ERROR + + /* Close the main compound */ + if(H5Tclose(cmpd) < 0) PACK_OOO_ERROR + + PASSED(); + + TESTING("random member insertion with full compound subtype"); + + /* Complete the inner compound type */ + if(H5Tinsert(sub_cmpd, "int", 0, H5T_STD_I32LE) < 0) PACK_OOO_ERROR + + /* Recreate main compound type */ + if((cmpd = H5Tcreate(H5T_COMPOUND, (4 * PACK_NMEMBS) + 1)) < 0) PACK_OOO_ERROR + + /* Insert the compound members in the random order previously generated */ + for(i=0; i<PACK_NMEMBS; i++) { + sprintf(name, "%05d", i); + if(i == sub_cmpd_order) { + if(H5Tinsert(cmpd, name, 4 * order[i], sub_cmpd) < 0) PACK_OOO_ERROR + } else + if(H5Tinsert(cmpd, name, 4 * order[i], H5T_STD_I32BE) < 0) PACK_OOO_ERROR + } /* end for */ + + /* Verify that the compound is packed */ + if(NULL == (dt = (H5T_t *) H5I_object_verify(cmpd, H5I_DATATYPE))) PACK_OOO_ERROR + if(!dt->shared->u.compnd.packed) PACK_OOO_ERROR + + /* Close */ + if(H5Tclose(cmpd) < 0) PACK_OOO_ERROR + if(H5Tclose(sub_cmpd) < 0) PACK_OOO_ERROR + + PASSED(); + + /* Change to reverse ordering, insert compound last */ + for(i=0; i<PACK_NMEMBS; i++) + order[i] = PACK_NMEMBS - i - 1; + sub_cmpd_order = PACK_NMEMBS - 1; + + TESTING("reverse member insertion with empty compound subtype"); + + /* Create inner compound type. It will be empty for the first run */ + if((sub_cmpd = H5Tcreate(H5T_COMPOUND, 4)) < 0) TEST_ERROR + + /* Create main compound type, with extra space at the end */ + if((cmpd = H5Tcreate(H5T_COMPOUND, (4 * PACK_NMEMBS) + 1)) < 0) TEST_ERROR + + /* Insert the compound members in the reverse order previously generated */ + for(i=0; i<PACK_NMEMBS; i++) { + sprintf(name, "%05d", i); + if(i == sub_cmpd_order) { + if(H5Tinsert(cmpd, name, 4 * order[i], sub_cmpd) < 0) TEST_ERROR + } else + if(H5Tinsert(cmpd, name, 4 * order[i], H5T_STD_I32BE) < 0) TEST_ERROR + } /* end for */ + + /* Verify that the compound is not packed */ + if(NULL == (dt = (H5T_t *) H5I_object_verify(cmpd, H5I_DATATYPE))) TEST_ERROR + if(dt->shared->u.compnd.packed) TEST_ERROR + + /* Close the main compound */ + if(H5Tclose(cmpd) < 0) TEST_ERROR + + PASSED(); + + TESTING("reverse member insertion with full compound subtype"); + + /* Complete the inner compound type */ + if(H5Tinsert(sub_cmpd, "int", 0, H5T_STD_I32LE) < 0) TEST_ERROR + + /* Recreate main compound type */ + if((cmpd = H5Tcreate(H5T_COMPOUND, (4 * PACK_NMEMBS) + 1)) < 0) TEST_ERROR + + /* Insert the compound members in the reverse order previously generated */ + for(i=0; i<PACK_NMEMBS; i++) { + sprintf(name, "%05d", i); + if(i == sub_cmpd_order) { + if(H5Tinsert(cmpd, name, 4 * order[i], sub_cmpd) < 0) TEST_ERROR + } else + if(H5Tinsert(cmpd, name, 4 * order[i], H5T_STD_I32BE) < 0) TEST_ERROR + } /* end for */ + + /* Verify that the compound is packed */ + if(NULL == (dt = (H5T_t *) H5I_object_verify(cmpd, H5I_DATATYPE))) TEST_ERROR + if(!dt->shared->u.compnd.packed) TEST_ERROR + + /* Close */ + if(H5Tclose(cmpd) < 0) TEST_ERROR + if(H5Tclose(sub_cmpd) < 0) TEST_ERROR + + PASSED(); + + /* Change to forward ordering, insert compound first */ + sub_cmpd_order = 0; + + TESTING("forward member insertion with empty compound subtype"); + + /* Create inner compound type. It will be empty for the first run */ + if((sub_cmpd = H5Tcreate(H5T_COMPOUND, 4)) < 0) TEST_ERROR + + /* Create main compound type, with extra space at the end */ + if((cmpd = H5Tcreate(H5T_COMPOUND, (4 * PACK_NMEMBS) + 1)) < 0) TEST_ERROR + + /* Insert the compound members in forward order */ + for(i=0; i<PACK_NMEMBS; i++) { + sprintf(name, "%05d", i); + if(i == sub_cmpd_order) { + if(H5Tinsert(cmpd, name, 4 * i, sub_cmpd) < 0) TEST_ERROR + } else + if(H5Tinsert(cmpd, name, 4 * i, H5T_STD_I32BE) < 0) TEST_ERROR + } /* end for */ + + /* Verify that the compound is not packed */ + if(NULL == (dt = (H5T_t *) H5I_object_verify(cmpd, H5I_DATATYPE))) TEST_ERROR + if(dt->shared->u.compnd.packed) TEST_ERROR + + /* Close the main compound */ + if(H5Tclose(cmpd) < 0) TEST_ERROR + + PASSED(); + + TESTING("forward member insertion with full compound subtype"); + + /* Complete the inner compound type */ + if(H5Tinsert(sub_cmpd, "int", 0, H5T_STD_I32LE) < 0) TEST_ERROR + + /* Recreate main compound type */ + if((cmpd = H5Tcreate(H5T_COMPOUND, (4 * PACK_NMEMBS) + 1)) < 0) TEST_ERROR + + /* Insert the compound members in forward order */ + for(i=0; i<PACK_NMEMBS; i++) { + sprintf(name, "%05d", i); + if(i == sub_cmpd_order) { + if(H5Tinsert(cmpd, name, 4 * i, sub_cmpd) < 0) TEST_ERROR + } else + if(H5Tinsert(cmpd, name, 4 * i, H5T_STD_I32BE) < 0) TEST_ERROR + } /* end for */ + + /* Verify that the compound is packed */ + if(NULL == (dt = (H5T_t *) H5I_object_verify(cmpd, H5I_DATATYPE))) TEST_ERROR + if(!dt->shared->u.compnd.packed) TEST_ERROR + + /* Close */ + if(H5Tclose(cmpd) < 0) TEST_ERROR + if(H5Tclose(sub_cmpd) < 0) TEST_ERROR + + PASSED(); + + return 0; + +error: + puts("*** DATASET TESTS FAILED ***"); + return 1; +} + /*------------------------------------------------------------------------- * Function: main @@ -1768,6 +2012,9 @@ main (int argc, char *argv[]) h5_fixname(FILENAME[2], fapl_id, fname, sizeof(fname)); nerrors += test_hdf5_dst_subset(fname, fapl_id); + puts("Testing that compound types can be packed out of order:"); + nerrors += test_pack_ooo(); + if (nerrors) { printf("***** %u FAILURE%s! *****\n", nerrors, 1==nerrors?"":"S"); |