NetCDF
4.3.2
|
00001 00011 #include "config.h" 00012 #include <errno.h> /* netcdf functions sometimes return system errors */ 00013 00014 #include "nc.h" 00015 #include "nc4internal.h" 00016 #include "nc4dispatch.h" 00017 00018 #include "H5DSpublic.h" 00019 00020 #ifdef USE_HDF4 00021 #include <mfhdf.h> 00022 #endif 00023 00024 #if 0 /*def USE_PNETCDF*/ 00025 #include <pnetcdf.h> 00026 #endif 00027 00028 /* This is to track opened HDF5 objects to make sure they are 00029 * closed. */ 00030 #ifdef EXTRA_TESTS 00031 extern int num_plists; 00032 extern int num_spaces; 00033 #endif /* EXTRA_TESTS */ 00034 00035 #define MIN_DEFLATE_LEVEL 0 00036 #define MAX_DEFLATE_LEVEL 9 00037 00038 /* These are the special attributes added by the HDF5 dimension scale 00039 * API. They will be ignored by netCDF-4. */ 00040 #define REFERENCE_LIST "REFERENCE_LIST" 00041 #define CLASS "CLASS" 00042 #define DIMENSION_LIST "DIMENSION_LIST" 00043 #define NAME "NAME" 00044 00045 /* Struct to track information about objects in a group, for nc4_rec_read_metadata() */ 00046 typedef struct NC4_rec_read_metadata_obj_info 00047 { 00048 hid_t oid; /* HDF5 object ID */ 00049 char oname[NC_MAX_NAME + 1]; /* Name of object */ 00050 H5G_stat_t statbuf; /* Information about the object */ 00051 struct NC4_rec_read_metadata_obj_info *next; /* Pointer to next node in list */ 00052 } NC4_rec_read_metadata_obj_info_t; 00053 00054 /* User data struct for call to H5Literate() in nc4_rec_read_metadata() */ 00055 /* Tracks the groups, named datatypes and datasets in the group, for later use */ 00056 typedef struct NC4_rec_read_metadata_ud 00057 { 00058 NC4_rec_read_metadata_obj_info_t *grps_head, *grps_tail; /* Pointers to head & tail of list of groups */ 00059 NC_GRP_INFO_T *grp; /* Pointer to parent group */ 00060 } NC4_rec_read_metadata_ud_t; 00061 00062 /* Forward */ 00063 static int NC4_enddef(int ncid); 00064 static int nc4_rec_read_metadata(NC_GRP_INFO_T *grp); 00065 static int close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort); 00066 00067 /* These are the default chunk cache sizes for HDF5 files created or 00068 * opened with netCDF-4. */ 00069 size_t nc4_chunk_cache_size = CHUNK_CACHE_SIZE; 00070 size_t nc4_chunk_cache_nelems = CHUNK_CACHE_NELEMS; 00071 float nc4_chunk_cache_preemption = CHUNK_CACHE_PREEMPTION; 00072 00073 /* To turn off HDF5 error messages, I have to catch an early 00074 invocation of a netcdf function. */ 00075 static int virgin = 1; 00076 00077 /* For performance, fill this array only the first time, and keep it 00078 * in global memory for each further use. */ 00079 #define NUM_TYPES 12 00080 static hid_t h5_native_type_constant_g[NUM_TYPES]; 00081 static const char nc_type_name_g[NUM_TYPES][NC_MAX_NAME + 1] = {"char", "byte", "short", 00082 "int", "float", "double", "ubyte", 00083 "ushort", "uint", "int64", 00084 "uint64", "string"}; 00085 static const nc_type nc_type_constant_g[NUM_TYPES] = {NC_CHAR, NC_BYTE, NC_SHORT, 00086 NC_INT, NC_FLOAT, NC_DOUBLE, NC_UBYTE, 00087 NC_USHORT, NC_UINT, NC_INT64, 00088 NC_UINT64, NC_STRING}; 00089 static const int nc_type_size_g[NUM_TYPES] = {sizeof(char), sizeof(char), sizeof(short), 00090 sizeof(int), sizeof(float), sizeof(double), sizeof(unsigned char), 00091 sizeof(unsigned short), sizeof(unsigned int), sizeof(long long), 00092 sizeof(unsigned long long), sizeof(char *)}; 00093 00094 /* Set chunk cache size. Only affects files opened/created *after* it 00095 * is called. */ 00096 int 00097 nc_set_chunk_cache(size_t size, size_t nelems, float preemption) 00098 { 00099 if (preemption < 0 || preemption > 1) 00100 return NC_EINVAL; 00101 nc4_chunk_cache_size = size; 00102 nc4_chunk_cache_nelems = nelems; 00103 nc4_chunk_cache_preemption = preemption; 00104 return NC_NOERR; 00105 } 00106 00107 /* Get chunk cache size. Only affects files opened/created *after* it 00108 * is called. */ 00109 int 00110 nc_get_chunk_cache(size_t *sizep, size_t *nelemsp, float *preemptionp) 00111 { 00112 if (sizep) 00113 *sizep = nc4_chunk_cache_size; 00114 00115 if (nelemsp) 00116 *nelemsp = nc4_chunk_cache_nelems; 00117 00118 if (preemptionp) 00119 *preemptionp = nc4_chunk_cache_preemption; 00120 return NC_NOERR; 00121 } 00122 00123 /* Required for fortran to avoid size_t issues. */ 00124 int 00125 nc_set_chunk_cache_ints(int size, int nelems, int preemption) 00126 { 00127 if (size <= 0 || nelems <= 0 || preemption < 0 || preemption > 100) 00128 return NC_EINVAL; 00129 nc4_chunk_cache_size = size; 00130 nc4_chunk_cache_nelems = nelems; 00131 nc4_chunk_cache_preemption = (float)preemption / 100; 00132 return NC_NOERR; 00133 } 00134 00135 int 00136 nc_get_chunk_cache_ints(int *sizep, int *nelemsp, int *preemptionp) 00137 { 00138 if (sizep) 00139 *sizep = (int)nc4_chunk_cache_size; 00140 if (nelemsp) 00141 *nelemsp = (int)nc4_chunk_cache_nelems; 00142 if (preemptionp) 00143 *preemptionp = (int)(nc4_chunk_cache_preemption * 100); 00144 00145 return NC_NOERR; 00146 } 00147 00148 /* This will return the length of a netcdf data type in bytes. */ 00149 int 00150 nc4typelen(nc_type type) 00151 { 00152 switch(type){ 00153 case NC_BYTE: 00154 case NC_CHAR: 00155 case NC_UBYTE: 00156 return 1; 00157 case NC_USHORT: 00158 case NC_SHORT: 00159 return 2; 00160 case NC_FLOAT: 00161 case NC_INT: 00162 case NC_UINT: 00163 return 4; 00164 case NC_DOUBLE: 00165 case NC_INT64: 00166 case NC_UINT64: 00167 return 8; 00168 } 00169 return -1; 00170 } 00171 00172 /* Given a filename, check to see if it is a HDF5 file. */ 00173 #define MAGIC_NUMBER_LEN 4 00174 #define NC_HDF5_FILE 1 00175 #define NC_HDF4_FILE 2 00176 static int 00177 nc_check_for_hdf(const char *path, int use_parallel, MPI_Comm comm, MPI_Info info, 00178 int *hdf_file) 00179 { 00180 char blob[MAGIC_NUMBER_LEN]; 00181 00182 assert(hdf_file && path); 00183 LOG((3, "%s: path %s", __func__, path)); 00184 00185 /* HDF5 function handles possible user block at beginning of file */ 00186 if(H5Fis_hdf5(path)) 00187 { 00188 *hdf_file = NC_HDF5_FILE; 00189 } else { 00190 00191 /* Get the 4-byte blob from the beginning of the file. Don't use posix 00192 * for parallel, use the MPI functions instead. */ 00193 #ifdef USE_PARALLEL 00194 if (use_parallel) 00195 { 00196 MPI_File fh; 00197 MPI_Status status; 00198 int retval; 00199 if ((retval = MPI_File_open(comm, (char *)path, MPI_MODE_RDONLY, 00200 info, &fh)) != MPI_SUCCESS) 00201 return NC_EPARINIT; 00202 if ((retval = MPI_File_read(fh, blob, MAGIC_NUMBER_LEN, MPI_CHAR, 00203 &status)) != MPI_SUCCESS) 00204 return NC_EPARINIT; 00205 if ((retval = MPI_File_close(&fh)) != MPI_SUCCESS) 00206 return NC_EPARINIT; 00207 } 00208 else 00209 #endif /* USE_PARALLEL */ 00210 { 00211 FILE *fp; 00212 if (!(fp = fopen(path, "r")) || 00213 fread(blob, MAGIC_NUMBER_LEN, 1, fp) != 1) { 00214 00215 if(fp) fclose(fp); 00216 return errno; 00217 } 00218 fclose(fp); 00219 } 00220 00221 /* Check for HDF4. */ 00222 if (!strncmp(blob, "\016\003\023\001", MAGIC_NUMBER_LEN)) 00223 *hdf_file = NC_HDF4_FILE; 00224 else 00225 *hdf_file = 0; 00226 } 00227 return NC_NOERR; 00228 } 00229 00230 /* Create a HDF5/netcdf-4 file. */ 00231 00232 static int 00233 nc4_create_file(const char *path, int cmode, MPI_Comm comm, MPI_Info info, 00234 NC *nc) 00235 { 00236 hid_t fcpl_id, fapl_id = -1; 00237 unsigned flags; 00238 FILE *fp; 00239 int retval = NC_NOERR; 00240 NC_HDF5_FILE_INFO_T* nc4_info = NULL; 00241 #ifdef USE_PARALLEL 00242 int comm_duped = 0; /* Whether the MPI Communicator was duplicated */ 00243 int info_duped = 0; /* Whether the MPI Info object was duplicated */ 00244 #else /* !USE_PARALLEL */ 00245 int persist = 0; /* Should diskless try to persist its data into file?*/ 00246 #endif 00247 00248 assert(nc); 00249 00250 if(cmode & NC_DISKLESS) 00251 flags = H5F_ACC_TRUNC; 00252 else if(cmode & NC_NOCLOBBER) 00253 flags = H5F_ACC_EXCL; 00254 else 00255 flags = H5F_ACC_TRUNC; 00256 00257 LOG((3, "%s: path %s mode 0x%x", __func__, path, cmode)); 00258 assert(nc && path); 00259 00260 /* If this file already exists, and NC_NOCLOBBER is specified, 00261 return an error. */ 00262 if (cmode & NC_DISKLESS) { 00263 #ifndef USE_PARALLEL 00264 if(cmode & NC_WRITE) 00265 persist = 1; 00266 #endif 00267 } else if ((cmode & NC_NOCLOBBER) && (fp = fopen(path, "r"))) { 00268 fclose(fp); 00269 return NC_EEXIST; 00270 } 00271 00272 /* Add necessary structs to hold netcdf-4 file data. */ 00273 if ((retval = nc4_nc4f_list_add(nc, path, (NC_WRITE | cmode)))) 00274 BAIL(retval); 00275 nc4_info = NC4_DATA(nc); 00276 assert(nc4_info && nc4_info->root_grp); 00277 00278 #if 0 /*def USE_PNETCDF*/ 00279 if (cmode & NC_PNETCDF) 00280 return NC_NOERR; 00281 #endif 00282 00283 /* Need this access plist to control how HDF5 handles open onjects 00284 * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to 00285 * fail if there are any open objects in the file. */ 00286 if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) 00287 BAIL(NC_EHDFERR); 00288 #ifdef EXTRA_TESTS 00289 num_plists++; 00290 #endif 00291 #ifdef EXTRA_TESTS 00292 if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI)) 00293 BAIL(NC_EHDFERR); 00294 #else 00295 if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_STRONG)) 00296 BAIL(NC_EHDFERR); 00297 #endif /* EXTRA_TESTS */ 00298 00299 #ifdef USE_PARALLEL 00300 /* If this is a parallel file create, set up the file creation 00301 property list. */ 00302 if ((cmode & NC_MPIIO) || (cmode & NC_MPIPOSIX)) 00303 { 00304 nc4_info->parallel = NC_TRUE; 00305 if (cmode & NC_MPIIO) /* MPI/IO */ 00306 { 00307 LOG((4, "creating parallel file with MPI/IO")); 00308 if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0) 00309 BAIL(NC_EPARINIT); 00310 } 00311 else /* MPI/POSIX */ 00312 { 00313 LOG((4, "creating parallel file with MPI/posix")); 00314 if (H5Pset_fapl_mpiposix(fapl_id, comm, 0) < 0) 00315 BAIL(NC_EPARINIT); 00316 } 00317 00318 /* Keep copies of the MPI Comm & Info objects */ 00319 if (MPI_SUCCESS != MPI_Comm_dup(comm, &nc4_info->comm)) 00320 BAIL(NC_EMPI); 00321 comm_duped++; 00322 if (MPI_INFO_NULL != info) 00323 { 00324 if (MPI_SUCCESS != MPI_Info_dup(info, &nc4_info->info)) 00325 BAIL(NC_EMPI); 00326 info_duped++; 00327 } 00328 else 00329 { 00330 /* No dup, just copy it. */ 00331 nc4_info->info = info; 00332 } 00333 } 00334 #else /* only set cache for non-parallel... */ 00335 if(cmode & NC_DISKLESS) { 00336 if (H5Pset_fapl_core(fapl_id, 4096, persist)) 00337 BAIL(NC_EDISKLESS); 00338 } 00339 if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size, 00340 nc4_chunk_cache_preemption) < 0) 00341 BAIL(NC_EHDFERR); 00342 LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f", 00343 __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems, nc4_chunk_cache_preemption)); 00344 #endif /* USE_PARALLEL */ 00345 00346 if (H5Pset_libver_bounds(fapl_id, H5F_LIBVER_LATEST, H5F_LIBVER_LATEST) < 0) 00347 BAIL(NC_EHDFERR); 00348 00349 /* Create the property list. */ 00350 if ((fcpl_id = H5Pcreate(H5P_FILE_CREATE)) < 0) 00351 BAIL(NC_EHDFERR); 00352 #ifdef EXTRA_TESTS 00353 num_plists++; 00354 #endif 00355 00356 /* RJ: this suppose to be FALSE that is defined in H5 private.h as 0 */ 00357 if (H5Pset_obj_track_times(fcpl_id,0)<0) 00358 BAIL(NC_EHDFERR); 00359 00360 /* Set latest_format in access propertly list and 00361 * H5P_CRT_ORDER_TRACKED in the creation property list. This turns 00362 * on HDF5 creation ordering. */ 00363 if (H5Pset_link_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED | 00364 H5P_CRT_ORDER_INDEXED)) < 0) 00365 BAIL(NC_EHDFERR); 00366 if (H5Pset_attr_creation_order(fcpl_id, (H5P_CRT_ORDER_TRACKED | 00367 H5P_CRT_ORDER_INDEXED)) < 0) 00368 BAIL(NC_EHDFERR); 00369 00370 /* Create the file. */ 00371 if ((nc4_info->hdfid = H5Fcreate(path, flags, fcpl_id, fapl_id)) < 0) 00372 /*Change the return error from NC_EFILEMETADATA to 00373 System error EACCES because that is the more likely problem */ 00374 BAIL(EACCES); 00375 00376 /* Open the root group. */ 00377 if ((nc4_info->root_grp->hdf_grpid = H5Gopen2(nc4_info->hdfid, "/", 00378 H5P_DEFAULT)) < 0) 00379 BAIL(NC_EFILEMETA); 00380 00381 /* Release the property lists. */ 00382 if (H5Pclose(fapl_id) < 0 || 00383 H5Pclose(fcpl_id) < 0) 00384 BAIL(NC_EHDFERR); 00385 #ifdef EXTRA_TESTS 00386 num_plists--; 00387 num_plists--; 00388 #endif 00389 00390 /* Define mode gets turned on automatically on create. */ 00391 nc4_info->flags |= NC_INDEF; 00392 00393 return NC_NOERR; 00394 00395 exit: /*failure exit*/ 00396 #ifdef USE_PARALLEL 00397 if (comm_duped) MPI_Comm_free(&nc4_info->comm); 00398 if (info_duped) MPI_Info_free(&nc4_info->info); 00399 #endif 00400 #ifdef EXTRA_TESTS 00401 num_plists--; 00402 #endif 00403 if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id); 00404 if(!nc4_info) return retval; 00405 close_netcdf4_file(nc4_info,1); /* treat like abort */ 00406 return retval; 00407 } 00408 00424 int 00425 NC4_create(const char* path, int cmode, size_t initialsz, int basepe, 00426 size_t *chunksizehintp, int use_parallel, void *mpidata, 00427 NC_Dispatch *dispatch, NC* nc_file) 00428 { 00429 MPI_Comm comm = MPI_COMM_WORLD; 00430 MPI_Info info = MPI_INFO_NULL; 00431 int res; 00432 00433 assert(nc_file && path); 00434 00435 LOG((1, "%s: path %s cmode 0x%x comm %d info %d", 00436 __func__, path, cmode, comm, info)); 00437 00438 #ifdef USE_PARALLEL 00439 if (mpidata) 00440 { 00441 comm = ((NC_MPI_INFO *)mpidata)->comm; 00442 info = ((NC_MPI_INFO *)mpidata)->info; 00443 } 00444 #endif /* USE_PARALLEL */ 00445 00446 /* If this is our first file, turn off HDF5 error messages. */ 00447 if (virgin) 00448 { 00449 if (H5Eset_auto(NULL, NULL) < 0) 00450 LOG((0, "Couldn't turn off HDF5 error messages!")); 00451 LOG((1, "HDF5 error messages have been turned off.")); 00452 virgin = 0; 00453 } 00454 00455 /* Check the cmode for validity. */ 00456 if (cmode & ~(NC_NOCLOBBER | NC_64BIT_OFFSET 00457 | NC_NETCDF4 | NC_CLASSIC_MODEL 00458 | NC_SHARE | NC_MPIIO | NC_MPIPOSIX | NC_LOCK | NC_PNETCDF 00459 | NC_DISKLESS 00460 | NC_WRITE /* to support diskless persistence */ 00461 ) 00462 || (cmode & NC_MPIIO && cmode & NC_MPIPOSIX) 00463 || (cmode & NC_64BIT_OFFSET && cmode & NC_NETCDF4) 00464 || (cmode & (NC_MPIIO | NC_MPIPOSIX) && cmode & NC_DISKLESS) 00465 ) 00466 return NC_EINVAL; 00467 00468 cmode |= NC_NETCDF4; 00469 00470 /* Apply default create format. */ 00471 if (nc_get_default_format() == NC_FORMAT_64BIT) 00472 cmode |= NC_64BIT_OFFSET; 00473 else if (nc_get_default_format() == NC_FORMAT_NETCDF4_CLASSIC) 00474 cmode |= NC_CLASSIC_MODEL; 00475 00476 LOG((2, "cmode after applying default format: 0x%x", cmode)); 00477 00478 nc_file->int_ncid = nc_file->ext_ncid; 00479 res = nc4_create_file(path, cmode, comm, info, nc_file); 00480 00481 #if 0 /*def USE_PNETCDF*/ 00482 if (cmode & NC_PNETCDF) 00483 { 00484 NC_HDF5_FILE_INFO_T* nc4_info; 00485 nc4_info = NC4_DATA(nc_file); 00486 assert(nc4_info); 00487 00488 nc4_info->pnetcdf_file++; 00489 res = ncmpi_create(comm, path, cmode, info, &(nc_file->int_ncid)); 00490 } 00491 #endif /* USE_PNETCDF */ 00492 00493 return res; 00494 } 00495 00496 /* This function is called by read_dataset when a dimension scale 00497 * dataset is encountered. It reads in the dimension data (creating a 00498 * new NC_DIM_INFO_T object), and also checks to see if this is a 00499 * dimension without a variable - that is, a coordinate dimension 00500 * which does not have any coordinate data. */ 00501 static int 00502 read_scale(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name, 00503 const H5G_stat_t *statbuf, hsize_t scale_size, hsize_t max_scale_size, 00504 NC_DIM_INFO_T **dim) 00505 { 00506 NC_DIM_INFO_T *new_dim; /* Dimension added to group */ 00507 char dimscale_name_att[NC_MAX_NAME + 1] = ""; /* Dimscale name, for checking if dim without var */ 00508 htri_t attr_exists = -1; /* Flag indicating hidden attribute exists */ 00509 hid_t attid = -1; /* ID of hidden attribute (to store dim ID) */ 00510 int dimscale_created = 0; /* Remember if a dimension was created (for error recovery) */ 00511 int initial_grp_ndims = grp->ndims; /* Retain for error recovery */ 00512 short initial_next_dimid = grp->nc4_info->next_dimid;/* Retain for error recovery */ 00513 int retval; 00514 00515 /* Add a dimension for this scale. */ 00516 if ((retval = nc4_dim_list_add(&grp->dim, &new_dim))) 00517 BAIL(retval); 00518 dimscale_created++; 00519 00520 /* Does this dataset have a hidden attribute that tells us its 00521 * dimid? If so, read it. */ 00522 if ((attr_exists = H5Aexists(datasetid, NC_DIMID_ATT_NAME)) < 0) 00523 BAIL(NC_EHDFERR); 00524 if (attr_exists) 00525 { 00526 if ((attid = H5Aopen_by_name(datasetid, ".", NC_DIMID_ATT_NAME, 00527 H5P_DEFAULT, H5P_DEFAULT)) < 0) 00528 BAIL(NC_EHDFERR); 00529 00530 if (H5Aread(attid, H5T_NATIVE_INT, &new_dim->dimid) < 0) 00531 BAIL(NC_EHDFERR); 00532 00533 /* Check if scale's dimid should impact the group's next dimid */ 00534 if (new_dim->dimid >= grp->nc4_info->next_dimid) 00535 grp->nc4_info->next_dimid = new_dim->dimid + 1; 00536 } 00537 else 00538 { 00539 /* Assign dimid */ 00540 new_dim->dimid = grp->nc4_info->next_dimid++; 00541 } 00542 00543 /* Increment number of dimensions. */ 00544 grp->ndims++; 00545 00546 if (!(new_dim->name = strdup(obj_name))) 00547 BAIL(NC_ENOMEM); 00548 if (SIZEOF_SIZE_T < 8 && scale_size > NC_MAX_UINT) 00549 { 00550 new_dim->len = NC_MAX_UINT; 00551 new_dim->too_long = NC_TRUE; 00552 } 00553 else 00554 new_dim->len = scale_size; 00555 new_dim->hdf5_objid.fileno[0] = statbuf->fileno[0]; 00556 new_dim->hdf5_objid.fileno[1] = statbuf->fileno[1]; 00557 new_dim->hdf5_objid.objno[0] = statbuf->objno[0]; 00558 new_dim->hdf5_objid.objno[1] = statbuf->objno[1]; 00559 00560 /* If the dimscale has an unlimited dimension, then this dimension 00561 * is unlimited. */ 00562 if (max_scale_size == H5S_UNLIMITED) 00563 new_dim->unlimited = NC_TRUE; 00564 00565 /* If the scale name is set to DIM_WITHOUT_VARIABLE, then this is a 00566 * dimension, but not a variable. (If get_scale_name returns an 00567 * error, just move on, there's no NAME.) */ 00568 if (H5DSget_scale_name(datasetid, dimscale_name_att, NC_MAX_NAME) >= 0) 00569 { 00570 if (!strncmp(dimscale_name_att, DIM_WITHOUT_VARIABLE, 00571 strlen(DIM_WITHOUT_VARIABLE))) 00572 { 00573 if (new_dim->unlimited) 00574 { 00575 size_t len = 0, *lenp = &len; 00576 00577 if ((retval = nc4_find_dim_len(grp, new_dim->dimid, &lenp))) 00578 BAIL(retval); 00579 new_dim->len = *lenp; 00580 } 00581 00582 /* Hold open the dataset, since the dimension doesn't have a coordinate variable */ 00583 new_dim->hdf_dimscaleid = datasetid; 00584 H5Iinc_ref(new_dim->hdf_dimscaleid); /* Increment number of objects using ID */ 00585 } 00586 } 00587 00588 /* Set the dimension created */ 00589 *dim = new_dim; 00590 00591 exit: 00592 /* Close the hidden attribute, if it was opened (error, or no error) */ 00593 if (attid > 0 && H5Aclose(attid) < 0) 00594 BAIL2(NC_EHDFERR); 00595 00596 /* On error, undo any dimscale creation */ 00597 if (retval < 0 && dimscale_created) 00598 { 00599 /* Delete the dimension */ 00600 if ((retval = nc4_dim_list_del(&grp->dim, new_dim))) 00601 BAIL2(retval); 00602 00603 /* Reset the group's information */ 00604 grp->ndims = initial_grp_ndims; 00605 grp->nc4_info->next_dimid = initial_next_dimid; 00606 } 00607 00608 return retval; 00609 } 00610 00611 /* This function reads the hacked in coordinates attribute I use for 00612 * multi-dimensional coordinates. */ 00613 static int 00614 read_coord_dimids(NC_VAR_INFO_T *var) 00615 { 00616 hid_t coord_att_typeid = -1, coord_attid = -1, spaceid = -1; 00617 hssize_t npoints; 00618 int ret = 0; 00619 00620 /* There is a hidden attribute telling us the ids of the 00621 * dimensions that apply to this multi-dimensional coordinate 00622 * variable. Read it. */ 00623 if ((coord_attid = H5Aopen_name(var->hdf_datasetid, COORDINATES)) < 0) ret++; 00624 if (!ret && (coord_att_typeid = H5Aget_type(coord_attid)) < 0) ret++; 00625 00626 /* How many dimensions are there? */ 00627 if (!ret && (spaceid = H5Aget_space(coord_attid)) < 0) ret++; 00628 #ifdef EXTRA_TESTS 00629 num_spaces++; 00630 #endif 00631 if (!ret && (npoints = H5Sget_simple_extent_npoints(spaceid)) < 0) ret++; 00632 00633 /* Check that the number of points is the same as the number of dimensions 00634 * for the variable */ 00635 if (!ret && npoints != var->ndims) ret++; 00636 00637 if (!ret && H5Aread(coord_attid, coord_att_typeid, var->dimids) < 0) ret++; 00638 LOG((4, "dimscale %s is multidimensional and has coords", var->name)); 00639 00640 /* Set my HDF5 IDs free! */ 00641 if (spaceid >= 0 && H5Sclose(spaceid) < 0) ret++; 00642 #ifdef EXTRA_TESTS 00643 num_spaces--; 00644 #endif 00645 if (coord_att_typeid >= 0 && H5Tclose(coord_att_typeid) < 0) ret++; 00646 if (coord_attid >= 0 && H5Aclose(coord_attid) < 0) ret++; 00647 return ret ? NC_EATTMETA : NC_NOERR; 00648 } 00649 00650 /* This function is called when reading a file's metadata for each 00651 * dimension scale attached to a variable.*/ 00652 static herr_t 00653 dimscale_visitor(hid_t did, unsigned dim, hid_t dsid, 00654 void *dimscale_hdf5_objids) 00655 { 00656 H5G_stat_t statbuf; 00657 00658 /* Get more info on the dimscale object.*/ 00659 if (H5Gget_objinfo(dsid, ".", 1, &statbuf) < 0) 00660 return -1; 00661 00662 /* Pass this information back to caller. */ 00663 (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[0] = statbuf.fileno[0]; 00664 (*(HDF5_OBJID_T *)dimscale_hdf5_objids).fileno[1] = statbuf.fileno[1]; 00665 (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[0] = statbuf.objno[0]; 00666 (*(HDF5_OBJID_T *)dimscale_hdf5_objids).objno[1] = statbuf.objno[1]; 00667 return 0; 00668 } 00669 00670 /* Given an HDF5 type, set a pointer to netcdf type. */ 00671 static int 00672 get_netcdf_type(NC_HDF5_FILE_INFO_T *h5, hid_t native_typeid, 00673 nc_type *xtype) 00674 { 00675 NC_TYPE_INFO_T *type; 00676 H5T_class_t class; 00677 htri_t is_str, equal = 0; 00678 00679 assert(h5 && xtype); 00680 00681 if ((class = H5Tget_class(native_typeid)) < 0) 00682 return NC_EHDFERR; 00683 00684 /* H5Tequal doesn't work with H5T_C_S1 for some reason. But 00685 * H5Tget_class will return H5T_STRING if this is a string. */ 00686 if (class == H5T_STRING) 00687 { 00688 if ((is_str = H5Tis_variable_str(native_typeid)) < 0) 00689 return NC_EHDFERR; 00690 if (is_str) 00691 *xtype = NC_STRING; 00692 else 00693 *xtype = NC_CHAR; 00694 return NC_NOERR; 00695 } 00696 else if (class == H5T_INTEGER || class == H5T_FLOAT) 00697 { 00698 /* For integers and floats, we don't have to worry about 00699 * endianness if we compare native types. */ 00700 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SCHAR)) < 0) 00701 return NC_EHDFERR; 00702 if (equal) 00703 { 00704 *xtype = NC_BYTE; 00705 return NC_NOERR; 00706 } 00707 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_SHORT)) < 0) 00708 return NC_EHDFERR; 00709 if (equal) 00710 { 00711 *xtype = NC_SHORT; 00712 return NC_NOERR; 00713 } 00714 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_INT)) < 0) 00715 return NC_EHDFERR; 00716 if (equal) 00717 { 00718 *xtype = NC_INT; 00719 return NC_NOERR; 00720 } 00721 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_FLOAT)) < 0) 00722 return NC_EHDFERR; 00723 if (equal) 00724 { 00725 *xtype = NC_FLOAT; 00726 return NC_NOERR; 00727 } 00728 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_DOUBLE)) < 0) 00729 return NC_EHDFERR; 00730 if (equal) 00731 { 00732 *xtype = NC_DOUBLE; 00733 return NC_NOERR; 00734 } 00735 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UCHAR)) < 0) 00736 return NC_EHDFERR; 00737 if (equal) 00738 { 00739 *xtype = NC_UBYTE; 00740 return NC_NOERR; 00741 } 00742 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_USHORT)) < 0) 00743 return NC_EHDFERR; 00744 if (equal) 00745 { 00746 *xtype = NC_USHORT; 00747 return NC_NOERR; 00748 } 00749 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_UINT)) < 0) 00750 return NC_EHDFERR; 00751 if (equal) 00752 { 00753 *xtype = NC_UINT; 00754 return NC_NOERR; 00755 } 00756 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_LLONG)) < 0) 00757 return NC_EHDFERR; 00758 if (equal) 00759 { 00760 *xtype = NC_INT64; 00761 return NC_NOERR; 00762 } 00763 if ((equal = H5Tequal(native_typeid, H5T_NATIVE_ULLONG)) < 0) 00764 return NC_EHDFERR; 00765 if (equal) 00766 { 00767 *xtype = NC_UINT64; 00768 return NC_NOERR; 00769 } 00770 } 00771 00772 /* Maybe we already know about this type. */ 00773 if (!equal) 00774 if((type = nc4_rec_find_hdf_type(h5->root_grp, native_typeid))) 00775 { 00776 *xtype = type->nc_typeid; 00777 return NC_NOERR; 00778 } 00779 00780 *xtype = NC_NAT; 00781 return NC_EBADTYPID; 00782 } 00783 00784 /* Given an HDF5 type, set a pointer to netcdf type_info struct, 00785 * either an existing one (for user-defined types) or a newly created 00786 * one. */ 00787 static int 00788 get_type_info2(NC_HDF5_FILE_INFO_T *h5, hid_t datasetid, 00789 NC_TYPE_INFO_T **type_info) 00790 { 00791 htri_t is_str, equal = 0; 00792 H5T_class_t class; 00793 hid_t native_typeid, hdf_typeid; 00794 H5T_order_t order; 00795 int t; 00796 00797 assert(h5 && type_info); 00798 00799 /* Because these N5T_NATIVE_* constants are actually function calls 00800 * (!) in H5Tpublic.h, I can't initialize this array in the usual 00801 * way, because at least some C compilers (like Irix) complain 00802 * about calling functions when defining constants. So I have to do 00803 * it like this. Note that there's no native types for char or 00804 * string. Those are handled later. */ 00805 if (!h5_native_type_constant_g[1]) 00806 { 00807 h5_native_type_constant_g[1] = H5T_NATIVE_SCHAR; 00808 h5_native_type_constant_g[2] = H5T_NATIVE_SHORT; 00809 h5_native_type_constant_g[3] = H5T_NATIVE_INT; 00810 h5_native_type_constant_g[4] = H5T_NATIVE_FLOAT; 00811 h5_native_type_constant_g[5] = H5T_NATIVE_DOUBLE; 00812 h5_native_type_constant_g[6] = H5T_NATIVE_UCHAR; 00813 h5_native_type_constant_g[7] = H5T_NATIVE_USHORT; 00814 h5_native_type_constant_g[8] = H5T_NATIVE_UINT; 00815 h5_native_type_constant_g[9] = H5T_NATIVE_LLONG; 00816 h5_native_type_constant_g[10] = H5T_NATIVE_ULLONG; 00817 } 00818 00819 /* Get the HDF5 typeid - we'll need it later. */ 00820 if ((hdf_typeid = H5Dget_type(datasetid)) < 0) 00821 return NC_EHDFERR; 00822 00823 /* Get the native typeid. Will be equivalent to hdf_typeid when 00824 * creating but not necessarily when reading, a variable. */ 00825 if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0) 00826 return NC_EHDFERR; 00827 00828 /* Is this type an integer, string, compound, or what? */ 00829 if ((class = H5Tget_class(native_typeid)) < 0) 00830 return NC_EHDFERR; 00831 00832 /* Is this an atomic type? */ 00833 if (class == H5T_STRING || class == H5T_INTEGER || class == H5T_FLOAT) 00834 { 00835 /* Allocate a phony NC_TYPE_INFO_T struct to hold type info. */ 00836 if (!(*type_info = calloc(1, sizeof(NC_TYPE_INFO_T)))) 00837 return NC_ENOMEM; 00838 00839 /* H5Tequal doesn't work with H5T_C_S1 for some reason. But 00840 * H5Tget_class will return H5T_STRING if this is a string. */ 00841 if (class == H5T_STRING) 00842 { 00843 if ((is_str = H5Tis_variable_str(native_typeid)) < 0) 00844 return NC_EHDFERR; 00845 /* Make sure fixed-len strings will work like variable-len strings */ 00846 if (is_str || H5Tget_size(hdf_typeid) > 1) 00847 { 00848 /* Set a class for the type */ 00849 t = NUM_TYPES - 1; 00850 (*type_info)->nc_type_class = NC_STRING; 00851 } 00852 else 00853 { 00854 /* Set a class for the type */ 00855 t = 0; 00856 (*type_info)->nc_type_class = NC_CHAR; 00857 } 00858 } 00859 else if (class == H5T_INTEGER || class == H5T_FLOAT) 00860 { 00861 for (t = 1; t < NUM_TYPES - 1; t++) 00862 { 00863 if ((equal = H5Tequal(native_typeid, h5_native_type_constant_g[t])) < 0) 00864 return NC_EHDFERR; 00865 if (equal) 00866 break; 00867 } 00868 00869 /* Find out about endianness. */ 00870 if (class == H5T_INTEGER) 00871 { 00872 if ((order = H5Tget_order(hdf_typeid)) < 0) 00873 return NC_EHDFERR; 00874 00875 /* Copy this into the type_info struct. */ 00876 if (order == H5T_ORDER_LE) 00877 (*type_info)->endianness = NC_ENDIAN_LITTLE; 00878 else if (order == H5T_ORDER_BE) 00879 (*type_info)->endianness = NC_ENDIAN_BIG; 00880 else /* don't support H5T_ORDER_VAX, H5T_ORDER_MIXED, H5T_ORDER_NONE */ 00881 return NC_EBADTYPE; 00882 00883 /* Set a class for the type */ 00884 /* (Note use of 'NC_INT' for all integer types) */ 00885 (*type_info)->nc_type_class = NC_INT; 00886 } 00887 else 00888 { 00889 /* Set a class for the type */ 00890 /* (Note use of 'NC_FLOAT' for all floating-point types) */ 00891 (*type_info)->nc_type_class = NC_FLOAT; 00892 } 00893 } 00894 (*type_info)->nc_typeid = nc_type_constant_g[t]; 00895 (*type_info)->size = nc_type_size_g[t]; 00896 if (!((*type_info)->name = strdup(nc_type_name_g[t]))) 00897 return NC_ENOMEM; 00898 (*type_info)->hdf_typeid = hdf_typeid; 00899 (*type_info)->native_hdf_typeid = native_typeid; 00900 return NC_NOERR; 00901 } 00902 else 00903 { 00904 NC_TYPE_INFO_T *type; 00905 00906 /* This is a user-defined type. */ 00907 if((type = nc4_rec_find_hdf_type(h5->root_grp, native_typeid))) 00908 *type_info = type; 00909 00910 /* The type entry in the array of user-defined types already has 00911 * an open data typeid (and native typeid), so close the ones we 00912 * opened above. */ 00913 if (H5Tclose(native_typeid) < 0) 00914 return NC_EHDFERR; 00915 if (H5Tclose(hdf_typeid) < 0) 00916 return NC_EHDFERR; 00917 00918 if (type) 00919 return NC_NOERR; 00920 } 00921 00922 return NC_EBADTYPID; 00923 } 00924 00925 /* Read an attribute. */ 00926 static int 00927 read_hdf5_att(NC_GRP_INFO_T *grp, hid_t attid, NC_ATT_INFO_T *att) 00928 { 00929 hid_t spaceid = 0, file_typeid = 0; 00930 hsize_t dims[1] = {0}; /* netcdf attributes always 1-D. */ 00931 int retval = NC_NOERR; 00932 size_t type_size; 00933 int att_ndims; 00934 hssize_t att_npoints; 00935 H5T_class_t att_class; 00936 int fixed_len_string = 0; 00937 size_t fixed_size = 0; 00938 00939 assert(att->name); 00940 LOG((5, "%s: att->attnum %d att->name %s att->nc_typeid %d att->len %d", 00941 __func__, att->attnum, att->name, (int)att->nc_typeid, att->len)); 00942 00943 /* Get type of attribute in file. */ 00944 if ((file_typeid = H5Aget_type(attid)) < 0) 00945 return NC_EATTMETA; 00946 if ((att->native_hdf_typeid = H5Tget_native_type(file_typeid, H5T_DIR_DEFAULT)) < 0) 00947 BAIL(NC_EHDFERR); 00948 if ((att_class = H5Tget_class(att->native_hdf_typeid)) < 0) 00949 BAIL(NC_EATTMETA); 00950 if (att_class == H5T_STRING && !H5Tis_variable_str(att->native_hdf_typeid)) 00951 { 00952 fixed_len_string++; 00953 if (!(fixed_size = H5Tget_size(att->native_hdf_typeid))) 00954 BAIL(NC_EATTMETA); 00955 } 00956 if ((retval = get_netcdf_type(grp->nc4_info, att->native_hdf_typeid, &(att->nc_typeid)))) 00957 BAIL(retval); 00958 00959 00960 /* Get len. */ 00961 if ((spaceid = H5Aget_space(attid)) < 0) 00962 BAIL(NC_EATTMETA); 00963 #ifdef EXTRA_TESTS 00964 num_spaces++; 00965 #endif 00966 if ((att_ndims = H5Sget_simple_extent_ndims(spaceid)) < 0) 00967 BAIL(NC_EATTMETA); 00968 if ((att_npoints = H5Sget_simple_extent_npoints(spaceid)) < 0) 00969 BAIL(NC_EATTMETA); 00970 00971 /* If both att_ndims and att_npoints are zero, then this is a 00972 * zero length att. */ 00973 if (att_ndims == 0 && att_npoints == 0) 00974 dims[0] = 0; 00975 else if (att->nc_typeid == NC_STRING) 00976 dims[0] = att_npoints; 00977 else if (att->nc_typeid == NC_CHAR) 00978 { 00979 /* NC_CHAR attributes are written as a scalar in HDF5, of type 00980 * H5T_C_S1, of variable length. */ 00981 if (att_ndims == 0) 00982 { 00983 if (!(dims[0] = H5Tget_size(file_typeid))) 00984 BAIL(NC_EATTMETA); 00985 } 00986 else 00987 { 00988 /* This is really a string type! */ 00989 att->nc_typeid = NC_STRING; 00990 dims[0] = att_npoints; 00991 } 00992 } 00993 else 00994 { 00995 H5S_class_t space_class; 00996 00997 /* All netcdf attributes are scalar or 1-D only. */ 00998 if (att_ndims > 1) 00999 BAIL(NC_EATTMETA); 01000 01001 /* Check class of HDF5 dataspace */ 01002 if ((space_class = H5Sget_simple_extent_type(spaceid)) < 0) 01003 BAIL(NC_EATTMETA); 01004 01005 /* Check for NULL HDF5 dataspace class (should be weeded out earlier) */ 01006 if (H5S_NULL == space_class) 01007 BAIL(NC_EATTMETA); 01008 01009 /* check for SCALAR HDF5 dataspace class */ 01010 if (H5S_SCALAR == space_class) 01011 dims[0] = 1; 01012 else /* Must be "simple" dataspace */ 01013 { 01014 /* Read the size of this attribute. */ 01015 if (H5Sget_simple_extent_dims(spaceid, dims, NULL) < 0) 01016 BAIL(NC_EATTMETA); 01017 } 01018 } 01019 01020 /* Tell the user what the length if this attribute is. */ 01021 att->len = dims[0]; 01022 01023 /* Allocate some memory if the len is not zero, and read the 01024 attribute. */ 01025 if (dims[0]) 01026 { 01027 if ((retval = nc4_get_typelen_mem(grp->nc4_info, att->nc_typeid, 0, 01028 &type_size))) 01029 return retval; 01030 if (att_class == H5T_VLEN) 01031 { 01032 if (!(att->vldata = malloc((unsigned int)(att->len * sizeof(hvl_t))))) 01033 BAIL(NC_ENOMEM); 01034 if (H5Aread(attid, att->native_hdf_typeid, att->vldata) < 0) 01035 BAIL(NC_EATTMETA); 01036 } 01037 else if (att->nc_typeid == NC_STRING) 01038 { 01039 if (!(att->stdata = calloc(att->len, sizeof(char *)))) 01040 BAIL(NC_ENOMEM); 01041 /* For a fixed length HDF5 string, the read requires 01042 * contiguous memory. Meanwhile, the netCDF API requires that 01043 * nc_free_string be called on string arrays, which would not 01044 * work if one contiguous memory block were used. So here I 01045 * convert the contiguous block of strings into an array of 01046 * malloced strings (each string with its own malloc). Then I 01047 * copy the data and free the contiguous memory. This 01048 * involves copying the data, which is bad, but this only 01049 * occurs for fixed length string attributes, and presumably 01050 * these are small. (And netCDF-4 does not create them - it 01051 * always uses variable length strings. */ 01052 if (fixed_len_string) 01053 { 01054 int i; 01055 char *contig_buf, *cur; 01056 01057 /* Alloc space for the contiguous memory read. */ 01058 if (!(contig_buf = malloc(att->len * fixed_size * sizeof(char)))) 01059 BAIL(NC_ENOMEM); 01060 01061 /* Read the fixed-len strings as one big block. */ 01062 if (H5Aread(attid, att->native_hdf_typeid, contig_buf) < 0) 01063 BAIL(NC_EATTMETA); 01064 01065 /* Copy strings, one at a time, into their new home. Alloc 01066 space for each string. The user will later free this 01067 space with nc_free_string. */ 01068 cur = contig_buf; 01069 for (i = 0; i < att->len; i++) 01070 { 01071 if (!(att->stdata[i] = malloc(fixed_size))) 01072 BAIL(NC_ENOMEM); 01073 strncpy(att->stdata[i], cur, fixed_size); 01074 cur += fixed_size; 01075 } 01076 01077 /* Free contiguous memory buffer. */ 01078 free(contig_buf); 01079 } 01080 else 01081 { 01082 /* Read variable-length string atts. */ 01083 if (H5Aread(attid, att->native_hdf_typeid, att->stdata) < 0) 01084 BAIL(NC_EATTMETA); 01085 } 01086 } 01087 else 01088 { 01089 if (!(att->data = malloc((unsigned int)(att->len * type_size)))) 01090 BAIL(NC_ENOMEM); 01091 if (H5Aread(attid, att->native_hdf_typeid, att->data) < 0) 01092 BAIL(NC_EATTMETA); 01093 } 01094 } 01095 01096 if (H5Tclose(file_typeid) < 0) 01097 BAIL(NC_EHDFERR); 01098 if (H5Sclose(spaceid) < 0) 01099 return NC_EHDFERR; 01100 #ifdef EXTRA_TESTS 01101 num_spaces--; 01102 #endif 01103 01104 return NC_NOERR; 01105 01106 exit: 01107 if (H5Tclose(file_typeid) < 0) 01108 BAIL2(NC_EHDFERR); 01109 if (spaceid > 0 && H5Sclose(spaceid) < 0) 01110 BAIL2(NC_EHDFERR); 01111 #ifdef EXTRA_TESTS 01112 num_spaces--; 01113 #endif 01114 return retval; 01115 } 01116 01117 /* Read information about a user defined type from the HDF5 file, and 01118 * stash it in the group's list of types. */ 01119 static int 01120 read_type(NC_GRP_INFO_T *grp, hid_t hdf_typeid, char *type_name) 01121 { 01122 NC_TYPE_INFO_T *type; 01123 H5T_class_t class; 01124 hid_t native_typeid; 01125 size_t type_size; 01126 int retval = NC_NOERR; 01127 01128 assert(grp && type_name); 01129 01130 LOG((4, "%s: type_name %s grp->name %s", __func__, type_name, grp->name)); 01131 01132 /* What is the native type for this platform? */ 01133 if ((native_typeid = H5Tget_native_type(hdf_typeid, H5T_DIR_DEFAULT)) < 0) 01134 return NC_EHDFERR; 01135 01136 /* What is the size of this type on this platform. */ 01137 if (!(type_size = H5Tget_size(native_typeid))) 01138 return NC_EHDFERR; 01139 LOG((5, "type_size %d", type_size)); 01140 01141 /* Add to the list for this new type, and get a local pointer to it. */ 01142 if ((retval = nc4_type_list_add(grp, type_size, type_name, &type))) 01143 return retval; 01144 01145 /* Remember common info about this type. */ 01146 type->committed = NC_TRUE; 01147 type->hdf_typeid = hdf_typeid; 01148 H5Iinc_ref(type->hdf_typeid); /* Increment number of objects using ID */ 01149 type->native_hdf_typeid = native_typeid; 01150 01151 /* What is the class of this type, compound, vlen, etc. */ 01152 if ((class = H5Tget_class(hdf_typeid)) < 0) 01153 return NC_EHDFERR; 01154 switch (class) 01155 { 01156 case H5T_STRING: 01157 type->nc_type_class = NC_STRING; 01158 break; 01159 01160 case H5T_COMPOUND: 01161 { 01162 int nmembers; 01163 unsigned int m; 01164 char* member_name = NULL; 01165 #ifdef JNA 01166 char jna[1001]; 01167 #endif 01168 01169 type->nc_type_class = NC_COMPOUND; 01170 01171 if ((nmembers = H5Tget_nmembers(hdf_typeid)) < 0) 01172 return NC_EHDFERR; 01173 LOG((5, "compound type has %d members", nmembers)); 01174 for (m = 0; m < nmembers; m++) 01175 { 01176 hid_t member_hdf_typeid; 01177 hid_t member_native_typeid; 01178 size_t member_offset; 01179 H5T_class_t mem_class; 01180 nc_type member_xtype; 01181 01182 retval = NC_NOERR; 01183 01184 /* Get the typeid and native typeid of this member of the 01185 * compound type. */ 01186 if ((member_hdf_typeid = H5Tget_member_type(type->native_hdf_typeid, m)) < 0) 01187 return NC_EHDFERR; 01188 01189 if ((member_native_typeid = H5Tget_native_type(member_hdf_typeid, H5T_DIR_DEFAULT)) < 0) 01190 return NC_EHDFERR; 01191 01192 /* Get the name of the member.*/ 01193 member_name = H5Tget_member_name(type->native_hdf_typeid, m); 01194 if (!member_name || strlen(member_name) > NC_MAX_NAME) { 01195 retval = NC_EBADNAME; 01196 break; 01197 } 01198 #ifdef JNA 01199 else { 01200 strncpy(jna,member_name,1000); 01201 member_name = jna; 01202 } 01203 #endif 01204 01205 /* Offset in bytes on *this* platform. */ 01206 member_offset = H5Tget_member_offset(type->native_hdf_typeid, m); 01207 01208 /* Get dimensional data if this member is an array of something. */ 01209 if ((mem_class = H5Tget_class(member_hdf_typeid)) < 0) 01210 return NC_EHDFERR; 01211 if (mem_class == H5T_ARRAY) 01212 { 01213 int ndims, dim_size[NC_MAX_VAR_DIMS]; 01214 hsize_t dims[NC_MAX_VAR_DIMS]; 01215 int d; 01216 01217 if ((ndims = H5Tget_array_ndims(member_hdf_typeid)) < 0) { 01218 retval = NC_EHDFERR; 01219 break; 01220 } 01221 if (H5Tget_array_dims(member_hdf_typeid, dims, NULL) != ndims) { 01222 retval = NC_EHDFERR; 01223 break; 01224 } 01225 for (d = 0; d < ndims; d++) 01226 dim_size[d] = dims[d]; 01227 01228 /* What is the netCDF typeid of this member? */ 01229 if ((retval = get_netcdf_type(grp->nc4_info, H5Tget_super(member_hdf_typeid), 01230 &member_xtype))) 01231 break; 01232 01233 /* Add this member to our list of fields in this compound type. */ 01234 if ((retval = nc4_field_list_add(&type->u.c.field, type->u.c.num_fields++, member_name, 01235 member_offset, H5Tget_super(member_hdf_typeid), 01236 H5Tget_super(member_native_typeid), 01237 member_xtype, ndims, dim_size))) 01238 break; 01239 } 01240 else 01241 { 01242 /* What is the netCDF typeid of this member? */ 01243 if ((retval = get_netcdf_type(grp->nc4_info, member_native_typeid, 01244 &member_xtype))) 01245 break; 01246 01247 /* Add this member to our list of fields in this compound type. */ 01248 if ((retval = nc4_field_list_add(&type->u.c.field, type->u.c.num_fields++, member_name, 01249 member_offset, member_hdf_typeid, member_native_typeid, 01250 member_xtype, 0, NULL))) 01251 break; 01252 } 01253 01254 #ifndef JNA 01255 /* Free the member name (which HDF5 allocated for us). */ 01256 if(member_name != NULL) free(member_name); 01257 #endif 01258 member_name = NULL; 01259 } 01260 #ifndef JNA 01261 if(member_name != NULL) 01262 free(member_name); 01263 #endif 01264 if(retval) /* error exit from loop */ 01265 return retval; 01266 } 01267 break; 01268 01269 case H5T_VLEN: 01270 { 01271 htri_t ret; 01272 01273 /* For conveninence we allow user to pass vlens of strings 01274 * with null terminated strings. This means strings are 01275 * treated slightly differently by the API, although they are 01276 * really just VLENs of characters. */ 01277 if ((ret = H5Tis_variable_str(hdf_typeid)) < 0) 01278 return NC_EHDFERR; 01279 if (ret) 01280 type->nc_type_class = NC_STRING; 01281 else 01282 { 01283 hid_t base_hdf_typeid; 01284 nc_type base_nc_type = NC_NAT; 01285 01286 type->nc_type_class = NC_VLEN; 01287 01288 /* Find the base type of this vlen (i.e. what is this a 01289 * vlen of?) */ 01290 if (!(base_hdf_typeid = H5Tget_super(native_typeid))) 01291 return NC_EHDFERR; 01292 01293 /* What size is this type? */ 01294 if (!(type_size = H5Tget_size(base_hdf_typeid))) 01295 return NC_EHDFERR; 01296 01297 /* What is the netcdf corresponding type. */ 01298 if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid, 01299 &base_nc_type))) 01300 return retval; 01301 LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d", 01302 base_hdf_typeid, type_size, base_nc_type)); 01303 01304 /* Remember the base types for this vlen */ 01305 type->u.v.base_nc_typeid = base_nc_type; 01306 type->u.v.base_hdf_typeid = base_hdf_typeid; 01307 } 01308 } 01309 break; 01310 01311 case H5T_OPAQUE: 01312 type->nc_type_class = NC_OPAQUE; 01313 break; 01314 01315 case H5T_ENUM: 01316 { 01317 hid_t base_hdf_typeid; 01318 nc_type base_nc_type = NC_NAT; 01319 void *value; 01320 int i; 01321 char *member_name = NULL; 01322 #ifdef JNA 01323 char jna[1001]; 01324 #endif 01325 01326 type->nc_type_class = NC_ENUM; 01327 01328 /* Find the base type of this enum (i.e. what is this a 01329 * enum of?) */ 01330 if (!(base_hdf_typeid = H5Tget_super(hdf_typeid))) 01331 return NC_EHDFERR; 01332 /* What size is this type? */ 01333 if (!(type_size = H5Tget_size(base_hdf_typeid))) 01334 return NC_EHDFERR; 01335 /* What is the netcdf corresponding type. */ 01336 if ((retval = get_netcdf_type(grp->nc4_info, base_hdf_typeid, 01337 &base_nc_type))) 01338 return retval; 01339 LOG((5, "base_hdf_typeid 0x%x type_size %d base_nc_type %d", 01340 base_hdf_typeid, type_size, base_nc_type)); 01341 01342 /* Remember the base types for this enum */ 01343 type->u.e.base_nc_typeid = base_nc_type; 01344 type->u.e.base_hdf_typeid = base_hdf_typeid; 01345 01346 /* Find out how many member are in the enum. */ 01347 if ((type->u.e.num_members = H5Tget_nmembers(hdf_typeid)) < 0) 01348 return NC_EHDFERR; 01349 01350 /* Allocate space for one value. */ 01351 if (!(value = calloc(1, type_size))) 01352 return NC_ENOMEM; 01353 01354 /* Read each name and value defined in the enum. */ 01355 for (i = 0; i < type->u.e.num_members; i++) 01356 { 01357 retval = NC_NOERR; 01358 /* Get the name and value from HDF5. */ 01359 if (!(member_name = H5Tget_member_name(hdf_typeid, i))) 01360 { 01361 retval = NC_EHDFERR; 01362 break; 01363 } 01364 #ifdef JNA 01365 strncpy(jna,member_name,1000); 01366 member_name = jna; 01367 #endif 01368 01369 if (strlen(member_name) > NC_MAX_NAME) 01370 { 01371 retval = NC_EBADNAME; 01372 break; 01373 } 01374 if (H5Tget_member_value(hdf_typeid, i, value) < 0) 01375 { 01376 retval = NC_EHDFERR; 01377 break; 01378 } 01379 01380 /* Insert new field into this type's list of fields. */ 01381 if ((retval = nc4_enum_member_add(&type->u.e.enum_member, type->size, 01382 member_name, value))) 01383 { 01384 break; 01385 } 01386 01387 #ifndef JNA 01388 /* Free the member name (which HDF5 allocated for us). */ 01389 if(member_name != NULL) free(member_name); 01390 #endif 01391 member_name = NULL; 01392 } 01393 01394 #ifndef JNA 01395 if(member_name != NULL) 01396 free(member_name); 01397 #endif 01398 if(value) free(value); 01399 if(retval) /* error exit from loop */ 01400 return retval; 01401 } 01402 break; 01403 01404 default: 01405 LOG((0, "unknown class")); 01406 return NC_EBADCLASS; 01407 } 01408 return retval; 01409 } 01410 01411 /* This function is called by read_dataset, (which is called by 01412 * nc4_rec_read_metadata) when a netCDF variable is found in the 01413 * file. This function reads in all the metadata about the var, 01414 * including the attributes. */ 01415 static int 01416 read_var(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name, 01417 size_t ndims, NC_DIM_INFO_T *dim) 01418 { 01419 NC_VAR_INFO_T *var = NULL; 01420 hid_t access_pid = 0; 01421 int incr_id_rc = 0; /* Whether the dataset ID's ref count has been incremented */ 01422 int natts, a, d; 01423 01424 NC_ATT_INFO_T *att; 01425 hid_t attid = 0; 01426 char att_name[NC_MAX_HDF5_NAME + 1]; 01427 01428 #define CD_NELEMS_ZLIB 1 01429 #define CD_NELEMS_SZIP 4 01430 H5Z_filter_t filter; 01431 int num_filters; 01432 unsigned int cd_values[CD_NELEMS_SZIP]; 01433 size_t cd_nelems = CD_NELEMS_SZIP; 01434 hid_t propid = 0; 01435 H5D_fill_value_t fill_status; 01436 H5D_layout_t layout; 01437 hsize_t chunksize[NC_MAX_VAR_DIMS] = {0}; 01438 int retval = NC_NOERR; 01439 double rdcc_w0; 01440 int f; 01441 01442 assert(obj_name && grp); 01443 LOG((4, "%s: obj_name %s", __func__, obj_name)); 01444 01445 /* Add a variable to the end of the group's var list. */ 01446 if ((retval = nc4_var_list_add(&grp->var, &var))) 01447 BAIL(retval); 01448 01449 /* Fill in what we already know. */ 01450 var->hdf_datasetid = datasetid; 01451 H5Iinc_ref(var->hdf_datasetid); /* Increment number of objects using ID */ 01452 incr_id_rc++; /* Indicate that we've incremented the ref. count (for errors) */ 01453 var->varid = grp->nvars++; 01454 var->created = NC_TRUE; 01455 var->ndims = ndims; 01456 01457 /* We need some room to store information about dimensions for this 01458 * var. */ 01459 if (var->ndims) 01460 { 01461 if (!(var->dim = calloc(var->ndims, sizeof(NC_DIM_INFO_T *)))) 01462 BAIL(NC_ENOMEM); 01463 if (!(var->dimids = calloc(var->ndims, sizeof(int)))) 01464 BAIL(NC_ENOMEM); 01465 } 01466 01467 /* Get the current chunk cache settings. */ 01468 if ((access_pid = H5Dget_access_plist(datasetid)) < 0) 01469 BAIL(NC_EVARMETA); 01470 #ifdef EXTRA_TESTS 01471 num_plists++; 01472 #endif 01473 01474 /* Learn about current chunk cache settings. */ 01475 if ((H5Pget_chunk_cache(access_pid, &(var->chunk_cache_nelems), 01476 &(var->chunk_cache_size), &rdcc_w0)) < 0) 01477 BAIL(NC_EHDFERR); 01478 var->chunk_cache_preemption = rdcc_w0; 01479 01480 /* Check for a weird case: a non-coordinate variable that has the 01481 * same name as a dimension. It's legal in netcdf, and requires 01482 * that the HDF5 dataset name be changed. */ 01483 if (strlen(obj_name) > strlen(NON_COORD_PREPEND) && 01484 !strncmp(obj_name, NON_COORD_PREPEND, strlen(NON_COORD_PREPEND))) 01485 { 01486 /* Allocate space for the name. */ 01487 if (!(var->name = malloc(((strlen(obj_name) - strlen(NON_COORD_PREPEND))+ 1) * sizeof(char)))) 01488 BAIL(NC_ENOMEM); 01489 01490 strcpy(var->name, &obj_name[strlen(NON_COORD_PREPEND)]); 01491 01492 /* Allocate space for the HDF5 name. */ 01493 if (!(var->hdf5_name = malloc((strlen(obj_name) + 1) * sizeof(char)))) 01494 BAIL(NC_ENOMEM); 01495 01496 strcpy(var->hdf5_name, obj_name); 01497 } 01498 else 01499 { 01500 /* Allocate space for the name. */ 01501 if (!(var->name = malloc((strlen(obj_name) + 1) * sizeof(char)))) 01502 BAIL(NC_ENOMEM); 01503 01504 strcpy(var->name, obj_name); 01505 } 01506 01507 /* Find out what filters are applied to this HDF5 dataset, 01508 * fletcher32, deflate, and/or shuffle. All other filters are 01509 * ignored. */ 01510 if ((propid = H5Dget_create_plist(datasetid)) < 0) 01511 BAIL(NC_EHDFERR); 01512 #ifdef EXTRA_TESTS 01513 num_plists++; 01514 #endif /* EXTRA_TESTS */ 01515 01516 /* Get the chunking info for non-scalar vars. */ 01517 if ((layout = H5Pget_layout(propid)) < -1) 01518 BAIL(NC_EHDFERR); 01519 if (layout == H5D_CHUNKED) 01520 { 01521 if (H5Pget_chunk(propid, NC_MAX_VAR_DIMS, chunksize) < 0) 01522 BAIL(NC_EHDFERR); 01523 if (!(var->chunksizes = malloc(var->ndims * sizeof(size_t)))) 01524 BAIL(NC_ENOMEM); 01525 for (d = 0; d < var->ndims; d++) 01526 var->chunksizes[d] = chunksize[d]; 01527 } 01528 else if (layout == H5D_CONTIGUOUS || layout == H5D_COMPACT) 01529 var->contiguous = NC_TRUE; 01530 01531 /* The possible values of filter (which is just an int) can be 01532 * found in H5Zpublic.h. */ 01533 if ((num_filters = H5Pget_nfilters(propid)) < 0) 01534 BAIL(NC_EHDFERR); 01535 for (f = 0; f < num_filters; f++) 01536 { 01537 if ((filter = H5Pget_filter2(propid, f, NULL, &cd_nelems, 01538 cd_values, 0, NULL, NULL)) < 0) 01539 BAIL(NC_EHDFERR); 01540 switch (filter) 01541 { 01542 case H5Z_FILTER_SHUFFLE: 01543 var->shuffle = NC_TRUE; 01544 break; 01545 01546 case H5Z_FILTER_FLETCHER32: 01547 var->fletcher32 = NC_TRUE; 01548 break; 01549 01550 case H5Z_FILTER_DEFLATE: 01551 var->deflate = NC_TRUE; 01552 if (cd_nelems != CD_NELEMS_ZLIB || cd_values[0] > MAX_DEFLATE_LEVEL) 01553 BAIL(NC_EHDFERR); 01554 var->deflate_level = cd_values[0]; 01555 break; 01556 01557 case H5Z_FILTER_SZIP: 01558 var->szip = NC_TRUE; 01559 if (cd_nelems != CD_NELEMS_SZIP) 01560 BAIL(NC_EHDFERR); 01561 var->options_mask = cd_values[0]; 01562 var->pixels_per_block = cd_values[1]; 01563 break; 01564 01565 default: 01566 LOG((1, "Yikes! Unknown filter type found on dataset!")); 01567 break; 01568 } 01569 } 01570 01571 /* Learn all about the type of this variable. */ 01572 if ((retval = get_type_info2(grp->nc4_info, datasetid, 01573 &var->type_info))) 01574 BAIL(retval); 01575 01576 /* Indicate that the variable has a pointer to the type */ 01577 var->type_info->rc++; 01578 01579 /* Is there a fill value associated with this dataset? */ 01580 if (H5Pfill_value_defined(propid, &fill_status) < 0) 01581 BAIL(NC_EHDFERR); 01582 01583 /* Get the fill value, if there is one defined. */ 01584 if (fill_status == H5D_FILL_VALUE_USER_DEFINED) 01585 { 01586 /* Allocate space to hold the fill value. */ 01587 if (!var->fill_value) 01588 { 01589 if (var->type_info->nc_type_class == NC_VLEN) 01590 { 01591 if (!(var->fill_value = malloc(sizeof(nc_vlen_t)))) 01592 BAIL(NC_ENOMEM); 01593 } 01594 else if (var->type_info->nc_type_class == NC_STRING) 01595 { 01596 if (!(var->fill_value = malloc(sizeof(char *)))) 01597 BAIL(NC_ENOMEM); 01598 } 01599 else 01600 { 01601 assert(var->type_info->size); 01602 if (!(var->fill_value = malloc(var->type_info->size))) 01603 BAIL(NC_ENOMEM); 01604 } 01605 } 01606 01607 /* Get the fill value from the HDF5 property lust. */ 01608 if (H5Pget_fill_value(propid, var->type_info->native_hdf_typeid, 01609 var->fill_value) < 0) 01610 BAIL(NC_EHDFERR); 01611 } 01612 else 01613 var->no_fill = NC_TRUE; 01614 01615 /* If it's a scale, mark it as such. */ 01616 if (dim) 01617 { 01618 assert(ndims); 01619 var->dimscale = NC_TRUE; 01620 if (var->ndims > 1) 01621 { 01622 if ((retval = read_coord_dimids(var))) 01623 BAIL(retval); 01624 } 01625 else 01626 { 01627 /* sanity check */ 01628 assert(0 == strcmp(var->name, dim->name)); 01629 01630 var->dimids[0] = dim->dimid; 01631 var->dim[0] = dim; 01632 } 01633 dim->coord_var = var; 01634 } 01635 /* If this is not a scale, but has scales, iterate 01636 * through them. (i.e. this is a variable that is not a 01637 * coordinate variable) */ 01638 else 01639 { 01640 int num_scales = 0; 01641 01642 /* Find out how many scales are attached to this 01643 * dataset. H5DSget_num_scales returns an error if there are no 01644 * scales, so convert a negative return value to zero. */ 01645 num_scales = H5DSget_num_scales(datasetid, 0); 01646 if (num_scales < 0) 01647 num_scales = 0; 01648 01649 if (num_scales && ndims) 01650 { 01651 /* Allocate space to remember whether the dimscale has been attached 01652 * for each dimension. */ 01653 if (NULL == (var->dimscale_attached = calloc(ndims, sizeof(nc_bool_t)))) 01654 BAIL(NC_ENOMEM); 01655 01656 /* Store id information allowing us to match hdf5 01657 * dimscales to netcdf dimensions. */ 01658 if (NULL == (var->dimscale_hdf5_objids = malloc(ndims * sizeof(struct hdf5_objid)))) 01659 BAIL(NC_ENOMEM); 01660 for (d = 0; d < var->ndims; d++) 01661 { 01662 if (H5DSiterate_scales(var->hdf_datasetid, d, NULL, dimscale_visitor, 01663 &(var->dimscale_hdf5_objids[d])) < 0) 01664 BAIL(NC_EHDFERR); 01665 var->dimscale_attached[d] = NC_TRUE; 01666 } 01667 } 01668 } 01669 01670 /* Now read all the attributes of this variable, ignoring the 01671 ones that hold HDF5 dimension scale information. */ 01672 if ((natts = H5Aget_num_attrs(var->hdf_datasetid)) < 0) 01673 BAIL(NC_EATTMETA); 01674 for (a = 0; a < natts; a++) 01675 { 01676 /* Close the attribute and try to move on with our 01677 * lives. Like bits through the network port, so 01678 * flows the Days of Our Lives! */ 01679 if (attid && H5Aclose(attid) < 0) 01680 BAIL(NC_EHDFERR); 01681 01682 /* Open the att and get its name. */ 01683 if ((attid = H5Aopen_idx(var->hdf_datasetid, (unsigned int)a)) < 0) 01684 BAIL(NC_EATTMETA); 01685 if (H5Aget_name(attid, NC_MAX_HDF5_NAME, att_name) < 0) 01686 BAIL(NC_EATTMETA); 01687 LOG((4, "%s:: a %d att_name %s", __func__, a, att_name)); 01688 01689 /* Should we ignore this attribute? */ 01690 if (strcmp(att_name, REFERENCE_LIST) && 01691 strcmp(att_name, CLASS) && 01692 strcmp(att_name, DIMENSION_LIST) && 01693 strcmp(att_name, NAME) && 01694 strcmp(att_name, COORDINATES) && 01695 strcmp(att_name, NC_DIMID_ATT_NAME)) 01696 { 01697 /* Add to the end of the list of atts for this var. */ 01698 if ((retval = nc4_att_list_add(&var->att, &att))) 01699 BAIL(retval); 01700 01701 /* Fill in the information we know. */ 01702 att->attnum = var->natts++; 01703 if (!(att->name = strdup(att_name))) 01704 BAIL(NC_ENOMEM); 01705 01706 /* Read the rest of the info about the att, 01707 * including its values. */ 01708 if ((retval = read_hdf5_att(grp, attid, att))) 01709 { 01710 if (NC_EBADTYPID == retval) 01711 { 01712 if ((retval = nc4_att_list_del(&var->att, att))) 01713 BAIL(retval); 01714 continue; 01715 } 01716 else 01717 BAIL(retval); 01718 } 01719 01720 att->created = NC_TRUE; 01721 } /* endif not HDF5 att */ 01722 } /* next attribute */ 01723 01724 /* Is this a deflated variable with a chunksize greater than the 01725 * current cache size? */ 01726 if ((retval = nc4_adjust_var_cache(grp, var))) 01727 BAIL(retval); 01728 01729 exit: 01730 if (retval) 01731 { 01732 if (incr_id_rc && H5Idec_ref(datasetid) < 0) 01733 BAIL2(NC_EHDFERR); 01734 if (var && nc4_var_list_del(&grp->var, var)) 01735 BAIL2(NC_EHDFERR); 01736 } 01737 if (access_pid && H5Pclose(access_pid) < 0) 01738 BAIL2(NC_EHDFERR); 01739 #ifdef EXTRA_TESTS 01740 num_plists--; 01741 #endif 01742 if (propid > 0 && H5Pclose(propid) < 0) 01743 BAIL2(NC_EHDFERR); 01744 #ifdef EXTRA_TESTS 01745 num_plists--; 01746 #endif 01747 if (attid > 0 && H5Aclose(attid) < 0) 01748 BAIL2(NC_EHDFERR); 01749 return retval; 01750 } 01751 01752 /* This function is called by nc4_rec_read_metadata to read all the 01753 * group level attributes (the NC_GLOBAL atts for this group). */ 01754 static int 01755 read_grp_atts(NC_GRP_INFO_T *grp) 01756 { 01757 hid_t attid = 0; 01758 hsize_t num_obj, i; 01759 NC_ATT_INFO_T *att; 01760 NC_TYPE_INFO_T *type; 01761 char obj_name[NC_MAX_HDF5_NAME + 1]; 01762 int max_len; 01763 int retval = NC_NOERR; 01764 01765 num_obj = H5Aget_num_attrs(grp->hdf_grpid); 01766 for (i = 0; i < num_obj; i++) 01767 { 01768 /* Close an attribute from previous loop iteration */ 01769 /* (Should be from 'continue' statement, below) */ 01770 if (attid && H5Aclose(attid) < 0) 01771 BAIL(NC_EHDFERR); 01772 01773 if ((attid = H5Aopen_idx(grp->hdf_grpid, (unsigned int)i)) < 0) 01774 BAIL(NC_EATTMETA); 01775 if (H5Aget_name(attid, NC_MAX_NAME + 1, obj_name) < 0) 01776 BAIL(NC_EATTMETA); 01777 LOG((3, "reading attribute of _netCDF group, named %s", obj_name)); 01778 01779 /* This may be an attribute telling us that strict netcdf-3 01780 * rules are in effect. If so, we will make note of the fact, 01781 * but not add this attribute to the metadata. It's not a user 01782 * attribute, but an internal netcdf-4 one. */ 01783 if (!strcmp(obj_name, NC3_STRICT_ATT_NAME)) 01784 grp->nc4_info->cmode |= NC_CLASSIC_MODEL; 01785 else 01786 { 01787 /* Add an att struct at the end of the list, and then go to it. */ 01788 if ((retval = nc4_att_list_add(&grp->att, &att))) 01789 BAIL(retval); 01790 01791 /* Add the info about this attribute. */ 01792 max_len = strlen(obj_name) > NC_MAX_NAME ? NC_MAX_NAME : strlen(obj_name); 01793 if (!(att->name = malloc((max_len + 1) * sizeof(char)))) 01794 BAIL(NC_ENOMEM); 01795 strncpy(att->name, obj_name, max_len); 01796 att->name[max_len] = 0; 01797 att->attnum = grp->natts++; 01798 if ((retval = read_hdf5_att(grp, attid, att))) 01799 { 01800 if (NC_EBADTYPID == retval) 01801 { 01802 if ((retval = nc4_att_list_del(&grp->att, att))) 01803 BAIL(retval); 01804 continue; 01805 } 01806 else 01807 BAIL(retval); 01808 } 01809 att->created = NC_TRUE; 01810 if ((retval = nc4_find_type(grp->nc4_info, att->nc_typeid, &type))) 01811 BAIL(retval); 01812 } 01813 } 01814 01815 exit: 01816 if (attid > 0 && H5Aclose(attid) < 0) 01817 BAIL2(NC_EHDFERR); 01818 return retval; 01819 } 01820 01821 /* This function is called when nc4_rec_read_metadata encounters an HDF5 01822 * dataset when reading a file. */ 01823 static int 01824 read_dataset(NC_GRP_INFO_T *grp, hid_t datasetid, const char *obj_name, 01825 const H5G_stat_t *statbuf) 01826 { 01827 NC_DIM_INFO_T *dim = NULL; /* Dimension created for scales */ 01828 hid_t spaceid = 0; 01829 int ndims; 01830 int is_scale = 0; 01831 int retval = NC_NOERR; 01832 01833 /* Get the dimension information for this dataset. */ 01834 if ((spaceid = H5Dget_space(datasetid)) < 0) 01835 BAIL(NC_EHDFERR); 01836 #ifdef EXTRA_TESTS 01837 num_spaces++; 01838 #endif 01839 if ((ndims = H5Sget_simple_extent_ndims(spaceid)) < 0) 01840 BAIL(NC_EHDFERR); 01841 01842 /* Is this a dimscale? */ 01843 if ((is_scale = H5DSis_scale(datasetid)) < 0) 01844 BAIL(NC_EHDFERR); 01845 if (is_scale) 01846 { 01847 hsize_t dims[H5S_MAX_RANK]; 01848 hsize_t max_dims[H5S_MAX_RANK]; 01849 01850 /* Query the scale's size & max. size */ 01851 if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0) 01852 BAIL(NC_EHDFERR); 01853 01854 /* Read the scale information. */ 01855 if ((retval = read_scale(grp, datasetid, obj_name, statbuf, dims[0], 01856 max_dims[0], &dim))) 01857 BAIL(retval); 01858 } 01859 01860 /* Add a var to the linked list, and get its metadata, 01861 * unless this is one of those funny dimscales that are a 01862 * dimension in netCDF but not a variable. (Spooky!) */ 01863 if (NULL == dim || (dim && !dim->hdf_dimscaleid)) 01864 if ((retval = read_var(grp, datasetid, obj_name, ndims, dim))) 01865 BAIL(retval); 01866 01867 exit: 01868 if (spaceid && H5Sclose(spaceid) <0) 01869 BAIL2(retval); 01870 #ifdef EXTRA_TESTS 01871 num_spaces--; 01872 #endif 01873 01874 return retval; 01875 } 01876 01877 static int 01878 nc4_rec_read_metadata_cb_list_add(NC4_rec_read_metadata_obj_info_t **head, 01879 NC4_rec_read_metadata_obj_info_t **tail, 01880 const NC4_rec_read_metadata_obj_info_t *oinfo) 01881 { 01882 NC4_rec_read_metadata_obj_info_t *new_oinfo; /* Pointer to info for object */ 01883 01884 /* Allocate memory for the object's info */ 01885 if (!(new_oinfo = calloc(1, sizeof(*new_oinfo)))) 01886 return NC_ENOMEM; 01887 01888 /* Make a copy of the object's info */ 01889 memcpy(new_oinfo, oinfo, sizeof(*oinfo)); 01890 01891 if (*tail) 01892 { 01893 assert(*head); 01894 (*tail)->next = new_oinfo; 01895 *tail = new_oinfo; 01896 } 01897 else 01898 { 01899 assert(NULL == *head); 01900 *head = *tail = new_oinfo; 01901 } 01902 01903 return (NC_NOERR); 01904 } 01905 01906 static int 01907 nc4_rec_read_metadata_cb(hid_t grpid, const char *name, const H5L_info_t *info, 01908 void *_op_data) 01909 { 01910 NC4_rec_read_metadata_ud_t *udata = (NC4_rec_read_metadata_ud_t *)_op_data; /* Pointer to user data for callback */ 01911 NC4_rec_read_metadata_obj_info_t oinfo; /* Pointer to info for object */ 01912 int retval = H5_ITER_CONT; 01913 01914 /* Reset the memory for the object's info */ 01915 memset(&oinfo, 0, sizeof(oinfo)); 01916 01917 /* Open this critter. */ 01918 if ((oinfo.oid = H5Oopen(grpid, name, H5P_DEFAULT)) < 0) 01919 BAIL(H5_ITER_ERROR); 01920 01921 /* Get info about the object.*/ 01922 if (H5Gget_objinfo(oinfo.oid, ".", 1, &oinfo.statbuf) < 0) 01923 BAIL(H5_ITER_ERROR); 01924 01925 strncpy(oinfo.oname, name, NC_MAX_NAME); 01926 01927 /* Add object to list, for later */ 01928 switch(oinfo.statbuf.type) 01929 { 01930 case H5G_GROUP: 01931 LOG((3, "found group %s", oinfo.oname)); 01932 01933 /* Defer descending into child group immediately, so that the types 01934 * in the current group can be processed and be ready for use by 01935 * vars in the child group(s). 01936 */ 01937 if (nc4_rec_read_metadata_cb_list_add(&udata->grps_head, &udata->grps_tail, &oinfo)) 01938 BAIL(H5_ITER_ERROR); 01939 break; 01940 01941 case H5G_DATASET: 01942 LOG((3, "found dataset %s", oinfo.oname)); 01943 01944 /* Learn all about this dataset, which may be a dimscale 01945 * (i.e. dimension metadata), or real data. */ 01946 if ((retval = read_dataset(udata->grp, oinfo.oid, oinfo.oname, &oinfo.statbuf))) 01947 { 01948 /* Allow NC_EBADTYPID to transparently skip over datasets 01949 * which have a datatype that netCDF-4 doesn't undertand 01950 * (currently), but break out of iteration for other 01951 * errors. 01952 */ 01953 if(NC_EBADTYPID != retval) 01954 BAIL(H5_ITER_ERROR); 01955 else 01956 retval = H5_ITER_CONT; 01957 } 01958 01959 /* Close the object */ 01960 if (H5Oclose(oinfo.oid) < 0) 01961 BAIL(H5_ITER_ERROR); 01962 break; 01963 01964 case H5G_TYPE: 01965 LOG((3, "found datatype %s", oinfo.oname)); 01966 01967 /* Process the named datatype */ 01968 if (read_type(udata->grp, oinfo.oid, oinfo.oname)) 01969 BAIL(H5_ITER_ERROR); 01970 01971 /* Close the object */ 01972 if (H5Oclose(oinfo.oid) < 0) 01973 BAIL(H5_ITER_ERROR); 01974 break; 01975 01976 default: 01977 LOG((0, "Unknown object class %d in %s!", oinfo.statbuf.type, __func__)); 01978 BAIL(H5_ITER_ERROR); 01979 } 01980 01981 exit: 01982 if (retval) 01983 { 01984 if (oinfo.oid > 0 && H5Oclose(oinfo.oid) < 0) 01985 BAIL2(H5_ITER_ERROR); 01986 } 01987 01988 return (retval); 01989 } 01990 01991 /* This is the main function to recursively read all the metadata for the file. */ 01992 /* The links in the 'grp' are iterated over and added to the file's metadata 01993 * information. Note that child groups are not immediately processed, but 01994 * are deferred until all the other links in the group are handled (so that 01995 * vars in the child groups are guaranteed to have types that they use in 01996 * a parent group in place). 01997 */ 01998 static int 01999 nc4_rec_read_metadata(NC_GRP_INFO_T *grp) 02000 { 02001 NC4_rec_read_metadata_ud_t udata; /* User data for iteration */ 02002 NC4_rec_read_metadata_obj_info_t *oinfo; /* Pointer to info for object */ 02003 hsize_t idx=0; 02004 hid_t pid = 0; 02005 unsigned crt_order_flags = 0; 02006 H5_index_t iter_index; 02007 int retval = NC_NOERR; /* everything worked! */ 02008 02009 assert(grp && grp->name); 02010 LOG((3, "%s: grp->name %s", __func__, grp->name)); 02011 02012 /* Portably initialize user data for later */ 02013 memset(&udata, 0, sizeof(udata)); 02014 02015 /* Open this HDF5 group and retain its grpid. It will remain open 02016 * with HDF5 until this file is nc_closed. */ 02017 if (!grp->hdf_grpid) 02018 { 02019 if (grp->parent) 02020 { 02021 if ((grp->hdf_grpid = H5Gopen2(grp->parent->hdf_grpid, 02022 grp->name, H5P_DEFAULT)) < 0) 02023 BAIL(NC_EHDFERR); 02024 } 02025 else 02026 { 02027 if ((grp->hdf_grpid = H5Gopen2(grp->nc4_info->hdfid, 02028 "/", H5P_DEFAULT)) < 0) 02029 BAIL(NC_EHDFERR); 02030 } 02031 } 02032 assert(grp->hdf_grpid > 0); 02033 02034 /* Get the group creation flags, to check for creation ordering */ 02035 pid = H5Gget_create_plist(grp->hdf_grpid); 02036 H5Pget_link_creation_order(pid, &crt_order_flags); 02037 if (H5Pclose(pid) < 0) 02038 BAIL(NC_EHDFERR); 02039 02040 /* Set the iteration index to use */ 02041 if (crt_order_flags & H5P_CRT_ORDER_TRACKED) 02042 iter_index = H5_INDEX_CRT_ORDER; 02043 else 02044 { 02045 NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info; 02046 02047 /* Without creation ordering, file must be read-only. */ 02048 if (!h5->no_write) 02049 BAIL(NC_ECANTWRITE); 02050 02051 iter_index = H5_INDEX_NAME; 02052 } 02053 02054 /* Set user data for iteration */ 02055 udata.grp = grp; 02056 02057 /* Iterate over links in this group, building lists for the types, 02058 * datasets and groups encountered 02059 */ 02060 if (H5Literate(grp->hdf_grpid, iter_index, H5_ITER_INC, &idx, 02061 nc4_rec_read_metadata_cb, (void *)&udata) < 0) 02062 BAIL(NC_EHDFERR); 02063 02064 /* Process the child groups found */ 02065 /* (Deferred until now, so that the types in the current group get 02066 * processed and are available for vars in the child group(s).) 02067 */ 02068 for (oinfo = udata.grps_head; oinfo; oinfo = udata.grps_head) 02069 { 02070 NC_GRP_INFO_T *child_grp; 02071 NC_HDF5_FILE_INFO_T *h5 = grp->nc4_info; 02072 02073 /* Add group to file's hierarchy */ 02074 if ((retval = nc4_grp_list_add(&(grp->children), h5->next_nc_grpid++, 02075 grp, grp->nc4_info->controller, oinfo->oname, &child_grp))) 02076 BAIL(retval); 02077 02078 /* Recursively read the child group's metadata */ 02079 if ((retval = nc4_rec_read_metadata(child_grp))) 02080 BAIL(retval); 02081 02082 /* Close the object */ 02083 if (H5Oclose(oinfo->oid) < 0) 02084 BAIL(NC_EHDFERR); 02085 02086 /* Advance to next node, free current node */ 02087 udata.grps_head = oinfo->next; 02088 free(oinfo); 02089 } 02090 02091 /* Scan the group for global (i.e. group-level) attributes. */ 02092 if ((retval = read_grp_atts(grp))) 02093 BAIL(retval); 02094 02095 exit: 02096 /* Clean up local information on error, if anything remains */ 02097 if (retval) 02098 { 02099 for (oinfo = udata.grps_head; oinfo; oinfo = udata.grps_head) 02100 { 02101 /* Close the object */ 02102 if (H5Oclose(oinfo->oid) < 0) 02103 BAIL2(NC_EHDFERR); 02104 02105 /* Advance to next node, free current node */ 02106 udata.grps_head = oinfo->next; 02107 free(oinfo); 02108 } 02109 } 02110 02111 return retval; 02112 } 02113 02114 /* Open a netcdf-4 file. Things have already been kicked off in 02115 * ncfunc.c in nc_open, but here the netCDF-4 part of opening a file 02116 * is handled. */ 02117 static int 02118 nc4_open_file(const char *path, int mode, MPI_Comm comm, 02119 MPI_Info info, NC *nc) 02120 { 02121 hid_t fapl_id = H5P_DEFAULT; 02122 unsigned flags = (mode & NC_WRITE) ? 02123 H5F_ACC_RDWR : H5F_ACC_RDONLY; 02124 int retval; 02125 NC_HDF5_FILE_INFO_T* nc4_info = NULL; 02126 #ifdef USE_PARALLEL 02127 int comm_duped = 0; /* Whether the MPI Communicator was duplicated */ 02128 int info_duped = 0; /* Whether the MPI Info object was duplicated */ 02129 #endif /* !USE_PARALLEL */ 02130 02131 LOG((3, "%s: path %s mode %d", __func__, path, mode)); 02132 assert(path && nc); 02133 /* Stop diskless open in its tracks */ 02134 if(mode & NC_DISKLESS) 02135 return NC_EDISKLESS; 02136 02137 /* Add necessary structs to hold netcdf-4 file data. */ 02138 if ((retval = nc4_nc4f_list_add(nc, path, mode))) 02139 BAIL(retval); 02140 nc4_info = NC4_DATA(nc); 02141 assert(nc4_info && nc4_info->root_grp); 02142 02143 /* Need this access plist to control how HDF5 handles open onjects 02144 * on file close. (Setting H5F_CLOSE_SEMI will cause H5Fclose to 02145 * fail if there are any open objects in the file. */ 02146 if ((fapl_id = H5Pcreate(H5P_FILE_ACCESS)) < 0) 02147 BAIL(NC_EHDFERR); 02148 #ifdef EXTRA_TESTS 02149 num_plists++; 02150 #endif 02151 #ifdef EXTRA_TESTS 02152 if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_SEMI)) 02153 BAIL(NC_EHDFERR); 02154 #else 02155 if (H5Pset_fclose_degree(fapl_id, H5F_CLOSE_STRONG)) 02156 BAIL(NC_EHDFERR); 02157 #endif 02158 02159 #ifdef USE_PARALLEL 02160 /* If this is a parallel file create, set up the file creation 02161 property list. */ 02162 if (mode & NC_MPIIO || mode & NC_MPIPOSIX) 02163 { 02164 nc4_info->parallel = NC_TRUE; 02165 if (mode & NC_MPIIO) /* MPI/IO */ 02166 { 02167 LOG((4, "opening parallel file with MPI/IO")); 02168 if (H5Pset_fapl_mpio(fapl_id, comm, info) < 0) 02169 BAIL(NC_EPARINIT); 02170 } 02171 else /* MPI/POSIX */ 02172 { 02173 LOG((4, "opening parallel file with MPI/posix")); 02174 if (H5Pset_fapl_mpiposix(fapl_id, comm, 0) < 0) 02175 BAIL(NC_EPARINIT); 02176 } 02177 02178 /* Keep copies of the MPI Comm & Info objects */ 02179 if (MPI_SUCCESS != MPI_Comm_dup(comm, &nc4_info->comm)) 02180 BAIL(NC_EMPI); 02181 comm_duped++; 02182 if (MPI_INFO_NULL != info) 02183 { 02184 if (MPI_SUCCESS != MPI_Info_dup(info, &nc4_info->info)) 02185 BAIL(NC_EMPI); 02186 info_duped++; 02187 } 02188 else 02189 { 02190 /* No dup, just copy it. */ 02191 nc4_info->info = info; 02192 } 02193 } 02194 #else /* only set cache for non-parallel. */ 02195 if (H5Pset_cache(fapl_id, 0, nc4_chunk_cache_nelems, nc4_chunk_cache_size, 02196 nc4_chunk_cache_preemption) < 0) 02197 BAIL(NC_EHDFERR); 02198 LOG((4, "%s: set HDF raw chunk cache to size %d nelems %d preemption %f", 02199 __func__, nc4_chunk_cache_size, nc4_chunk_cache_nelems, nc4_chunk_cache_preemption)); 02200 #endif /* USE_PARALLEL */ 02201 02202 /* The NetCDF-3.x prototype contains an mode option NC_SHARE for 02203 multiple processes accessing the dataset concurrently. As there 02204 is no HDF5 equivalent, NC_SHARE is treated as NC_NOWRITE. */ 02205 if ((nc4_info->hdfid = H5Fopen(path, flags, fapl_id)) < 0) 02206 BAIL(NC_EHDFERR); 02207 02208 /* Does the mode specify that this file is read-only? */ 02209 if ((mode & NC_WRITE) == 0) 02210 nc4_info->no_write = NC_TRUE; 02211 02212 /* Now read in all the metadata. Some types and dimscale 02213 * information may be difficult to resolve here, if, for example, a 02214 * dataset of user-defined type is encountered before the 02215 * definition of that type. */ 02216 if ((retval = nc4_rec_read_metadata(nc4_info->root_grp))) 02217 BAIL(retval); 02218 02219 /* Now figure out which netCDF dims are indicated by the dimscale 02220 * information. */ 02221 if ((retval = nc4_rec_match_dimscales(nc4_info->root_grp))) 02222 BAIL(retval); 02223 02224 #ifdef LOGGING 02225 /* This will print out the names, types, lens, etc of the vars and 02226 atts in the file, if the logging level is 2 or greater. */ 02227 log_metadata_nc(nc); 02228 #endif 02229 02230 /* Close the property list. */ 02231 if (H5Pclose(fapl_id) < 0) 02232 BAIL(NC_EHDFERR); 02233 #ifdef EXTRA_TESTS 02234 num_plists--; 02235 #endif 02236 02237 return NC_NOERR; 02238 02239 exit: 02240 #ifdef USE_PARALLEL 02241 if (comm_duped) MPI_Comm_free(&nc4_info->comm); 02242 if (info_duped) MPI_Info_free(&nc4_info->info); 02243 #endif 02244 #ifdef EXTRA_TESTS 02245 num_plists--; 02246 #endif 02247 if (fapl_id != H5P_DEFAULT) H5Pclose(fapl_id); 02248 if (!nc4_info) return retval; 02249 close_netcdf4_file(nc4_info,1); /* treat like abort*/ 02250 return retval; 02251 } 02252 02253 /* Given an HDF4 type, set a pointer to netcdf type. */ 02254 #ifdef USE_HDF4 02255 static int 02256 get_netcdf_type_from_hdf4(NC_HDF5_FILE_INFO_T *h5, int32 hdf4_typeid, 02257 nc_type *xtype, NC_TYPE_INFO_T *type_info) 02258 { 02259 int t; 02260 assert(h5 && xtype); 02261 02262 switch(hdf4_typeid) 02263 { 02264 case DFNT_CHAR: 02265 *xtype = NC_CHAR; 02266 t = 0; 02267 break; 02268 case DFNT_UCHAR: 02269 case DFNT_UINT8: 02270 *xtype = NC_UBYTE; 02271 t = 6; 02272 break; 02273 case DFNT_INT8: 02274 *xtype = NC_BYTE; 02275 t = 1; 02276 break; 02277 case DFNT_INT16: 02278 *xtype = NC_SHORT; 02279 t = 2; 02280 break; 02281 case DFNT_UINT16: 02282 *xtype = NC_USHORT; 02283 t = 7; 02284 break; 02285 case DFNT_INT32: 02286 *xtype = NC_INT; 02287 t = 3; 02288 break; 02289 case DFNT_UINT32: 02290 *xtype = NC_UINT; 02291 t = 8; 02292 break; 02293 case DFNT_FLOAT32: 02294 *xtype = NC_FLOAT; 02295 t = 4; 02296 break; 02297 case DFNT_FLOAT64: 02298 *xtype = NC_DOUBLE; 02299 t = 5; 02300 break; 02301 default: 02302 *xtype = NC_NAT; 02303 return NC_EBADTYPID; 02304 } 02305 02306 if (type_info) 02307 { 02308 if (hdf4_typeid == DFNT_FLOAT32) 02309 type_info->nc_type_class = NC_FLOAT; 02310 else if (hdf4_typeid == DFNT_FLOAT64) 02311 type_info->nc_type_class = NC_DOUBLE; 02312 else if (hdf4_typeid == DFNT_CHAR) 02313 type_info->nc_type_class = NC_STRING; 02314 else 02315 type_info->nc_type_class = NC_INT; 02316 type_info->endianness = NC_ENDIAN_BIG; 02317 type_info->nc_typeid = *xtype; 02318 type_info->size = nc_type_size_g[t]; 02319 if (!(type_info->name = strdup(nc_type_name_g[t]))) 02320 return NC_ENOMEM; 02321 } 02322 02323 return NC_NOERR; 02324 } 02325 02326 /* Open a HDF4 file. Things have already been kicked off in nc_open, 02327 * but here the netCDF-4 part of opening a file is handled. */ 02328 static int 02329 nc4_open_hdf4_file(const char *path, int mode, NC *nc) 02330 { 02331 NC_HDF5_FILE_INFO_T *h5; 02332 NC_GRP_INFO_T *grp; 02333 NC_ATT_INFO_T *att; 02334 int32 num_datasets, num_gatts; 02335 int32 rank; 02336 int v, d, a; 02337 int retval; 02338 NC_HDF5_FILE_INFO_T* nc4_info = NULL; 02339 02340 LOG((3, "%s: path %s mode %d", __func__, path, mode)); 02341 assert(path && nc); 02342 02343 /* Must be read-only access to hdf4 files. */ 02344 if (mode & NC_WRITE) 02345 return NC_EINVAL; 02346 02347 /* Add necessary structs to hold netcdf-4 file data. */ 02348 if ((retval = nc4_nc4f_list_add(nc, path, mode))) 02349 return retval; 02350 nc4_info = NC4_DATA(nc); 02351 assert(nc4_info && nc4_info->root_grp); 02352 h5 = nc4_info; 02353 h5->hdf4 = NC_TRUE; 02354 grp = h5->root_grp; 02355 h5->no_write = NC_TRUE; 02356 02357 /* Open the file and initialize SD interface. */ 02358 if ((h5->sdid = SDstart(path, DFACC_READ)) == FAIL) 02359 return NC_EHDFERR; 02360 02361 /* Learn how many datasets and global atts we have. */ 02362 if (SDfileinfo(h5->sdid, &num_datasets, &num_gatts)) 02363 return NC_EHDFERR; 02364 02365 /* Read the atts. */ 02366 for (a = 0; a < num_gatts; a++) 02367 { 02368 int32 att_data_type, att_count; 02369 size_t att_type_size; 02370 02371 /* Add to the end of the list of atts for this var. */ 02372 if ((retval = nc4_att_list_add(&h5->root_grp->att, &att))) 02373 return retval; 02374 att->attnum = grp->natts++; 02375 att->created = NC_TRUE; 02376 02377 /* Learn about this attribute. */ 02378 if (!(att->name = malloc(NC_MAX_HDF4_NAME * sizeof(char)))) 02379 return NC_ENOMEM; 02380 if (SDattrinfo(h5->sdid, a, att->name, &att_data_type, &att_count)) 02381 return NC_EATTMETA; 02382 if ((retval = get_netcdf_type_from_hdf4(h5, att_data_type, 02383 &att->nc_typeid, NULL))) 02384 return retval; 02385 att->len = att_count; 02386 02387 /* Allocate memory to hold the data. */ 02388 if ((retval = nc4_get_typelen_mem(h5, att->nc_typeid, 0, &att_type_size))) 02389 return retval; 02390 if (!(att->data = malloc(att_type_size * att->len))) 02391 return NC_ENOMEM; 02392 02393 /* Read the data. */ 02394 if (SDreadattr(h5->sdid, a, att->data)) 02395 return NC_EHDFERR; 02396 } 02397 02398 /* Read each dataset. */ 02399 for (v = 0; v < num_datasets; v++) 02400 { 02401 NC_VAR_INFO_T *var; 02402 int32 data_type, num_atts; 02403 /* Problem: Number of dims is returned by the call that requires 02404 a pre-allocated array, 'dimsize'. 02405 From SDS_SD website: 02406 http://www.hdfgroup.org/training/HDFtraining/UsersGuide/SDS_SD.fm3.html 02407 The maximum rank is 32, or MAX_VAR_DIMS (as defined in netcdf.h). 02408 02409 int32 dimsize[MAX_VAR_DIMS]; 02410 */ 02411 int32 *dimsize = NULL; 02412 size_t var_type_size; 02413 int a; 02414 02415 /* Add a variable to the end of the group's var list. */ 02416 if ((retval = nc4_var_list_add(&grp->var, &var))) 02417 return retval; 02418 02419 var->varid = grp->nvars++; 02420 var->created = NC_TRUE; 02421 var->written_to = NC_TRUE; 02422 02423 /* Open this dataset in HDF4 file. */ 02424 if ((var->sdsid = SDselect(h5->sdid, v)) == FAIL) 02425 return NC_EVARMETA; 02426 02427 /* Get shape, name, type, and attribute info about this dataset. */ 02428 if (!(var->name = malloc(NC_MAX_HDF4_NAME + 1))) 02429 return NC_ENOMEM; 02430 02431 /* Invoke SDgetInfo with null dimsize to get rank. */ 02432 if (SDgetinfo(var->sdsid, var->name, &rank, NULL, &data_type, &num_atts)) 02433 return NC_EVARMETA; 02434 02435 if(!(dimsize = (int32*)malloc(sizeof(int32)*rank))) 02436 return NC_ENOMEM; 02437 02438 if (SDgetinfo(var->sdsid, var->name, &rank, dimsize, &data_type, &num_atts)) { 02439 if(dimsize) free(dimsize); 02440 return NC_EVARMETA; 02441 } 02442 02443 var->ndims = rank; 02444 var->hdf4_data_type = data_type; 02445 02446 /* Fill special type_info struct for variable type information. */ 02447 if (!(var->type_info = calloc(1, sizeof(NC_TYPE_INFO_T)))) { 02448 if(dimsize) free(dimsize); 02449 return NC_ENOMEM; 02450 } 02451 02452 if ((retval = get_netcdf_type_from_hdf4(h5, data_type, &var->type_info->nc_typeid, var->type_info))) { 02453 if(dimsize) free(dimsize); 02454 return retval; 02455 } 02456 02457 /* Indicate that the variable has a pointer to the type */ 02458 var->type_info->rc++; 02459 02460 if ((retval = nc4_get_typelen_mem(h5, var->type_info->nc_typeid, 0, &var_type_size))) { 02461 if(dimsize) free(dimsize); 02462 return retval; 02463 } 02464 02465 var->type_info->size = var_type_size; 02466 LOG((3, "reading HDF4 dataset %s, rank %d netCDF type %d", var->name, 02467 rank, var->type_info->nc_typeid)); 02468 02469 /* Get the fill value. */ 02470 if (!(var->fill_value = malloc(var_type_size))) { 02471 if(dimsize) free(dimsize); 02472 return NC_ENOMEM; 02473 } 02474 02475 if (SDgetfillvalue(var->sdsid, var->fill_value)) 02476 { 02477 /* Whoops! No fill value! */ 02478 free(var->fill_value); 02479 var->fill_value = NULL; 02480 } 02481 02482 /* Allocate storage for dimension info in this variable. */ 02483 if (var->ndims) 02484 { 02485 if (!(var->dim = malloc(sizeof(NC_DIM_INFO_T *) * var->ndims))) { 02486 if(dimsize) free(dimsize); 02487 return NC_ENOMEM; 02488 } 02489 02490 if (!(var->dimids = malloc(sizeof(int) * var->ndims))) { 02491 if(dimsize) free(dimsize); 02492 return NC_ENOMEM; 02493 } 02494 } 02495 02496 02497 /* Find its dimensions. */ 02498 for (d = 0; d < var->ndims; d++) 02499 { 02500 int32 dimid, dim_len, dim_data_type, dim_num_attrs; 02501 char dim_name[NC_MAX_NAME + 1]; 02502 NC_DIM_INFO_T *dim; 02503 02504 if ((dimid = SDgetdimid(var->sdsid, d)) == FAIL) { 02505 if(dimsize) free(dimsize); 02506 return NC_EDIMMETA; 02507 } 02508 if (SDdiminfo(dimid, dim_name, &dim_len, &dim_data_type, 02509 &dim_num_attrs)) 02510 { 02511 if(dimsize) free(dimsize); 02512 return NC_EDIMMETA; 02513 } 02514 02515 /* Do we already have this dimension? HDF4 explicitly uses 02516 * the name to tell. */ 02517 for (dim = grp->dim; dim; dim = dim->l.next) 02518 if (!strcmp(dim->name, dim_name)) 02519 break; 02520 02521 /* If we didn't find this dimension, add one. */ 02522 if (!dim) 02523 { 02524 LOG((4, "adding dimension %s for HDF4 dataset %s", 02525 dim_name, var->name)); 02526 if ((retval = nc4_dim_list_add(&grp->dim, &dim))) 02527 return retval; 02528 grp->ndims++; 02529 dim->dimid = grp->nc4_info->next_dimid++; 02530 if (strlen(dim_name) > NC_MAX_HDF4_NAME) 02531 return NC_EMAXNAME; 02532 if (!(dim->name = strdup(dim_name))) 02533 return NC_ENOMEM; 02534 if (dim_len) 02535 dim->len = dim_len; 02536 else 02537 dim->len = *dimsize; 02538 } 02539 02540 /* Tell the variable the id of this dimension. */ 02541 var->dimids[d] = dim->dimid; 02542 } 02543 02544 /* Read the atts. */ 02545 for (a = 0; a < num_atts; a++) 02546 { 02547 int32 att_data_type, att_count; 02548 size_t att_type_size; 02549 02550 /* Add to the end of the list of atts for this var. */ 02551 if ((retval = nc4_att_list_add(&var->att, &att))) { 02552 if(dimsize) free(dimsize); 02553 return retval; 02554 } 02555 att->attnum = var->natts++; 02556 att->created = NC_TRUE; 02557 02558 /* Learn about this attribute. */ 02559 if (!(att->name = malloc(NC_MAX_HDF4_NAME * sizeof(char)))) { 02560 if(dimsize) free(dimsize); 02561 return NC_ENOMEM; 02562 } 02563 if (SDattrinfo(var->sdsid, a, att->name, &att_data_type, &att_count)) { 02564 if(dimsize) free(dimsize); 02565 return NC_EATTMETA; 02566 } 02567 if ((retval = get_netcdf_type_from_hdf4(h5, att_data_type, 02568 &att->nc_typeid, NULL))) { 02569 if(dimsize) free(dimsize); 02570 return retval; 02571 } 02572 02573 att->len = att_count; 02574 02575 /* Allocate memory to hold the data. */ 02576 if ((retval = nc4_get_typelen_mem(h5, att->nc_typeid, 0, &att_type_size))) { 02577 if(dimsize) free(dimsize); 02578 return retval; 02579 } 02580 if (!(att->data = malloc(att_type_size * att->len))) { 02581 if(dimsize) free(dimsize); 02582 return NC_ENOMEM; 02583 } 02584 02585 /* Read the data. */ 02586 if (SDreadattr(var->sdsid, a, att->data)) { 02587 if(dimsize) free(dimsize); 02588 return NC_EHDFERR; 02589 } 02590 } 02591 if(dimsize) free(dimsize); 02592 } /* next var */ 02593 02594 #ifdef LOGGING 02595 /* This will print out the names, types, lens, etc of the vars and 02596 atts in the file, if the logging level is 2 or greater. */ 02597 log_metadata_nc(h5->root_grp->nc4_info->controller); 02598 #endif 02599 return NC_NOERR; 02600 return NC_ENOTBUILT; 02601 } 02602 #endif /* USE_HDF4 */ 02603 02604 int 02605 NC4_open(const char *path, int mode, int basepe, size_t *chunksizehintp, 02606 int use_parallel, void *mpidata, NC_Dispatch *dispatch, NC *nc_file) 02607 { 02608 int hdf_file = 0; 02609 MPI_Comm comm = MPI_COMM_WORLD; 02610 MPI_Info info = MPI_INFO_NULL; 02611 int res; 02612 02613 assert(nc_file && path); 02614 02615 LOG((1, "%s: path %s mode %d comm %d info %d", 02616 __func__, path, mode, comm, info)); 02617 02618 #ifdef USE_PARALLEL 02619 if (mpidata) 02620 { 02621 comm = ((NC_MPI_INFO *)mpidata)->comm; 02622 info = ((NC_MPI_INFO *)mpidata)->info; 02623 } 02624 #endif /* USE_PARALLEL */ 02625 02626 /* If this is our first file, turn off HDF5 error messages. */ 02627 if (virgin) 02628 { 02629 if (H5Eset_auto(NULL, NULL) < 0) 02630 LOG((0, "Couldn't turn off HDF5 error messages!")); 02631 LOG((1, "HDF5 error messages turned off!")); 02632 virgin = 0; 02633 } 02634 02635 /* Check the mode for validity. First make sure only certain bits 02636 * are turned on. Also MPI I/O and MPI POSIX cannot both be 02637 * selected at once. */ 02638 if (mode & ~(NC_WRITE | NC_SHARE | NC_MPIIO | NC_MPIPOSIX | 02639 NC_PNETCDF | NC_NOCLOBBER | NC_NETCDF4 | NC_CLASSIC_MODEL) || 02640 (mode & NC_MPIIO && mode & NC_MPIPOSIX)) 02641 return NC_EINVAL; 02642 02643 02644 /* Depending on the type of file, open it. */ 02645 02646 #if 0 /*def USE_PNETCDF*/ 02647 if(mode & NC_PNETCDF) { 02648 /* this is not really an hdf file */ 02649 int pnetcdf_nvars, i; 02650 NC_HDF5_FILE_INFO_T* nc4_info; 02651 02652 /* Create the fake nc4_info data */ 02653 res = nc4_nc4f_list_add(nc_file, path, mode); 02654 02655 nc4_info = NC4_DATA(nc_file); 02656 assert(nc4_info); 02657 02658 res = ncmpi_open(comm, path, mode, info, &(nc_file->int_ncid)); 02659 nc4_info->pnetcdf_file++; 02660 02661 /* Default to independent access, like netCDF-4/HDF5 files. */ 02662 if (!res) 02663 res = ncmpi_begin_indep_data(nc_file->int_ncid); 02664 02665 /* I need to keep track of the ndims of each var to translate 02666 * start, count, and stride arrays to MPI_Offset type. */ 02667 if (!res) 02668 { 02669 res = ncmpi_inq_nvars(nc_file->int_ncid, &pnetcdf_nvars); 02670 for (i = 0; i < pnetcdf_nvars; i++) 02671 res = ncmpi_inq_varndims(nc_file->int_ncid, i, 02672 &(nc4_info->pnetcdf_ndims[i])); 02673 02674 } 02675 } else 02676 #endif 02677 { 02678 /* Figure out if this is a hdf4 or hdf5 file. */ 02679 if ((res = nc_check_for_hdf(path, use_parallel, comm, info, &hdf_file))) 02680 return res; 02681 02682 if (hdf_file == NC_HDF5_FILE) 02683 { 02684 nc_file->int_ncid = nc_file->ext_ncid; 02685 res = nc4_open_file(path, mode, comm, info, nc_file); 02686 } 02687 #ifdef USE_HDF4 02688 else if (hdf_file == NC_HDF4_FILE) 02689 { 02690 nc_file->int_ncid = nc_file->ext_ncid; 02691 res = nc4_open_hdf4_file(path, mode, nc_file); 02692 } 02693 #endif /* USE_HDF4 */ 02694 else /* netcdf */ 02695 { 02696 assert(0); /* should never happen */ 02697 } 02698 } 02699 02700 return res; 02701 } 02702 02703 /* Unfortunately HDF only allows specification of fill value only when 02704 a dataset is created. Whereas in netcdf, you first create the 02705 variable and then (optionally) specify the fill value. To 02706 accomplish this in HDF5 I have to delete the dataset, and recreate 02707 it, with the fill value specified. */ 02708 /* QAK: This looks completely unused in the code. (?) */ 02709 int 02710 NC4_set_fill(int ncid, int fillmode, int *old_modep) 02711 { 02712 NC *nc; 02713 NC_HDF5_FILE_INFO_T* nc4_info; 02714 02715 LOG((2, "%s: ncid 0x%x fillmode %d", __func__, ncid, fillmode)); 02716 02717 if (!(nc = nc4_find_nc_file(ncid,&nc4_info))) 02718 return NC_EBADID; 02719 assert(nc4_info); 02720 02721 /* Trying to set fill on a read-only file? You sicken me! */ 02722 if (nc4_info->no_write) 02723 return NC_EPERM; 02724 02725 /* Did you pass me some weird fillmode? */ 02726 if (fillmode != NC_FILL && fillmode != NC_NOFILL) 02727 return NC_EINVAL; 02728 02729 /* If the user wants to know, tell him what the old mode was. */ 02730 if (old_modep) 02731 *old_modep = nc4_info->fill_mode; 02732 02733 nc4_info->fill_mode = fillmode; 02734 02735 #if 0 /*def USE_PNETCDF*/ 02736 /* Take care of files created/opened with parallel-netcdf library. */ 02737 if (nc4_info->pnetcdf_file) 02738 return ncmpi_set_fill(nc->int_ncid, fillmode, old_modep); 02739 #endif /* USE_PNETCDF */ 02740 02741 02742 return NC_NOERR; 02743 } 02744 02745 /* Put the file back in redef mode. This is done automatically for 02746 * netcdf-4 files, if the user forgets. */ 02747 int 02748 NC4_redef(int ncid) 02749 { 02750 //NC *nc; 02751 NC_HDF5_FILE_INFO_T* nc4_info; 02752 02753 LOG((1, "%s: ncid 0x%x", __func__, ncid)); 02754 02755 /* Find this file's metadata. */ 02756 if (!(nc4_find_nc_file(ncid,&nc4_info))) 02757 return NC_EBADID; 02758 assert(nc4_info); 02759 02760 #if 0 /*def USE_PNETCDF*/ 02761 /* Take care of files created/opened with parallel-netcdf library. */ 02762 if (nc4_info->pnetcdf_file) 02763 return ncmpi_redef(nc->int_ncid); 02764 #endif /* USE_PNETCDF */ 02765 02766 /* If we're already in define mode, return an error. */ 02767 if (nc4_info->flags & NC_INDEF) 02768 return NC_EINDEFINE; 02769 02770 /* If the file is read-only, return an error. */ 02771 if (nc4_info->no_write) 02772 return NC_EPERM; 02773 02774 /* Set define mode. */ 02775 nc4_info->flags |= NC_INDEF; 02776 02777 /* For nc_abort, we need to remember if we're in define mode as a 02778 redef. */ 02779 nc4_info->redef = NC_TRUE; 02780 02781 return NC_NOERR; 02782 } 02783 02784 /* For netcdf-4 files, this just calls nc_enddef, ignoring the extra 02785 * parameters. */ 02786 int 02787 NC4__enddef(int ncid, size_t h_minfree, size_t v_align, 02788 size_t v_minfree, size_t r_align) 02789 { 02790 if (nc4_find_nc_file(ncid,NULL) == NULL) 02791 return NC_EBADID; 02792 02793 return NC4_enddef(ncid); 02794 } 02795 02796 /* Take the file out of define mode. This is called automatically for 02797 * netcdf-4 files, if the user forgets. */ 02798 static int NC4_enddef(int ncid) 02799 { 02800 NC *nc; 02801 NC_HDF5_FILE_INFO_T* nc4_info; 02802 02803 LOG((1, "%s: ncid 0x%x", __func__, ncid)); 02804 02805 if (!(nc = nc4_find_nc_file(ncid,&nc4_info))) 02806 return NC_EBADID; 02807 assert(nc4_info); 02808 02809 #if 0 /*def USE_PNETCDF*/ 02810 if (nc4_info->pnetcdf_file) 02811 { 02812 int res; 02813 res = ncmpi_enddef(nc->int_ncid); 02814 if (!res) 02815 { 02816 if (nc4_info->pnetcdf_access_mode == NC_INDEPENDENT) 02817 res = ncmpi_begin_indep_data(nc->int_ncid); 02818 } 02819 return res; 02820 } 02821 #endif /* USE_PNETCDF */ 02822 02823 return nc4_enddef_netcdf4_file(nc4_info); 02824 } 02825 02826 /* This function will write all changed metadata, and (someday) reread 02827 * all metadata from the file. */ 02828 static int 02829 sync_netcdf4_file(NC_HDF5_FILE_INFO_T *h5) 02830 { 02831 int retval; 02832 02833 assert(h5); 02834 LOG((3, "%s", __func__)); 02835 02836 /* If we're in define mode, that's an error, for strict nc3 rules, 02837 * otherwise, end define mode. */ 02838 if (h5->flags & NC_INDEF) 02839 { 02840 if (h5->cmode & NC_CLASSIC_MODEL) 02841 return NC_EINDEFINE; 02842 02843 /* Turn define mode off. */ 02844 h5->flags ^= NC_INDEF; 02845 02846 /* Redef mode needs to be tracked seperately for nc_abort. */ 02847 h5->redef = NC_FALSE; 02848 } 02849 02850 #ifdef LOGGING 02851 /* This will print out the names, types, lens, etc of the vars and 02852 atts in the file, if the logging level is 2 or greater. */ 02853 log_metadata_nc(h5->root_grp->nc4_info->controller); 02854 #endif 02855 02856 /* Write any metadata that has changed. */ 02857 if (!(h5->cmode & NC_NOWRITE)) 02858 { 02859 int bad_coord_order = 0; /* if detected, propagate to all groups to consistently store dimids */ 02860 02861 if ((retval = nc4_rec_write_groups_types(h5->root_grp))) 02862 return retval; 02863 if ((retval = nc4_rec_detect_need_to_preserve_dimids(h5->root_grp, &bad_coord_order))) 02864 return retval; 02865 if ((retval = nc4_rec_write_metadata(h5->root_grp, bad_coord_order))) 02866 return retval; 02867 } 02868 02869 if (H5Fflush(h5->hdfid, H5F_SCOPE_GLOBAL) < 0) 02870 return NC_EHDFERR; 02871 02872 return retval; 02873 } 02874 02875 /* Flushes all buffers associated with the file, after writing all 02876 changed metadata. This may only be called in data mode. */ 02877 int 02878 NC4_sync(int ncid) 02879 { 02880 NC *nc; 02881 int retval; 02882 NC_HDF5_FILE_INFO_T* nc4_info; 02883 02884 LOG((2, "%s: ncid 0x%x", __func__, ncid)); 02885 02886 if (!(nc = nc4_find_nc_file(ncid,&nc4_info))) 02887 return NC_EBADID; 02888 assert(nc4_info); 02889 02890 #if 0 /*def USE_PNETCDF*/ 02891 /* Take care of files created/opened with parallel-netcdf library. */ 02892 if (nc4_info->pnetcdf_file) 02893 return ncmpi_sync(nc->int_ncid); 02894 #endif /* USE_PNETCDF */ 02895 02896 /* If we're in define mode, we can't sync. */ 02897 if (nc4_info && nc4_info->flags & NC_INDEF) 02898 { 02899 if (nc4_info->cmode & NC_CLASSIC_MODEL) 02900 return NC_EINDEFINE; 02901 if ((retval = NC4_enddef(ncid))) 02902 return retval; 02903 } 02904 02905 return sync_netcdf4_file(nc4_info); 02906 } 02907 02908 /* This function will free all allocated metadata memory, and close 02909 the HDF5 file. The group that is passed in must be the root group 02910 of the file. */ 02911 static int 02912 close_netcdf4_file(NC_HDF5_FILE_INFO_T *h5, int abort) 02913 { 02914 int retval = NC_NOERR; 02915 02916 assert(h5 && h5->root_grp); 02917 LOG((3, "%s: h5->path %s abort %d", __func__, h5->controller->path, abort)); 02918 02919 /* According to the docs, always end define mode on close. */ 02920 if (h5->flags & NC_INDEF) 02921 h5->flags ^= NC_INDEF; 02922 02923 /* Sync the file, unless we're aborting, or this is a read-only 02924 * file. */ 02925 if (!h5->no_write && !abort) 02926 if ((retval = sync_netcdf4_file(h5))) 02927 goto exit; 02928 02929 /* Delete all the list contents for vars, dims, and atts, in each 02930 * group. */ 02931 if ((retval = nc4_rec_grp_del(&h5->root_grp, h5->root_grp))) 02932 goto exit; 02933 02934 /* Close hdf file. */ 02935 #ifdef USE_HDF4 02936 if (h5->hdf4) 02937 { 02938 if (SDend(h5->sdid)) 02939 BAIL_QUIET(NC_EHDFERR); 02940 } 02941 else 02942 #endif /* USE_HDF4 */ 02943 { 02944 #ifdef USE_PARALLEL 02945 /* Free the MPI Comm & Info objects, if we opened the file in parallel */ 02946 if(h5->parallel) 02947 { 02948 MPI_Comm_free(&h5->comm); 02949 if(MPI_INFO_NULL != h5->info) 02950 MPI_Info_free(&h5->info); 02951 } 02952 #endif 02953 if (H5Fclose(h5->hdfid) < 0) 02954 { 02955 int nobjs; 02956 02957 nobjs = H5Fget_obj_count(h5->hdfid, H5F_OBJ_ALL); 02958 /* Apparently we can get an error even when nobjs == 0 */ 02959 if(nobjs < 0) { 02960 BAIL_QUIET(NC_EHDFERR); 02961 } else if(nobjs > 0) { 02962 #ifdef LOGGING 02963 /* If the close doesn't work, probably there are still some HDF5 02964 * objects open, which means there's a bug in the library. So 02965 * print out some info on to help the poor programmer figure it 02966 * out. */ 02967 LOG((0, "There are %d HDF5 objects open!", nobjs)); 02968 #endif 02969 BAIL_QUIET(NC_EHDFERR); 02970 } 02971 } 02972 } 02973 02974 exit: 02975 /* Free the nc4_info struct; above code should have reclaimed 02976 everything else */ 02977 if(h5 != NULL) 02978 free(h5); 02979 02980 return retval; 02981 } 02982 02983 /* From the netcdf-3 docs: The function nc_abort just closes the 02984 netCDF dataset, if not in define mode. If the dataset is being 02985 created and is still in define mode, the dataset is deleted. If 02986 define mode was entered by a call to nc_redef, the netCDF dataset 02987 is restored to its state before definition mode was entered and the 02988 dataset is closed. */ 02989 int 02990 NC4_abort(int ncid) 02991 { 02992 NC *nc; 02993 int delete_file = 0; 02994 char path[NC_MAX_NAME + 1]; 02995 int retval = NC_NOERR; 02996 NC_HDF5_FILE_INFO_T* nc4_info; 02997 02998 LOG((2, "%s: ncid 0x%x", __func__, ncid)); 02999 03000 /* Find metadata for this file. */ 03001 if (!(nc = nc4_find_nc_file(ncid,&nc4_info))) 03002 return NC_EBADID; 03003 03004 assert(nc4_info); 03005 03006 #if 0 /*def USE_PNETCDF*/ 03007 /* Take care of files created/opened with parallel-netcdf library. */ 03008 if (nc4_info->pnetcdf_file) 03009 return ncmpi_abort(nc->int_ncid); 03010 #endif /* USE_PNETCDF */ 03011 03012 /* If we're in define mode, but not redefing the file, delete it. */ 03013 if (nc4_info->flags & NC_INDEF && !nc4_info->redef) 03014 { 03015 delete_file++; 03016 strncpy(path, nc->path,NC_MAX_NAME); 03017 } 03018 03019 /* Free any resources the netcdf-4 library has for this file's 03020 * metadata. */ 03021 if ((retval = close_netcdf4_file(nc4_info, 1))) 03022 return retval; 03023 03024 /* Delete the file, if we should. */ 03025 if (delete_file) 03026 if (remove(path) < 0) 03027 return NC_ECANTREMOVE; 03028 03029 return retval; 03030 } 03031 03032 /* Close the netcdf file, writing any changes first. */ 03033 int 03034 NC4_close(int ncid) 03035 { 03036 NC_GRP_INFO_T *grp; 03037 NC *nc; 03038 NC_HDF5_FILE_INFO_T *h5; 03039 int retval; 03040 03041 LOG((1, "%s: ncid 0x%x", __func__, ncid)); 03042 03043 /* Find our metadata for this file. */ 03044 if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) 03045 return retval; 03046 03047 assert(nc && h5 && grp); 03048 03049 /* This must be the root group. */ 03050 if (grp->parent) 03051 return NC_EBADGRPID; 03052 03053 #if 0 /*def USE_PNETCDF*/ 03054 /* Take care of files created/opened with parallel-netcdf library. */ 03055 if (h5->pnetcdf_file) 03056 return ncmpi_close(nc->int_ncid); 03057 #endif /* USE_PNETCDF */ 03058 03059 /* Call the nc4 close. */ 03060 if ((retval = close_netcdf4_file(grp->nc4_info, 0))) 03061 return retval; 03062 03063 return NC_NOERR; 03064 } 03065 03066 /* It's possible for any of these pointers to be NULL, in which case 03067 don't try to figure out that value. */ 03068 int 03069 NC4_inq(int ncid, int *ndimsp, int *nvarsp, int *nattsp, int *unlimdimidp) 03070 { 03071 NC *nc; 03072 NC_HDF5_FILE_INFO_T *h5; 03073 NC_GRP_INFO_T *grp; 03074 NC_DIM_INFO_T *dim; 03075 NC_ATT_INFO_T *att; 03076 NC_VAR_INFO_T *var; 03077 int retval; 03078 03079 LOG((2, "%s: ncid 0x%x", __func__, ncid)); 03080 03081 /* Find file metadata. */ 03082 if ((retval = nc4_find_nc_grp_h5(ncid, &nc, &grp, &h5))) 03083 return retval; 03084 03085 assert(h5 && grp && nc); 03086 03087 #if 0 /*def USE_PNETCDF*/ 03088 /* Take care of files created/opened with parallel-netcdf library. */ 03089 if (h5->pnetcdf_file) 03090 return ncmpi_inq(nc->int_ncid, ndimsp, nvarsp, nattsp, unlimdimidp); 03091 #endif /* USE_PNETCDF */ 03092 03093 /* Count the number of dims, vars, and global atts. */ 03094 if (ndimsp) 03095 { 03096 *ndimsp = 0; 03097 for (dim = grp->dim; dim; dim = dim->l.next) 03098 (*ndimsp)++; 03099 } 03100 if (nvarsp) 03101 { 03102 *nvarsp = 0; 03103 for (var = grp->var; var; var= var->l.next) 03104 (*nvarsp)++; 03105 } 03106 if (nattsp) 03107 { 03108 *nattsp = 0; 03109 for (att = grp->att; att; att = att->l.next) 03110 (*nattsp)++; 03111 } 03112 03113 if (unlimdimidp) 03114 { 03115 /* Default, no unlimited dimension */ 03116 *unlimdimidp = -1; 03117 03118 /* If there's more than one unlimited dim, which was not possible 03119 with netcdf-3, then only the last unlimited one will be reported 03120 back in xtendimp. */ 03121 /* Note that this code is inconsistent with nc_inq_unlimid() */ 03122 for (dim = grp->dim; dim; dim = dim->l.next) 03123 if (dim->unlimited) 03124 { 03125 *unlimdimidp = dim->dimid; 03126 break; 03127 } 03128 } 03129 03130 return NC_NOERR; 03131 } 03132 03133 03134 /* This function will do the enddef stuff for a netcdf-4 file. */ 03135 int 03136 nc4_enddef_netcdf4_file(NC_HDF5_FILE_INFO_T *h5) 03137 { 03138 assert(h5); 03139 LOG((3, "%s", __func__)); 03140 03141 /* If we're not in define mode, return an error. */ 03142 if (!(h5->flags & NC_INDEF)) 03143 return NC_ENOTINDEFINE; 03144 03145 /* Turn define mode off. */ 03146 h5->flags ^= NC_INDEF; 03147 03148 /* Redef mode needs to be tracked seperately for nc_abort. */ 03149 h5->redef = NC_FALSE; 03150 03151 return sync_netcdf4_file(h5); 03152 } 03153 03154 #ifdef EXTRA_TESTS 03155 int 03156 nc_exit() 03157 { 03158 if (num_plists || num_spaces) 03159 return NC_EHDFERR; 03160 03161 return NC_NOERR; 03162 } 03163 #endif /* EXTRA_TESTS */ 03164 03165 #ifdef USE_PARALLEL 03166 int 03167 nc_use_parallel_enabled() 03168 { 03169 return 0; 03170 } 03171 #endif /* USE_PARALLEL */ 03172 03173