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 <ctype.h>
20 #include <hdf5.h>
21 #include "util.h"
22 #include "status.h"
23 #include "names-and-paths.h"
24 
25 #ifdef VALGRIND
26 #  include <valgrind/memcheck.h>
27 #else
28 #  define VALGRIND_CHECK_MEM_IS_DEFINED(A,B)
29 #  define VALGRIND_MAKE_MEM_UNDEFINED(A,B)
30 #endif
31 
mhdf_malloc(size_t size,mhdf_Status * status)32 void* mhdf_malloc( size_t size, mhdf_Status* status )
33 {
34   void* result;
35   result = malloc(size);
36   if (!result)
37     mhdf_setFail( status, "Allocation of %d bytes failed.\n", (int)size );
38   return result;
39 }
40 
mhdf_realloc(void * ptr,size_t size,mhdf_Status * status)41 void* mhdf_realloc( void* ptr, size_t size, mhdf_Status* status )
42 {
43   void* result;
44   result = realloc(ptr, size);
45   if (!result)
46     mhdf_setFail( status, "Allocation of %d bytes failed.\n", (int)size );
47   return result;
48 }
49 
mhdf_name_to_path(const char * name,char * path,size_t path_len)50 size_t mhdf_name_to_path( const char* name, char* path, size_t path_len )
51 {
52   size_t length = 1;
53   unsigned char *iter;
54 
55   if (0==strcmp(name, "."))
56   {
57     if (path_len >= 4)
58       sprintf(path, "\\%02X", (int)*name);
59     return 4;
60   }
61 
62   for (iter = (unsigned char*)name; *iter; ++iter)
63   {
64     if ( iscntrl(*iter) || *iter == '/' || *iter == '\\' || *iter > 127 )
65       length += 3;
66     else
67       length += 1;
68   }
69   if (path_len < length)
70     return length;
71 
72 
73   for (iter = (unsigned char*)name; *iter; ++iter)
74   {
75     if (iscntrl(*iter) || *iter == '/' || *iter == '\\' || *iter > 127)
76     {
77       sprintf(path, "\\%02X", (int)(*iter));
78       path += 3;
79     }
80     else
81     {
82       *(path++) = *iter;
83     }
84   }
85 
86   *path = '\0';
87   return length;
88 }
89 
mhdf_hex_char(int c)90 static int mhdf_hex_char( int c )
91 {
92   if (isdigit(c))
93     return c - '0';
94   else
95     return toupper(c) - ('A' - 10);
96 }
97 
mhdf_path_to_name(const char * path,char * name)98 int mhdf_path_to_name( const char* path, char* name )
99 {
100   const char* iter;
101   char c1, c2;
102 
103   for (iter = path; *iter; ++iter, ++name)
104   {
105     if (*iter == '\\')
106     {
107       c1 = *++iter;
108       c2 = *++iter;
109       if (!isxdigit(c1) || !isxdigit(c2))
110         return 0;
111 
112       *name = (char)(16 * mhdf_hex_char(c1) + mhdf_hex_char(c2));
113     }
114     else
115     {
116       *name = *iter;
117     }
118   }
119 
120   *name = '\0';
121   return 1;
122 }
123 
mhdf_name_to_path_copy(const char * name,mhdf_Status * status)124 char* mhdf_name_to_path_copy( const char* name, mhdf_Status* status )
125 {
126   size_t size;
127   char* buffer;
128 
129   size = mhdf_name_to_path( name, NULL, 0 );
130   buffer = (char*)mhdf_malloc( size, status );
131   if (!buffer) return NULL;
132 
133   mhdf_name_to_path( name, buffer, size );
134   return buffer;
135 }
136 
137 
mhdf_name_to_path_cat(const char * prefix,const char * name,mhdf_Status * status)138 char* mhdf_name_to_path_cat( const char* prefix, const char* name, mhdf_Status* status )
139 {
140   size_t size, plen;
141   char* buffer;
142 
143   plen = strlen( prefix );
144   size = mhdf_name_to_path( name, NULL, 0 ) + 1;
145   buffer = (char*)mhdf_malloc( size + plen, status );
146   if (!buffer) return NULL;
147 
148   memcpy( buffer, prefix, plen );
149   mhdf_name_to_path( name, buffer + plen, size );
150   return buffer;
151 }
152 
153 
mhdf_elem_group_from_handle(FileHandle * file_ptr,const char * elem_handle,mhdf_Status * status)154 hid_t mhdf_elem_group_from_handle( FileHandle* file_ptr,
155                                    const char* elem_handle,
156                                    mhdf_Status* status )
157 {
158   char* path;
159   hid_t result;
160 
161   path = mhdf_name_to_path_cat( ELEMENT_GROUP, elem_handle, status );
162   if (NULL == path)
163     return -1;
164 
165 #if defined(H5Gopen_vers) && H5Gopen_vers > 1
166   result = H5Gopen2( file_ptr->hdf_handle, path, H5P_DEFAULT );
167 #else
168   result = H5Gopen( file_ptr->hdf_handle, path );
169 #endif
170   free( path );
171   if (result < 0)
172     mhdf_setFail( status, "Failed to open element group: \"%s\"", elem_handle );
173   return result;
174 }
175 
mhdf_create_scalar_attrib(hid_t object,const char * name,hid_t type,const void * value,mhdf_Status * status)176 int mhdf_create_scalar_attrib( hid_t object,
177                               const char* name,
178                               hid_t type,
179                               const void* value,
180                               mhdf_Status* status )
181 {
182   hid_t dspace_id, attr_id;
183   herr_t rval;
184 
185   dspace_id = H5Screate( H5S_SCALAR );
186   if (dspace_id < 0)
187   {
188     mhdf_setFail( status, "Internal error calling H5Screate_simple." );
189     return 0;
190   }
191 
192 #if defined(H5Acreate_vers) && H5Acreate_vers > 1
193   attr_id = H5Acreate2( object, name, type, dspace_id, H5P_DEFAULT, H5P_DEFAULT );
194 #else
195   attr_id = H5Acreate( object, name, type, dspace_id, H5P_DEFAULT );
196 #endif
197   H5Sclose( dspace_id );
198   if (attr_id < 0)
199   {
200     mhdf_setFail( status, "Failed to create \"%s\" attrib.", name );
201     return 0;
202   }
203 
204   VALGRIND_CHECK_MEM_IS_DEFINED( value, H5Tget_size(type) );
205   rval = H5Awrite( attr_id, type, value );
206   H5Aclose( attr_id );
207   if (rval < 0)
208   {
209     mhdf_setFail( status, "Failed to write \"%s\" attrib.", name );
210     return 0;
211   }
212 
213   return 1;
214 }
215 
216 
mhdf_read_scalar_attrib(hid_t object,const char * name,hid_t type,void * value,mhdf_Status * status)217 int mhdf_read_scalar_attrib( hid_t object,
218                              const char* name,
219                              hid_t type,
220                              void* value,
221                              mhdf_Status* status )
222 {
223   hid_t attr_id, type_id;
224   herr_t rval;
225 
226   attr_id = H5Aopen_name( object, name );
227   if (attr_id < 0)
228   {
229     mhdf_setFail( status, "Failed to create \"%s\" attrib.", name );
230     return 0;
231   }
232 
233   if (type > 0)
234   {
235     type_id = type;
236   }
237   else
238   {
239     type_id = H5Aget_type( attr_id );
240     if (type_id < 0)
241     {
242       H5Aclose( attr_id );
243       return 0;
244     }
245   }
246 
247   rval = H5Aread( attr_id, type_id, value );
248   H5Aclose( attr_id );
249   if (type < 1)
250     H5Tclose( type_id );
251   if (rval < 0)
252   {
253     mhdf_setFail( status, "Failed to read \"%s\" attrib.", name );
254     return 0;
255   }
256 
257   return 1;
258 }
259 
260 #if defined(H5Aiterate_vers) && H5Aiterate_vers > 1
find_attr_by_name(hid_t handle,const char * name,const H5A_info_t * info,void * mydata)261 static herr_t find_attr_by_name( hid_t handle, const char* name, const H5A_info_t* info, void* mydata ) {
262     /* empty statement to remove compiler warning */
263   if (info) {}
264 #else
265 static herr_t find_attr_by_name( hid_t handle, const char* name, void* mydata )
266 {
267 #endif
268     /* empty statement to remove compiler warning */
269   if (handle) {}
270   return !strcmp( name, (const char*)mydata ); }
271 
272 int mhdf_find_attribute ( hid_t object,
273                           const char* attrib_name,
274                           unsigned int* index_out,
275                           mhdf_Status* status )
276 {
277   herr_t rval;
278 #if defined(H5Aiterate_vers) && H5Aiterate_vers > 1
279   hsize_t idx = 0;
280   rval = H5Aiterate2( object, H5_INDEX_CRT_ORDER, H5_ITER_NATIVE, &idx, &find_attr_by_name, (void*)attrib_name );
281   *index_out = (unsigned int)idx;
282 #else
283   *index_out = 0;
284   rval = H5Aiterate( object, index_out, &find_attr_by_name, (void*)attrib_name );
285 #endif
286   if (rval < 0)
287     mhdf_setFail( status, "Internal error calling H5Aiterate." );
288   return (int)rval;
289 }
290 
291 
292 static herr_t find_link_by_name( hid_t handle, const char* name, void* mydata )
293 {
294     /* empty statement to remove compiler warning */
295   if (handle) {}
296   return !strcmp( name, (const char*)mydata ); }
297 
298 int mhdf_is_in_group( hid_t group, const char* name, mhdf_Status* status )
299 {
300   int rval;
301   rval = H5Giterate( group, ".", NULL, &find_link_by_name, (void*)name );
302   if (rval < 0)
303     mhdf_setFail( status, "Internal error in H5Giterate." );
304   return rval;
305 }
306 
307 static int
308 mhdf_readwrite( hid_t data_id, int read,
309                 long offset, long count,
310                 hid_t type, void* array,
311                 hid_t io_prop,
312                 mhdf_Status* status )
313 {
314   hid_t slab_id, mem_id;
315   hsize_t offsets[2], counts[2] = {1,1};
316   herr_t rval;
317   int dims;
318 /*#if (1000 * H5_VERS_MAJOR + H5_VERS_MINOR) < 1008*/
319   const hsize_t one = 1;
320 /*#endif*/
321 
322   if (offset < 0 || count < 0)
323   {
324     mhdf_setFail( status, "Invalid input for %s: "
325                           "offset = %ld, count = %ld\n",
326                           read ? "read" : "write",
327                           offset, count );
328     return 0;
329   }
330 
331   slab_id = H5Dget_space( data_id );
332   if (slab_id < 0)
333   {
334     mhdf_setFail( status, "Internal error calling H5Dget_space.");
335     return 0;
336   }
337 
338   dims = H5Sget_simple_extent_ndims( slab_id );
339   if (dims < 1 || dims > 2)
340   {
341     H5Sclose( slab_id );
342     mhdf_setFail( status, "Internal error: unexpected dataset rank: %d.", dims);
343     return 0;
344   }
345 
346   dims = H5Sget_simple_extent_dims( slab_id, counts, NULL );
347   if (dims < 0)
348   {
349     H5Sclose( slab_id );
350     mhdf_setFail( status, "Internal error calling H5Sget_simple_extend_dims.");
351     return 0;
352   }
353 
354   if ((unsigned long)(offset + count) > counts[0])
355   {
356     H5Sclose( slab_id );
357     mhdf_setFail( status,
358       "Requested %s of rows %ld to %ld of a %ld row table.\n",
359       read ? "read" : "write", offset, offset+count-1, (long)counts[dims-1]);
360     return 0;
361   }
362 
363 
364   counts[0] = (hsize_t)count;
365   offsets[0] = (hsize_t)offset;
366   offsets[1] = 0;
367   if (count)
368     rval = H5Sselect_hyperslab( slab_id, H5S_SELECT_SET, offsets, NULL, counts, NULL );
369   else
370     rval = H5Sselect_none( slab_id );
371   if (rval < 0)
372   {
373     H5Sclose( slab_id );
374     mhdf_setFail( status, "Internal error calling H5Sselect_hyperslab." );
375     return 0;
376   }
377 
378   if (count)
379     mem_id = H5Screate_simple( dims, counts, NULL );
380   else {
381 /*#if H5_VERS_MAJOR > 1 || H5_VERS_MINOR >= 8
382     mem_id = H5Screate(H5S_NULL);
383 #else*/
384     mem_id = H5Screate_simple( 1, &one, NULL );
385     if (mem_id && 0 > H5Sselect_none( mem_id )) {
386       H5Sclose( mem_id );
387       mem_id = -1;
388     }
389 /*#endif*/
390   }
391 
392   if (mem_id < 0)
393   {
394     H5Sclose( slab_id );
395     mhdf_setFail( status, "Internal error calling H5Screate_simple." );
396     return 0;
397   }
398 
399 
400   if (read)
401     rval = H5Dread( data_id, type, mem_id, slab_id, io_prop, array );
402   else {
403     VALGRIND_CHECK_MEM_IS_DEFINED( array, counts[0]*counts[1]*H5Tget_size(type) );
404     rval = H5Dwrite( data_id, type, mem_id, slab_id, io_prop, array );
405   }
406   H5Sclose( slab_id );
407   H5Sclose( mem_id );
408   if (rval < 0)
409   {
410     mhdf_setFail( status, "Internal error calling H5D%s.", read ? "read" : "write" );
411     return 0;
412   }
413 
414   mhdf_setOkay( status );
415   return 1;
416 }
417 
418 
419 static int
420 mhdf_readwrite_column( hid_t data_id, int read,
421                        int column,
422                        long offset, long count,
423                        hid_t type, void* array,
424                        hid_t io_prop,
425                        mhdf_Status* status )
426 {
427   hid_t slab_id, mem_id;
428   hsize_t offsets[2], counts[2];
429   herr_t rval;
430   int dims;
431 /*#if (1000 * H5_VERS_MAJOR + H5_VERS_MINOR) < 1008*/
432   const hsize_t one = 1;
433 /*#endif*/
434 
435   if (column < 0 || offset < 0 || count < 0)
436   {
437     mhdf_setFail( status, "Invalid input for %s: "
438                           "column = %d, offset = %ld, count = %ld\n",
439                           read ? "read" : "write",
440                           column, offset, count );
441     return 0;
442   }
443 
444   slab_id = H5Dget_space( data_id );
445   if (slab_id < 0)
446   {
447     mhdf_setFail( status, "Internal error calling H5Dget_space.");
448     return 0;
449   }
450 
451   dims = H5Sget_simple_extent_ndims( slab_id );
452   if (dims < 1 || dims > 2)
453   {
454     H5Sclose( slab_id );
455     mhdf_setFail( status, "Internal error: unexpected dataset rank: %d.", dims);
456     return 0;
457   }
458 
459   dims = H5Sget_simple_extent_dims( slab_id, counts, NULL );
460   if (dims < 0)
461   {
462     H5Sclose( slab_id );
463     mhdf_setFail( status, "Internal error calling H5Sget_simple_extend_dims.");
464     return 0;
465   }
466 
467   if ((unsigned long)(offset + count) > counts[0] ||
468       (unsigned long)column > counts[1])
469   {
470     H5Sclose( slab_id );
471     mhdf_setFail( status,
472       "Requested %s of (%ld,%d)->(%ld,%ld) of (%ld, %ld) table.\n",
473       read ? "read" : "write", offset, column, offset+count-1,
474       column, (long)counts[0], (long)counts[1]);
475     return 0;
476   }
477 
478   counts[0] = (hsize_t)count;
479   offsets[0] = (hsize_t)offset;
480   counts[1] = 1;
481   offsets[1] = column;
482   if (count)
483     rval = H5Sselect_hyperslab( slab_id, H5S_SELECT_SET, offsets, NULL, counts, NULL );
484   else
485     rval = H5Sselect_none( slab_id );
486   if (rval < 0)
487   {
488     H5Sclose( slab_id );
489     mhdf_setFail( status, "Internal error calling H5Sselect_hyperslab." );
490     return 0;
491   }
492 
493 
494   if (count)
495     mem_id = H5Screate_simple( dims, counts, NULL );
496   else {
497 /*#if H5_VERS_MAJOR > 1 || H5_VERS_MINOR >= 8
498     mem_id = H5Screate(H5S_NULL);
499 #else*/
500     mem_id = H5Screate_simple( 1, &one, NULL );
501     if (mem_id && 0 > H5Sselect_none( mem_id )) {
502       H5Sclose( mem_id );
503       mem_id = -1;
504     }
505 /*#endif*/
506   }
507 
508   if (mem_id < 0)
509   {
510     H5Sclose( slab_id );
511     mhdf_setFail( status, "Internal error calling H5Screate_simple." );
512     return 0;
513   }
514 
515 
516   if (read)
517     rval = H5Dread( data_id, type, mem_id, slab_id, io_prop, array );
518   else {
519     VALGRIND_CHECK_MEM_IS_DEFINED( array, count*H5Tget_size(type) );
520     rval = H5Dwrite( data_id, type, mem_id, slab_id, io_prop, array );
521     VALGRIND_MAKE_MEM_UNDEFINED( array, count*H5Tget_size(type) );
522   }
523   H5Sclose( slab_id );
524   H5Sclose( mem_id );
525   if (rval < 0)
526   {
527     mhdf_setFail( status, "Internal error calling H5D%s.", read ? "read" : "write" );
528     return 0;
529   }
530 
531   mhdf_setOkay( status );
532   return 1;
533 }
534 
535 
536 int
537 mhdf_write_data( hid_t data_id,
538                  long offset,
539                  long count,
540                  hid_t type_id,
541                  const void* array,
542                  hid_t prop,
543                  mhdf_Status* status )
544 {
545   return mhdf_readwrite( data_id, 0, offset, count, type_id, (void*)array, prop, status );
546 }
547 
548 int
549 mhdf_read_data( hid_t data_id,
550                 long offset,
551                 long count,
552                 hid_t type_id,
553                 void* array,
554                 hid_t prop,
555                 mhdf_Status* status )
556 {
557   return mhdf_readwrite( data_id, 1, offset, count, type_id, array, prop, status );
558 }
559 
560 int
561 mhdf_read_column( hid_t data_id,
562                   int column,
563                   long offset,
564                   long count,
565                   hid_t type,
566                   void* array,
567                   hid_t prop,
568                   mhdf_Status* status )
569 {
570   return mhdf_readwrite_column( data_id, 1, column, offset, count,
571                                 type, array, prop, status );
572 }
573 
574 int
575 mhdf_write_column( hid_t data_id,
576                    int column,
577                    long offset,
578                    long count,
579                    hid_t type,
580                    const void* array,
581                    hid_t prop,
582                    mhdf_Status* status )
583 {
584   return mhdf_readwrite_column( data_id, 0, column, offset, count,
585                                 type, (void*)array, prop, status );
586 }
587 
588 
589 hid_t
590 mhdf_create_table( hid_t group_id,
591                    const char* path,
592                    hid_t type,
593                    int rank,
594                    hsize_t* dims,
595                    mhdf_Status* status )
596 {
597   return mhdf_create_table_with_prop( group_id, path, type, rank, dims, H5P_DEFAULT, status );
598 }
599 
600 hid_t
601 mhdf_create_table_with_prop( hid_t group_id,
602                              const char* path,
603                              hid_t type,
604                              int rank,
605                              hsize_t* dims,
606                              hid_t create_prop,
607                              mhdf_Status* status )
608 {
609   hid_t space_id, table_id;
610 
611   space_id = H5Screate_simple( rank, dims, NULL );
612   if (space_id < 0)
613   {
614     mhdf_setFail( status, "Internal error calling H5Screate_simple." );
615     return -1;
616   }
617 
618 #if defined(H5Dcreate_vers) && H5Dcreate_vers > 1
619   table_id = H5Dcreate2( group_id, path, type, space_id, H5P_DEFAULT, create_prop, H5P_DEFAULT );
620 #else
621   table_id = H5Dcreate( group_id, path, type, space_id, create_prop );
622 #endif
623   H5Sclose(space_id);
624   if (table_id < 0)
625   {
626     mhdf_setFail( status, "HDF5 DataSet creation failed.");
627     return -1;
628   }
629 
630   mhdf_setOkay( status );
631   return table_id;
632 
633 }
634 
635 hid_t
636 mhdf_open_table( hid_t group_id,
637                  const char* path,
638                  int columns,
639                  hsize_t* rows_out,
640                  mhdf_Status* status )
641 {
642   hid_t table_id, space_id;
643   hsize_t dims[2];
644   int rank;
645 
646 #if defined(H5Dopen_vers) && H5Dopen_vers > 1
647   table_id = H5Dopen2( group_id, path, H5P_DEFAULT );
648 #else
649   table_id = H5Dopen( group_id, path );
650 #endif
651   if (table_id < 0)
652   {
653     mhdf_setFail( status, "HDF5 DataSet creation failed.");
654     return -1;
655   }
656 
657   space_id = H5Dget_space( table_id );
658   if (space_id < 0)
659   {
660     mhdf_setFail( status, "Internal error in H5Dget_space.");
661     H5Dclose( table_id );
662     return -1;
663   }
664 
665   rank = H5Sget_simple_extent_ndims( space_id );
666   if (rank != (columns ? 1 : 2))
667   {
668     mhdf_setFail( status, "Incorrect DataSpace for DataSet." );
669     H5Sclose( space_id );
670     H5Dclose( table_id );
671     return -1;
672   }
673 
674   rank = H5Sget_simple_extent_dims( space_id, dims, NULL );
675   H5Sclose( space_id );
676   if (rank < 0)
677   {
678     mhdf_setFail( status, "Internal error calling H5Sget_simple_extent_dims.");
679     H5Dclose( table_id );
680     return -1;
681   }
682 
683   *rows_out = dims[0];
684   mhdf_setOkay( status );
685   return table_id;
686 }
687 
688 hid_t
689 mhdf_open_table2( hid_t group_id,
690                   const char* path,
691                   int rank,
692                   hsize_t* dims_out,
693                   long* start_id_out,
694                   mhdf_Status* status )
695 {
696   hid_t table_id, space_id;
697 
698 #if defined(H5Dopen_vers) && H5Dopen_vers > 1
699   table_id = H5Dopen2( group_id, path, H5P_DEFAULT );
700 #else
701   table_id = H5Dopen( group_id, path );
702 #endif
703   if (table_id < 0)
704   {
705     mhdf_setFail( status, "HDF5 DataSet creation failed.");
706     return -1;
707   }
708 
709   space_id = H5Dget_space( table_id );
710   if (space_id < 0)
711   {
712     mhdf_setFail( status, "Internal error in H5Dget_space.");
713     H5Dclose( table_id );
714     return -1;
715   }
716 
717   if (H5Sget_simple_extent_ndims( space_id ) != rank)
718   {
719     mhdf_setFail( status, "Incorrect DataSpace for DataSet." );
720     H5Sclose( space_id );
721     H5Dclose( table_id );
722     return -1;
723   }
724 
725   rank = H5Sget_simple_extent_dims( space_id, dims_out, NULL );
726   H5Sclose( space_id );
727   if (rank < 0)
728   {
729     mhdf_setFail( status, "Internal error calling H5Sget_simple_extent_dims.");
730     H5Dclose( table_id );
731     return -1;
732   }
733 
734   if (!mhdf_read_scalar_attrib( table_id, START_ID_ATTRIB, H5T_NATIVE_LONG, start_id_out, status ))
735   {
736     mhdf_setFail( status, "File format error.  Failed to retreive ID offset.");
737     H5Dclose( table_id );
738     return -1;
739   }
740 
741   mhdf_setOkay( status );
742   return table_id;
743 }
744 
745 hid_t
746 mhdf_open_table_simple( hid_t group_id, const char* path, mhdf_Status* status )
747 {
748   hid_t table_id;
749 
750 #if defined(H5Dopen_vers) && H5Dopen_vers > 1
751   table_id = H5Dopen2( group_id, path, H5P_DEFAULT );
752 #else
753   table_id = H5Dopen( group_id, path );
754 #endif
755   if (table_id < 0)
756   {
757     mhdf_setFail( status, "HDF5 DataSet creation failed.");
758   }
759   else
760   {
761     mhdf_setOkay( status );
762   }
763 
764   return table_id;
765 }
766 
767 static int qs_comp_int ( const void* ptr1, const void* ptr2 )
768 {
769   const int* left = (const int*)ptr1;
770   const int* right = (const int*)ptr2;
771   return *left < *right ? -1 : *left > *right ? 1 : 0;
772 }
773 
774 int mhdf_compact_to_ranges( int* length, int* ids, int ordered )
775 {
776   int new_length = 0;
777   int* iter, *end;
778   int prev, count;
779   int need_copy = 0;
780   int *copy_ptr = 0, *w_iter;
781   size_t blen;
782 
783   if (!ordered)
784     qsort( ids, *length, sizeof(int), &qs_comp_int );
785 
786   iter = ids;
787   end = ids + *length;
788   while( iter != end )
789   {
790     prev = *(iter++);
791     while( iter < end && *(iter++) == ++prev );
792     new_length += 2;
793     if (new_length > (iter - ids))
794       need_copy = 1;
795   }
796 
797   if (new_length > *length)
798     return 0;
799 
800   if (need_copy)
801   {
802     blen = sizeof(int) * *length;
803     copy_ptr = (int*)malloc( blen );
804     memcpy( copy_ptr, ids, blen );
805     iter = copy_ptr;
806   }
807   else
808   {
809     iter = ids;
810   }
811 
812   end = iter + *length;
813   w_iter = ids;
814   while( iter != end )
815   {
816     prev = *(iter++);
817     count = 1;
818     while( iter < end && *(iter++) == ++prev );
819     *(w_iter++) = prev - count;
820     *(w_iter++) = count;
821   }
822 
823   *length = new_length;
824   if (need_copy)
825     free( copy_ptr );
826   return 1;
827 }
828 
829 hid_t
830 get_elem_type_enum( FileHandle* file_ptr, mhdf_Status* status )
831 {
832   hid_t result;
833 #if defined(H5Topen_vers) && H5Topen_vers > 1
834   result = H5Topen2( file_ptr->hdf_handle, TYPE_ENUM_PATH, H5P_DEFAULT );
835 #else
836   result = H5Topen( file_ptr->hdf_handle, TYPE_ENUM_PATH );
837 #endif
838   if (result < 0)
839     mhdf_setFail( status, "Element type enum does not exist in file.  Invalid file." );
840   return result;
841 }
842 
843 int
844 mhdf_write_max_id( FileHandle* file_ptr, mhdf_Status* status )
845 {
846   hid_t group_id, attr_id, space_id;
847   herr_t rval;
848 
849 #if defined(H5Gopen_vers) && H5Gopen_vers > 1
850   group_id = H5Gopen2( file_ptr->hdf_handle, ROOT_GROUP, H5P_DEFAULT );
851 #else
852   group_id = H5Gopen( file_ptr->hdf_handle, ROOT_GROUP );
853 #endif
854   if (group_id < 0)
855   {
856     mhdf_setFail( status, "Internal error -- file invalid." );
857     return 0;
858   }
859 
860   attr_id = H5Aopen_name( group_id, MAX_ID_ATTRIB );
861   if (attr_id < 0)
862   {
863     space_id = H5Screate( H5S_SCALAR );
864 #if defined(H5Acreate_vers) && H5Acreate_vers > 1
865     attr_id = H5Acreate2( group_id,
866                           MAX_ID_ATTRIB,
867                           H5T_NATIVE_ULONG,
868                           space_id,
869                           H5P_DEFAULT,
870                           H5P_DEFAULT );
871 #else
872     attr_id = H5Acreate( group_id,
873                          MAX_ID_ATTRIB,
874                          H5T_NATIVE_ULONG,
875                          space_id,
876                          H5P_DEFAULT );
877 #endif
878     H5Sclose( space_id );
879   }
880   H5Gclose( group_id );
881   if (attr_id < 0)
882   {
883     mhdf_setFail( status, "Failed to create attribute \"%s\" on \"%s\"", MAX_ID_ATTRIB, ROOT_GROUP );
884     return 0;
885   }
886 
887 
888   rval = H5Awrite( attr_id, H5T_NATIVE_ULONG, &file_ptr->max_id );
889   H5Aclose( attr_id );
890   if (rval < 0)
891   {
892     mhdf_setFail( status, "Failed to write \"%s\" attrib.", MAX_ID_ATTRIB );
893     return 0;
894   }
895 
896   return 1;
897 }
898 
899 
900 
901 static int mhdf_api_handle_count = 0;
902 
903 static int num_open( )
904 {
905   hid_t list[64];
906   int nf, rval, i, count = 0;
907 
908   nf = H5Fget_obj_ids( H5F_OBJ_ALL, H5F_OBJ_FILE, sizeof(list)/sizeof(hid_t), list );
909   if (nf <= 0 || nf > 64)
910     return 0;
911 
912   for (i = 0; i < nf; i++)
913   {
914     rval = H5Fget_obj_count( list[i], H5F_OBJ_ALL );
915     if (rval > 0)
916       count += rval;
917   }
918 
919   return count;
920 }
921 
922 
923 void mhdf_api_begin_internal( )
924 {
925   /* HDF5 docs are incorrect.  Passing H5F_OBJ_ALL as the first
926      arg to H5Fget_obj_count returns the total number of open
927      handles, not just those in files (i.e. temporary types and such.)
928   mhdf_api_handle_count = H5Fget_obj_count( H5F_OBJ_ALL, H5F_OBJ_ALL );
929      Need to loop to get actual file handles:
930   */
931   mhdf_api_handle_count = num_open();
932 }
933 
934 void mhdf_api_end_internal( int expected_diff,
935                             const char* filename,
936                             int linenumber )
937 {
938   if (mhdf_api_handle_count + expected_diff != num_open( ))
939   {
940     fprintf( stderr, "Unclosed handles at end of mhdf API call.\n" );
941     fprintf( stderr, "Entered with %d, expected %d change, got %d.\n",
942       mhdf_api_handle_count, expected_diff, num_open( ) );
943     fprintf( stderr, "%s:%d\n", filename, linenumber );
944     abort();
945   }
946 
947   mhdf_api_handle_count = 0;
948 }
949 
950 
951