1 /**
2  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
3  * storing and accessing finite element mesh data.
4  *
5  * Copyright 2004 Sandia Corporation.  Under the terms of Contract
6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
7  * retains certain rights in this software.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  */
15 
16 #include <stdlib.h>
17 #include <string.h>
18 #include <assert.h>
19 #include <H5Fpublic.h>
20 #include <H5Ppublic.h>
21 #include <H5Gpublic.h>
22 #include <H5Spublic.h>
23 #include <H5Tpublic.h>
24 #include <H5Apublic.h>
25 #ifdef MOAB_HAVE_HDF5_PARALLEL
26 #  include <H5FDmpi.h>
27 #  include <H5FDmpio.h>
28 #endif
29 #include "mhdf.h"
30 #include "status.h"
31 #include "names-and-paths.h"
32 #include "util.h"
33 #include "file-handle.h"
34 
35 static int
36 make_hdf_group( const char* path, hid_t file, size_t size, mhdf_Status* status );
37 
38 mhdf_FileHandle
mhdf_createFile(const char * filename,int overwrite,const char ** elem_type_list,size_t elem_list_len,hid_t id_type,mhdf_Status * status)39 mhdf_createFile( const char* filename,
40                  int overwrite,
41                  const char** elem_type_list,
42                  size_t elem_list_len,
43                  hid_t id_type,
44                  mhdf_Status* status )
45 {
46   FileHandle* file_ptr;
47   unsigned int flags;
48   unsigned char idx;
49   size_t i;
50   hid_t enum_id, group_id;
51   int rval;
52   API_BEGIN;
53 
54   if (elem_list_len > 255)
55   {
56     mhdf_setFail( status, "Element type list too long." );
57     return NULL;
58   }
59   mhdf_setOkay( status );
60 
61     /* Create struct to hold working data */
62   file_ptr = mhdf_alloc_FileHandle( 0, id_type, status );
63   if (!file_ptr) return NULL;
64 
65     /* Create the file */
66   flags = overwrite ? H5F_ACC_TRUNC : H5F_ACC_EXCL;
67   file_ptr->hdf_handle = H5Fcreate( filename, flags, H5P_DEFAULT, H5P_DEFAULT );
68   if (file_ptr->hdf_handle < 0)
69   {
70     mhdf_setFail( status, "Failed to create file \"%s\"", filename );
71     free( file_ptr );
72     return NULL;
73   }
74 
75 
76     /* Create file structure */
77   if (!make_hdf_group(     ROOT_GROUP, file_ptr->hdf_handle, 6, status )
78    || !make_hdf_group(      TAG_GROUP, file_ptr->hdf_handle, 0, status )
79    || !make_hdf_group(  ELEMENT_GROUP, file_ptr->hdf_handle, 8, status )
80    || !make_hdf_group(     NODE_GROUP, file_ptr->hdf_handle, 3, status )
81    || !make_hdf_group(      SET_GROUP, file_ptr->hdf_handle, 5, status )
82    || !make_hdf_group( NODE_TAG_GROUP, file_ptr->hdf_handle, 0, status )
83    || !make_hdf_group(  SET_TAG_GROUP, file_ptr->hdf_handle, 0, status ))
84   {
85     H5Fclose( file_ptr->hdf_handle );
86     free( file_ptr );
87     return NULL;
88   }
89 
90     /* Store the max ID as an attribite on the /tstt/ group */
91 #if defined(H5Gopen_vers) && H5Gopen_vers > 1
92   group_id = H5Gopen2( file_ptr->hdf_handle, ROOT_GROUP, H5P_DEFAULT );
93 #else
94   group_id = H5Gopen( file_ptr->hdf_handle, ROOT_GROUP );
95 #endif
96   rval = mhdf_create_scalar_attrib( group_id,
97                                     MAX_ID_ATTRIB,
98                                     H5T_NATIVE_ULONG,
99                                     &file_ptr->max_id,
100                                     status );
101   H5Gclose( group_id );
102   if (!rval)
103   {
104     H5Fclose( file_ptr->hdf_handle );
105     free( file_ptr );
106     return NULL;
107   }
108 
109     /* Create the type name list in file */
110   enum_id = H5Tenum_create( H5T_NATIVE_UCHAR );
111   if (enum_id < 0)
112   {
113     mhdf_setFail( status, "Failed to store elem type list." );
114     H5Fclose( file_ptr->hdf_handle );
115     free( file_ptr );
116     return NULL;
117   }
118   for (i = 0; i < elem_list_len; ++i)
119   {
120     if (!elem_type_list[i] || !*elem_type_list[i])
121       continue;
122 
123     idx = (unsigned char)i;
124     if ( H5Tenum_insert( enum_id, elem_type_list[i], &idx ) < 0)
125     {
126       mhdf_setFail( status, "Failed to store elem type list." );
127       H5Fclose( file_ptr->hdf_handle );
128       free( file_ptr );
129       return NULL;
130     }
131   }
132 #if defined(H5Tcommit_vers) && H5Tcommit_vers > 1
133   if (H5Tcommit2( file_ptr->hdf_handle, TYPE_ENUM_PATH, enum_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT ) < 0)
134 #else
135   if (H5Tcommit( file_ptr->hdf_handle, TYPE_ENUM_PATH, enum_id ) < 0)
136 #endif
137   {
138     mhdf_setFail( status, "Failed to store elem type list." );
139     H5Fclose( file_ptr->hdf_handle );
140     free( file_ptr );
141     return NULL;
142   }
143   H5Tclose( enum_id );
144 
145   API_END_H( 1 );
146   return file_ptr;
147 }
148 
149 
150 mhdf_FileHandle
mhdf_openFile(const char * filename,int writeable,unsigned long * max_id_out,hid_t id_type,mhdf_Status * status)151 mhdf_openFile( const char* filename,
152                int writeable,
153                unsigned long* max_id_out,
154                hid_t id_type,
155                mhdf_Status* status )
156 {
157   return mhdf_openFileWithOpt( filename,
158                                writeable,
159                                max_id_out,
160                                id_type,
161                                H5P_DEFAULT,
162                                status );
163 }
164 
165 
mhdf_countOpenHandles(mhdf_FileHandle file_handle)166 int mhdf_countOpenHandles( mhdf_FileHandle file_handle )
167 {
168   return H5Fget_obj_count(((FileHandle*)file_handle)->hdf_handle,H5F_OBJ_ALL);
169 }
170 
get_max_id(hid_t group_id,const char * subgroup,const char * datatable,unsigned long * data)171 static herr_t get_max_id( hid_t group_id,
172                           const char* subgroup,
173                           const char* datatable,
174                           unsigned long* data )
175 {
176   unsigned long id;
177   hid_t elem_id, conn_id, attr_id, space_id;
178   herr_t rval;
179   int rank;
180   hsize_t dims[2];
181 
182 #if defined(H5Gopen_vers) && H5Gopen_vers > 1
183   elem_id = H5Gopen2( group_id, subgroup, H5P_DEFAULT );
184 #else
185   elem_id = H5Gopen( group_id, subgroup );
186 #endif
187   if (elem_id < 0) return (herr_t)-1;
188 
189 #if defined(H5Dopen_vers) && H5Dopen_vers > 1
190   conn_id = H5Dopen2( elem_id, datatable, H5P_DEFAULT );
191 #else
192   conn_id = H5Dopen( elem_id, datatable );
193 #endif
194   H5Gclose( elem_id );
195   if (conn_id < 0) return (herr_t)-1;
196 
197   space_id = H5Dget_space( conn_id );
198   if (space_id < 0) { H5Dclose( conn_id ); return -1; }
199 
200   rank = H5Sget_simple_extent_ndims( space_id );
201   if (rank <= 0 || rank > 2) { H5Dclose(conn_id); H5Sclose(space_id); return -1; }
202 
203   rval = H5Sget_simple_extent_dims( space_id, dims, NULL );
204   H5Sclose( space_id );
205   if (rval < 0) { H5Dclose( conn_id ); return -1; }
206 
207   attr_id = H5Aopen_name( conn_id, START_ID_ATTRIB );
208   H5Dclose( conn_id );
209   if (attr_id < 0) return (herr_t)-1;
210 
211   rval = H5Aread( attr_id, H5T_NATIVE_ULONG, &id );
212   H5Aclose( attr_id );
213   if (rval < 0) return rval;
214 
215   id += dims[0];
216   if (id > *data)
217     *data = id;
218   return 0;
219 }
220 
max_id_iter(hid_t group_id,const char * name,void * data)221 static herr_t max_id_iter( hid_t group_id, const char* name, void* data )
222 {
223   return get_max_id( group_id, name, CONNECTIVITY_NAME, (unsigned long*)data );
224 }
225 
226 static int
scan_for_max_id(FileHandle * file_ptr,mhdf_Status * status)227 scan_for_max_id( FileHandle* file_ptr, mhdf_Status* status )
228 {
229   hid_t group_id;
230   herr_t rval;
231 
232     /* Check for new format, with max_id as attrib of root group */
233 #if defined(H5Gopen_vers) && H5Gopen_vers > 1
234   group_id = H5Gopen2( file_ptr->hdf_handle, ROOT_GROUP, H5P_DEFAULT );
235 #else
236   group_id = H5Gopen( file_ptr->hdf_handle, ROOT_GROUP );
237 #endif
238   if (group_id < 0)
239   {
240     mhdf_setFail( status, "Internal error - invalid file.");
241     return 0;
242   }
243   if (mhdf_read_scalar_attrib( group_id, MAX_ID_ATTRIB,
244                                H5T_NATIVE_ULONG, &file_ptr->max_id,
245                                status ))
246   {
247     H5Gclose( group_id );
248     return 1;
249   }
250 
251     /* Didn't find it, scan the elements group */
252   rval = H5Giterate( group_id, ELEMENT_GROUP_NAME, 0, &max_id_iter, &file_ptr->max_id );
253   if (rval)
254   {
255     H5Gclose( group_id );
256     mhdf_setFail( status, "Internal error -- invalid file." );
257     return 0;
258   }
259 
260     /* Check node table too */
261   rval = get_max_id( group_id, NODE_GROUP_NAME, "coordinates", (unsigned long*)(&file_ptr->max_id) );
262   if (rval)
263   {
264     H5Gclose( group_id );
265     mhdf_setFail( status, "Internal error -- invalid file." );
266     return 0;
267   }
268 
269     /* Check set table, if it exists */
270   rval = mhdf_is_in_group( group_id, SET_GROUP_NAME, status );
271   if (rval < 1)
272   {
273     H5Gclose( group_id );
274     return !rval;
275   }
276   rval = get_max_id( group_id, SET_GROUP_NAME, SET_META_NAME, (unsigned long*)(&file_ptr->max_id) );
277   H5Gclose( group_id );
278   if (rval)
279   {
280     mhdf_setFail( status, "Internal error -- invalid file." );
281     return 0;
282   }
283 
284   return 1;
285 }
286 
287 
288 
289 
290 
291 mhdf_FileHandle
mhdf_openFileWithOpt(const char * filename,int writable,unsigned long * max_id_out,hid_t id_type,hid_t access_prop,mhdf_Status * status)292 mhdf_openFileWithOpt( const char* filename,
293                       int writable,
294                       unsigned long* max_id_out,
295                       hid_t id_type,
296                       hid_t access_prop,
297                       mhdf_Status* status )
298 {
299   FileHandle* file_ptr;
300   unsigned int flags;
301   hid_t group_id;
302   int check_is_hdf5 = 1;
303 #ifdef MOAB_HAVE_HDF5_PARALLEL
304   herr_t err;
305   MPI_Comm comm;
306   MPI_Info info;
307 #endif
308   API_BEGIN;
309 
310     /* Check if file is HDF5 */
311   /* Don't do this because it can't handle MPI-IO driver code that
312      passes options via prefixes on the file name. */
313 #ifdef MOAB_HAVE_HDF5_PARALLEL
314   if (access_prop != H5P_DEFAULT) {
315     err = H5Pget_fapl_mpio( access_prop, &comm, &info );
316     if (err >= 0) {
317       check_is_hdf5 = 0;
318       /* MPI Documentation is inconsistent with regards to whether
319          or not the above call dup's these, but my testing with 1.8.3
320          indicates that at least for that version they are not.
321       MPI_Comm_free(&comm);
322       MPI_Info_free(&info); */
323     }
324   }
325 #endif
326   if (check_is_hdf5 && H5Fis_hdf5( filename ) <= 0) {
327     mhdf_setFail( status, "%s: File is not HDF5", filename );
328     return NULL;
329   }
330 
331     /* Create struct to hold working data */
332   file_ptr = mhdf_alloc_FileHandle( 0, id_type, status );
333   if (!file_ptr) {
334     mhdf_setFail( status, "Memory allocation failed" );
335     return NULL;
336   }
337 
338     /* Create the file */
339   flags = writable ? H5F_ACC_RDWR : H5F_ACC_RDONLY;
340   file_ptr->hdf_handle = H5Fopen( filename, flags, access_prop );
341   if (file_ptr->hdf_handle < 0)
342   {
343     mhdf_setFail( status, "Failed to open file \"%s\"", filename );
344     free( file_ptr );
345     return NULL;
346   }
347 
348     /* Check for TSTT data in file */
349 #if defined(H5Gopen_vers) && H5Gopen_vers > 1
350   group_id = H5Gopen2( file_ptr->hdf_handle, ROOT_GROUP, H5P_DEFAULT );
351 #else
352   group_id = H5Gopen( file_ptr->hdf_handle, ROOT_GROUP );
353 #endif
354   if (group_id < 0)
355   {
356     mhdf_setFail( status, "Invalid file \"%s\"\n", filename );
357     H5Fclose( file_ptr->hdf_handle );
358     free( file_ptr );
359     return NULL;
360   }
361   H5Gclose( group_id );
362 
363     /* Get max id */
364   if (!scan_for_max_id( file_ptr, status ))
365   {
366     H5Fclose( file_ptr->hdf_handle );
367     mhdf_setFail( status, "Internal error reading file" );
368     free( file_ptr );
369     return NULL;
370   }
371 
372   if (max_id_out)
373     *max_id_out = file_ptr->max_id;
374 
375   mhdf_setOkay( status );
376   API_END_H(1);
377   return file_ptr;
378 }
379 
380 
381 void
mhdf_getElemName(mhdf_FileHandle file_handle,unsigned int type_index,char * buffer,size_t buf_size,mhdf_Status * status)382 mhdf_getElemName( mhdf_FileHandle file_handle,
383                   unsigned int type_index,
384                   char* buffer,
385                   size_t buf_size,
386                   mhdf_Status* status )
387 {
388   FileHandle* file_ptr;
389   herr_t rval;
390   hid_t enum_id;
391   API_BEGIN;
392 
393   if (type_index > 255)
394   {
395     mhdf_setFail( status, "Type index out of bounds." );
396     return;
397   }
398 
399   file_ptr = (FileHandle*)(file_handle);
400   if (!mhdf_check_valid_file( file_ptr, status ))
401     return;
402 
403   enum_id = get_elem_type_enum( file_ptr, status );
404   if (enum_id < 0)
405     return;
406 
407   rval = H5Tconvert( H5T_NATIVE_UINT, H5Tget_super(enum_id), 1, &type_index, NULL, H5P_DEFAULT );
408   if (rval < 0)
409   {
410     H5Tclose( enum_id );
411     mhdf_setFail( status, "Internal error converting to enum type." );
412     return;
413   }
414 
415   rval = H5Tenum_nameof( enum_id, &type_index, buffer, buf_size );
416   H5Tclose( enum_id );
417   if (rval < 0)
418     mhdf_setFail( status, "H5Tenum_nameof failed.  Invalid type index?" );
419   else
420     mhdf_setOkay( status );
421 
422   API_END;
423 }
424 
425 int
mhdf_checkOpenHandles(mhdf_FileHandle handle,mhdf_Status * status)426 mhdf_checkOpenHandles( mhdf_FileHandle handle,
427                        mhdf_Status* status )
428 {
429   FileHandle* file_ptr;
430   int result;
431   API_BEGIN;
432 
433   file_ptr = (FileHandle*)(handle);
434   if (!mhdf_check_valid_file( file_ptr, status ))
435     return -1;
436 
437   /* Check for open handles.  HDF5 will not actually close the
438      file until all handles are closed. */
439   result = H5Fget_obj_count( file_ptr->hdf_handle, H5F_OBJ_ALL );
440   if (result != 1)
441   {
442     mhdf_setFail( status, "Cannot close file with open handles: "
443                  "%d file, %d data, %d group, %d type, %d attr\n",
444                   H5Fget_obj_count( file_ptr->hdf_handle, H5F_OBJ_FILE ) - 1,
445                   H5Fget_obj_count( file_ptr->hdf_handle, H5F_OBJ_DATASET ),
446                   H5Fget_obj_count( file_ptr->hdf_handle, H5F_OBJ_GROUP ),
447                   H5Fget_obj_count( file_ptr->hdf_handle, H5F_OBJ_DATATYPE ),
448                   H5Fget_obj_count( file_ptr->hdf_handle, H5F_OBJ_ATTR ) );
449     return result - 1;
450   }
451 
452   API_END_H( 0 );
453   return 0;
454 }
455 
456 void
mhdf_closeFile(mhdf_FileHandle handle,mhdf_Status * status)457 mhdf_closeFile( mhdf_FileHandle handle,
458                 mhdf_Status* status )
459 {
460   FileHandle* file_ptr;
461   API_BEGIN;
462 
463   file_ptr = (FileHandle*)(handle);
464   if (!mhdf_check_valid_file( file_ptr, status ))
465     return;
466 /*
467   if (file_ptr->open_handle_count)
468   {
469     mhdf_setError( status, "Cannot close file with %d open data handles.",
470       file_ptr->open_handle_count );
471     return;
472   }
473 */
474 
475   /* Check for open handles.  HDF5 will not actually close the
476      file until all handles are closed. */
477   if (mhdf_checkOpenHandles( handle, status ))
478     return;
479 
480   if (0 > H5Fclose( file_ptr->hdf_handle ))
481   {
482     mhdf_setFail( status, "H5FClose failed.  Invalid handle?" );
483     return;
484   }
485 
486   memset( file_ptr, 0, sizeof(FileHandle) );
487   free( file_ptr );
488   mhdf_setOkay( status );
489   API_END_H( -1 );
490 }
491 
492 void
mhdf_closeData(mhdf_FileHandle file,hid_t handle,mhdf_Status * status)493 mhdf_closeData( mhdf_FileHandle file, hid_t handle, mhdf_Status* status )
494 {
495   FileHandle* file_ptr;
496   herr_t rval = -1;
497 
498   file_ptr = (FileHandle*)(file);
499   if (!mhdf_check_valid_file( file_ptr, status ))
500     return;
501 
502   switch ( H5Iget_type( handle ) )
503   {
504     case H5I_GROUP    :  rval = H5Gclose( handle );  break;
505     case H5I_DATATYPE :  rval = H5Tclose( handle );  break;
506     case H5I_DATASPACE:  rval = H5Sclose( handle );  break;
507     case H5I_DATASET  :  rval = H5Dclose( handle );  break;
508     default           :  rval = -1;
509   }
510 
511   if (rval < 0)
512   {
513     mhdf_setFail( status, "H5Xclose failed.  Invalid handle?\n");
514   }
515   else
516   {
517     file_ptr->open_handle_count--;
518     mhdf_setOkay( status );
519   }
520 }
521 
522 
523 void
mhdf_addElement(mhdf_FileHandle file_handle,const char * name,unsigned int elem_type,mhdf_Status * status)524 mhdf_addElement( mhdf_FileHandle file_handle,
525                  const char* name,
526                  unsigned int elem_type,
527                  mhdf_Status* status )
528 {
529   FileHandle* file_ptr = (FileHandle*)file_handle;
530   hid_t group_id, tag_id, enum_id;
531   char* path, *ptr;
532   size_t name_len;
533   herr_t rval;
534   API_BEGIN;
535 
536   if (!mhdf_check_valid_file( file_ptr, status ))
537     return;
538 
539   name_len = mhdf_name_to_path( name, NULL, 0 );
540   name_len += strlen(ELEMENT_GROUP) + 1;
541   path = (char*)mhdf_malloc( name_len, status );
542   if (!path)
543     return;
544 
545   strcpy( path, ELEMENT_GROUP );
546   ptr = path + strlen(ELEMENT_GROUP);
547   if (!mhdf_path_to_name( name, ptr ))
548   {
549     mhdf_setFail( status, "Invalid character string in internal file path: \"%s\"\n",
550       name );
551     return;
552   }
553 
554 #if defined(H5Gcreate_vers) && H5Gcreate_vers > 1
555   group_id = H5Gcreate2( file_ptr->hdf_handle, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
556 #else
557   group_id = H5Gcreate( file_ptr->hdf_handle, path, 3 );
558 #endif
559   if (group_id < 0)
560   {
561     mhdf_setFail( status, "Creation of \"%s\" group failed.\n", path );
562     free( path );
563     return;
564   }
565   free( path );
566 
567 #if defined(H5Gcreate_vers) && H5Gcreate_vers > 1
568   tag_id = H5Gcreate2( group_id, DENSE_TAG_SUBGROUP, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
569 #else
570   tag_id = H5Gcreate( group_id, DENSE_TAG_SUBGROUP, 0 );
571 #endif
572   if (tag_id < 0)
573   {
574     H5Gclose( group_id );
575     mhdf_setFail( status, "Creation of tag subgroup failed.\n" );
576     return;
577   }
578   H5Gclose( tag_id );
579 
580   enum_id = get_elem_type_enum( file_ptr, status );
581   if (enum_id < 0)
582   {
583     H5Gclose( group_id );
584     return;
585   }
586 
587   rval = H5Tconvert( H5T_NATIVE_UINT, H5Tget_super(enum_id), 1, &elem_type, NULL, H5P_DEFAULT );
588   if (rval < 0)
589   {
590     H5Gclose( group_id );
591     H5Tclose( enum_id );
592     mhdf_setFail( status, "Internal error converting to enum type." );
593     return;
594   }
595 
596   rval = mhdf_create_scalar_attrib( group_id, ELEM_TYPE_ATTRIB, enum_id,
597                                  &elem_type, status );
598   H5Tclose( enum_id );
599   if (rval < 0)
600   {
601     H5Gclose( group_id );
602     return;
603   }
604 
605   H5Gclose( group_id );
606   mhdf_setOkay( status );
607   API_END;
608 }
609 
610 
611 char**
mhdf_getElemHandles(mhdf_FileHandle file_handle,unsigned int * count_out,mhdf_Status * status)612 mhdf_getElemHandles( mhdf_FileHandle file_handle,
613                      unsigned int* count_out,
614                      mhdf_Status* status )
615 {
616   hsize_t count, length, i;
617   char** buffer;
618   char* current;
619   hid_t group_id;
620   herr_t rval;
621   ssize_t rlen = 0;
622   size_t remaining;
623   FileHandle* file_ptr = (FileHandle*)file_handle;
624   if (!mhdf_check_valid_file( file_ptr, status ))
625     return NULL;
626 
627 #if defined(H5Gopen_vers) && H5Gopen_vers > 1
628   group_id = H5Gopen2( file_ptr->hdf_handle, ELEMENT_GROUP, H5P_DEFAULT );
629 #else
630   group_id = H5Gopen( file_ptr->hdf_handle, ELEMENT_GROUP );
631 #endif
632   if (group_id < 0)
633   {
634     mhdf_setFail( status, "Invalid file -- element group does not exist." );
635     return NULL;
636   }
637 
638   rval = H5Gget_num_objs( group_id, &count );
639   if (rval < 0)
640   {
641     H5Gclose( group_id );
642     mhdf_setFail( status, "Internal error calling H5Gget_num_objs." );
643     return NULL;
644   }
645   *count_out = count;
646 
647   for (i = 0; i < count; ++i)
648   {
649     rlen += H5Gget_objname_by_idx( group_id, i, NULL, 0 ) + 1;
650   }
651 
652   length = count * sizeof(char*) + rlen;
653   buffer = (char**)mhdf_malloc( length, status );
654   if (!buffer) { H5Gclose( group_id ); return NULL; }
655   current = (char*)(buffer + count);
656   remaining = rlen;
657 
658   for (i = 0; i < count; ++i)
659   {
660     buffer[i] = current;
661     rlen = H5Gget_objname_by_idx( group_id, i, current, remaining ) + 1;
662     if (rlen < 0)
663     {
664       H5Gclose( group_id );
665       free( buffer );
666       mhdf_setFail( status, "Internal error calling H5Gget_objname_by_idx." );
667       return NULL;
668     }
669 
670     mhdf_path_to_name( current, current );
671     remaining -= rlen;
672     current += rlen;
673   }
674 
675   H5Gclose( group_id );
676   mhdf_setOkay( status );
677   return buffer;
678 }
679 
680 
681 void
mhdf_getElemTypeName(mhdf_FileHandle file_handle,const char * elem_handle,char * buffer,size_t buf_len,mhdf_Status * status)682 mhdf_getElemTypeName( mhdf_FileHandle file_handle,
683                       const char* elem_handle,
684                       char* buffer, size_t buf_len,
685                       mhdf_Status* status )
686 {
687   FileHandle* file_ptr;
688   hid_t elem_id, type_id, attr_id;
689   char bytes[16];
690   herr_t rval;
691   API_BEGIN;
692 
693   if (NULL == buffer || buf_len < 2)
694   {
695     mhdf_setFail( status, "invalid input" );
696     return;
697   }
698   buffer[0] = '\0';
699 
700   file_ptr = (FileHandle*)(file_handle);
701   if (!mhdf_check_valid_file( file_ptr, status ))
702     return;
703 
704   elem_id = mhdf_elem_group_from_handle( file_ptr, elem_handle, status );
705   if (elem_id < 0) return;
706 
707   attr_id = H5Aopen_name( elem_id, ELEM_TYPE_ATTRIB );
708   H5Gclose( elem_id );
709   if (attr_id < 0)
710   {
711     mhdf_setFail( status, "Missing element type attribute.  Invalid file." );
712     return;
713   }
714 
715   type_id = H5Aget_type( attr_id );
716   assert( type_id > 0 );
717 
718   rval = H5Aread( attr_id, type_id, bytes );
719   H5Aclose( attr_id );
720   if (rval < 0)
721   {
722     H5Tclose( type_id );
723     mhdf_setFail( status, "Failed to read element type attribute.  Invalid file." );
724     return;
725   }
726 
727   rval = H5Tenum_nameof( type_id, bytes, buffer, buf_len );
728   H5Tclose( type_id );
729   if (rval < 0)
730   {
731     mhdf_setFail( status, "Invalid datatype for element type attribute.  Invalid file." );
732     return;
733   }
734 
735   mhdf_setOkay( status );
736   API_END;
737   return ;
738 }
739 
740 
741 static int
make_hdf_group(const char * path,hid_t file,size_t sz,mhdf_Status * status)742 make_hdf_group( const char* path, hid_t file, size_t sz, mhdf_Status* status )
743 {
744 #if defined(H5Gcreate_vers) && H5Gcreate_vers > 1
745   hid_t handle = H5Gcreate2( file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
746     /* empty statement to avoid compiler warning */
747   if (sz) {}
748 #else
749   hid_t handle = H5Gcreate( file, path, sz );
750 #endif
751   if (handle < 0)
752   {
753     mhdf_setFail( status, "Failed to create \"%s\" group.", path );
754     return 0;
755   }
756   else
757   {
758     H5Gclose( handle );
759     return 1;
760   }
761 }
762 
763 const char*
mhdf_node_type_handle(void)764 mhdf_node_type_handle(void)
765 {
766   static const char rval[] = "nodes";
767   return rval;
768 }
769 
770 const char*
mhdf_set_type_handle(void)771 mhdf_set_type_handle(void)
772 {
773   static const char rval[] = "sets";
774   return rval;
775 }
776 
777 int
mhdf_isPolyElement(mhdf_FileHandle file_handle,const char * elem_handle,mhdf_Status * status)778 mhdf_isPolyElement( mhdf_FileHandle file_handle,
779                     const char* elem_handle,
780                     mhdf_Status* status )
781 {
782   FileHandle* file_ptr;
783   hid_t elem_id;
784   int rval;
785   API_BEGIN;
786 
787   file_ptr = (FileHandle*)(file_handle);
788   if (!mhdf_check_valid_file( file_ptr, status ))
789     return -1;
790 
791   elem_id = mhdf_elem_group_from_handle( file_ptr, elem_handle, status );
792   if (elem_id < 0) return -1;
793 
794   mhdf_setOkay( status );
795   rval = mhdf_is_in_group( elem_id, POLY_INDEX_NAME, status );
796   H5Gclose( elem_id );
797   API_END;
798   return rval;
799 }
800 
801 void
mhdf_writeHistory(mhdf_FileHandle file_handle,const char ** strings,int num_strings,mhdf_Status * status)802 mhdf_writeHistory( mhdf_FileHandle file_handle,
803                    const char** strings,
804                    int num_strings,
805                    mhdf_Status* status )
806 {
807   FileHandle* file_ptr;
808   hid_t data_id, type_id, space_id;
809   hsize_t dim = (hsize_t)num_strings;
810   herr_t rval;
811   API_BEGIN;
812 
813   file_ptr = (FileHandle*)(file_handle);
814   if (!mhdf_check_valid_file( file_ptr, status ))
815     return;
816 
817   type_id = H5Tcopy( H5T_C_S1 );
818   if (type_id < 0 || H5Tset_size( type_id, H5T_VARIABLE ) < 0)
819   {
820     if (type_id >= 0) H5Tclose(type_id);
821     mhdf_setFail( status, "Could not create variable length string type." );
822     return;
823   }
824 
825   space_id = H5Screate_simple( 1, &dim, NULL );
826   if (space_id < 0)
827   {
828     H5Tclose( type_id );
829     mhdf_setFail( status, "H5Screate_simple failed." );
830     return;
831   }
832 
833 #if defined(H5Dcreate_vers) && H5Dcreate_vers > 1
834   data_id = H5Dcreate2( file_ptr->hdf_handle, HISTORY_PATH, type_id, space_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT );
835 #else
836   data_id = H5Dcreate( file_ptr->hdf_handle, HISTORY_PATH, type_id, space_id, H5P_DEFAULT );
837 #endif
838   H5Sclose( space_id );
839   if (data_id < 0)
840   {
841     H5Tclose( type_id );
842     mhdf_setFail( status, "Failed to create \"%s\".", HISTORY_PATH );
843     return;
844   }
845 
846   rval = H5Dwrite( data_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, strings );
847   H5Dclose( data_id );
848   H5Tclose( type_id );
849   if (rval < 0)
850   {
851     H5Gunlink( file_ptr->hdf_handle, HISTORY_PATH );
852     mhdf_setFail( status, "Failed to write history data." );
853     return;
854   }
855 
856   mhdf_setOkay( status );
857   API_END;
858 }
859 
860 char**
mhdf_readHistory(mhdf_FileHandle file_handle,int * num_strings,mhdf_Status * status)861 mhdf_readHistory( mhdf_FileHandle file_handle,
862                   int* num_strings,
863                   mhdf_Status* status )
864 {
865   FileHandle* file_ptr;
866   hid_t data_id, type_id, space_id, group_id;
867   hsize_t dim;
868   herr_t rval;
869   char** array;
870   API_BEGIN;
871 
872   file_ptr = (FileHandle*)(file_handle);
873   if (!mhdf_check_valid_file( file_ptr, status ))
874     return NULL;
875 
876     /* check if file contains history data */
877 #if defined(H5Gopen_vers) && H5Gopen_vers > 1
878   group_id = H5Gopen2( file_ptr->hdf_handle, ROOT_GROUP, H5P_DEFAULT );
879 #else
880   group_id = H5Gopen( file_ptr->hdf_handle, ROOT_GROUP );
881 #endif
882   if (group_id < 0)
883   {
884     mhdf_setFail( status, "Could not open root group.  Invalid file." );
885     return NULL;
886   }
887 
888   rval = mhdf_is_in_group( group_id, HISTORY_NAME, status );
889   if (rval < 1)
890   {
891     H5Gclose( group_id );
892     *num_strings = 0;
893     if (0 == rval)
894       mhdf_setOkay( status );
895     return NULL;
896   }
897 
898 #if defined(H5Dopen_vers) && H5Dopen_vers > 1
899   data_id = H5Dopen2( group_id, HISTORY_NAME, H5P_DEFAULT );
900 #else
901   data_id = H5Dopen( group_id, HISTORY_NAME );
902 #endif
903   H5Gclose( group_id );
904   if (data_id < 0)
905   {
906     mhdf_setFail( status, "Failed to open \"%s\".", HISTORY_PATH );
907     return NULL;
908   }
909 
910   space_id = H5Dget_space( data_id );
911   if (space_id < 0)
912   {
913     H5Dclose( data_id );
914     mhdf_setFail( status, "Internal error calling H5Dget_space.");
915     return NULL;
916   }
917 
918   if (1 != H5Sget_simple_extent_ndims( space_id ) ||
919       1 != H5Sget_simple_extent_dims( space_id, &dim, NULL ))
920   {
921     H5Dclose( data_id );
922     mhdf_setFail( status, "Invalid dimension for \"%s\".", HISTORY_PATH );
923     return NULL;
924   }
925   H5Sclose( space_id );
926 
927   if (0 == dim)
928   {
929     H5Dclose( data_id );
930     *num_strings = 0;
931     mhdf_setOkay( status );
932     return NULL;
933   }
934 
935   array = (char**)mhdf_malloc( dim * sizeof(char*), status );
936   if (!array)
937   {
938     H5Dclose( data_id );
939     return NULL;
940   }
941 
942   type_id = H5Tcopy( H5T_C_S1 );
943   if (type_id < 0 || H5Tset_size( type_id, H5T_VARIABLE ) < 0)
944   {
945     H5Dclose( data_id );
946     if (type_id >= 0) H5Tclose(type_id);
947     mhdf_setFail( status, "Could not create variable length string type." );
948     free( array );
949     return NULL;
950   }
951 
952   rval = H5Dread( data_id, type_id, H5S_ALL, H5S_ALL, H5P_DEFAULT, array );
953   H5Tclose( type_id );
954   H5Dclose( data_id );
955   if (rval < 0)
956   {
957     free( array );
958     mhdf_setFail( status, "H5Dread failed." );
959     return NULL;
960   }
961 
962   *num_strings = dim;
963   mhdf_setOkay( status );
964   API_END;
965   return array;
966 }
967 
968 void
mhdf_getNextStartId(mhdf_FileHandle file,mhdf_index_t * start_id_out,mhdf_Status * status)969 mhdf_getNextStartId( mhdf_FileHandle file,
970                      mhdf_index_t* start_id_out,
971                      mhdf_Status* status )
972 {
973   FileHandle* file_ptr = (FileHandle*)file;
974   API_BEGIN;
975 
976   mhdf_setOkay( status );
977   if (mhdf_check_valid_file( file_ptr, status ))
978     *start_id_out = file_ptr->max_id+1;
979 
980   API_END;
981 }
982