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