1 #include "mhdf.h"
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <sys/stat.h>
6 #include <H5Tpublic.h>
7 
8 
9 
10 /* Top-level validation functions */
11 
12 /* check that group ID ranges are valid and non-overlapping */
13 static int check_valid_file_ids( struct mhdf_FileDesc* desc );
14 
15 /* check that file doesn't contain holes (unwritten regions) */
16 static int check_file_contains_holes( const char* filename );
17 
18 /* check IDs are valid for all element connectivity data */
19 static int check_valid_connectivity( mhdf_FileHandle file, struct mhdf_FileDesc* desc );
20 
21 /* check that any adjacency lists contain valid IDs */
22 static int check_valid_adjacencies( mhdf_FileHandle file, struct mhdf_FileDesc* desc );
23 
24 /* check that set data is consistent and that sets contain valid ids */
25 static int check_valid_sets( mhdf_FileHandle file, struct mhdf_FileDesc* desc );
26 
27 /* check tag consistency and for handle tags verify that values are valid ids */
28 static int check_valid_tags( mhdf_FileHandle file, struct mhdf_FileDesc* desc );
29 
30 
31 
32 /* Low-level helper routines */
33 
34 /* Get string name from mhdf_EntDesc pointer */
35 static const char* desc_name( struct mhdf_FileDesc* desc, struct mhdf_EntDesc* grp );
36 
37 /* Given a list of ID range pairs of [start_id,count] interleaved in 'ranges',
38    test if the passed file_id is contained in at least one passed range */
39 static int id_contained( long file_id, const long* ranges, int num_range );
40 
41 /* Like id_contained, only returns logical and for all ids in a passed array */
42 static int ids_contained( const long* ids, int num_ids, const long* ranges, int num_ranges );
43 
44 /* Like ids_contained, both input lists are in ranged format */
45 static int ranges_contained( const long* id_ranges, int num_id_ranges, const long* ranges, int num_ranges );
46 
47 /* Search for a string in a null-terminated list of strings */
48 static int string_contained( const char* str, const char* const* const list );
49 
50 /* Check if an array of longs contains duplicate values.  Will sort passed array */
51 static int contains_duplicates( long* array, long n );
52 
53 /* Check if an array of [start_id,count] range pairs containes duplicate values/overlapping ranges.
54    Will sort passed array */
55 static int ranges_contain_duplicates( long* ranges, long nranges );
56 
57 /* Get dynamically allocated array of [start_id,count] range pairs corresponding to all valid
58    file IDs.  If include_null is non-zero, results will also include special ID of zero. */
59 static long* all_id_ranges( struct mhdf_FileDesc* desc, int include_null, int* num_ranges_out );
60 
61 /* Get dynamically allocated array of [start_id,count] range pairs corresponding to all valid
62    file IDs for entities with a specific dimension. */
63 static long* get_dim_ranges( struct mhdf_FileDesc* desc, int dim, int* num_ranges_out );
64 
65 /* Merge adjacent ranges */
66 static int merge_ranges( long* ranges, int nranges );
67 
68 /* Misc. high-level helper routines */
69 
70 /* Check that end-index lists used in various file data for variable-length data
71    (e.g. set contents, poly connectivity, variable-length tags) is valid.  If
72    min_len is non-zero, it will be considered an error if the data for any entity
73    contains less than that number of values.  It is considered an error if any
74    index in the list is past the end of a dataset containing max_value entries. */
75 static int check_valid_end_indices( const long* indices, long num_idx, int min_len,
76                                     long start_id, long max_value,
77                                     const char* typestr, const char* name );
78 
79 /* Do check_valid_connectivity for an element group with constant connectivity length */
80 static int check_valid_elem_conn( int idx, mhdf_FileHandle file, struct mhdf_FileDesc* desc, int conn_dim );
81 
82 /* Do check_valid_connectivity for an element group with old-format variable-length connectivity */
83 static int check_valid_poly_conn( int idx, mhdf_FileHandle file, struct mhdf_FileDesc* desc, int conn_dim );
84 
85 /* Do check_valid_tags for a fixed-length tag */
86 static int check_valid_tag( int tag_idx, mhdf_FileHandle file, struct mhdf_FileDesc* desc );
87 
88 /* Do check_valid_tags for a variable-length tag */
89 static int check_valid_var_len_tag( int tag_idx, mhdf_FileHandle file, struct mhdf_FileDesc* desc );
90 
91 /* Do check_valid_adjacencies for the adjacency list of a single entity group */
92 static int check_valid_adj_list( long start_id, long count, const long* data, long data_len,
93                                  const long* valid_ranges, long num_valid_ranges, const char* name );
94 
95 /* Do subset of check_valid_sets for either set parent or set child data */
96 static int check_valid_parents_children( long start_id,
97                                          long count,
98                                          hid_t meta_handle,
99                                          hid_t data_handle,
100                                          long data_len,
101                                          int parents );
102 
103 /* Do subset of check_valid_sets for set contents */
104 static int check_valid_set_contents( struct mhdf_FileDesc* desc,
105                                      long start_id,
106                                      long count,
107                                      hid_t meta_handle,
108                                      hid_t data_handle,
109                                      long data_len );
110 
111 
112 /* Comparison routines for use with qsort */
113 
114 /* Compare two long int values */
lcomp(const void * p1,const void * p2)115 static int lcomp( const void* p1, const void* p2 )
116 {
117   long l1 = *(const long*)p1;
118   long l2 = *(const long*)p2;
119   return l1 < l2 ? -1 : l1 > l2 ? 1 : 0;
120 }
121 
122 /* Compare start_ids of mhdf_EntDesc pointed to by passed values */
dcomp(const void * p1,const void * p2)123 static int dcomp( const void* p1, const void* p2 )
124 {
125   const struct mhdf_EntDesc* d1 = *(const struct mhdf_EntDesc**)(p1);
126   const struct mhdf_EntDesc* d2 = *(const struct mhdf_EntDesc**)(p2);
127   return lcomp( &d1->start_id, &d2->start_id );
128 }
129 
130 
131 
132 int verbose = 0;
133 
main(int argc,char * argv[])134 int main( int argc, char* argv[] )
135 {
136   int result = 0;
137   mhdf_FileHandle file;
138   mhdf_Status status;
139   unsigned long max_id;
140   struct mhdf_FileDesc* desc;
141 
142   if (argc != 2) {
143     fprintf( stderr,"Usage: %s <filename>\n", argv[0] );
144     return 1;
145   }
146 
147   file = mhdf_openFile( argv[1], 0, &max_id, -1, &status );
148   if (mhdf_isError( &status )) {
149     fprintf( stderr,"%s: %s\n", argv[1], mhdf_message( &status ) );
150     return 1;
151   }
152 
153   desc = mhdf_getFileSummary( file, H5T_NATIVE_LONG, &status, 0 ); /*no extra set info*/
154   if (mhdf_isError( &status )) {
155     fprintf( stderr,"%s: %s\n", argv[1], mhdf_message( &status ) );
156     return 1;
157   }
158 
159   if (desc->nodes.count < 1)
160     puts("WARNING: file contains no vertices.");
161   if (desc->num_elem_desc < 1)
162     puts("WARNING: file contains no elements.");
163 
164   result += check_valid_file_ids( desc );
165   result += check_file_contains_holes( argv[1] );
166   result += check_valid_connectivity( file, desc );
167   result += check_valid_adjacencies( file, desc );
168   result += check_valid_sets( file, desc );
169   result += check_valid_tags( file, desc );
170 
171   free( desc );
172   mhdf_closeFile( file, &status );
173   return result;
174 }
175 
176 
desc_name(struct mhdf_FileDesc * desc,struct mhdf_EntDesc * grp)177 static const char* desc_name( struct mhdf_FileDesc* desc, struct mhdf_EntDesc* grp )
178 {
179   struct mhdf_ElemDesc junk, *elem;
180   const size_t diff = (char*)&junk.desc - (char*)&junk;
181   static const char nodes[] = "Vertices";
182   static const char sets[] = "Sets";
183   if (grp == &desc->nodes)
184     return nodes;
185   if (grp == &desc->sets)
186     return sets;
187 
188   elem = (struct mhdf_ElemDesc*)((char*)grp - diff);
189   return elem->handle;
190 }
191 
check_valid_file_ids(struct mhdf_FileDesc * desc)192 int check_valid_file_ids( struct mhdf_FileDesc* desc )
193 {
194   const int ngrp = 2 + desc->num_elem_desc;
195   int i, err = 0;
196   struct mhdf_EntDesc** sorted = malloc(ngrp*sizeof(struct mhdf_EntDesc*));
197   for (i = 0; i < desc->num_elem_desc; ++i)
198     sorted[i] = &desc->elems[i].desc;
199   sorted[i++] = &desc->nodes;
200   sorted[i] = &desc->sets;
201   qsort( sorted, ngrp, sizeof(struct mhdf_EntDesc*), &dcomp );
202   for (i = 0; i < ngrp; ++i) {
203     if (sorted[i]->count < 0) {
204       printf("Group \"%s\" has negative count!\n", desc_name( desc, sorted[i] ));
205       ++err;
206     }
207     if (sorted[i]->count && sorted[i]->start_id == 0) {
208       printf("Group \"%s\" contains NULL ID!\n", desc_name( desc, sorted[i] ));
209       ++err;
210     }
211 
212 
213     if (i > 0 && sorted[i-1]->start_id + sorted[i-1]->count > sorted[i]->start_id) {
214       printf("Conflicting group IDs for \"%s\" [%ld,%ld] and \"%s\" [%ld,%ld]\n",
215         desc_name(desc,sorted[i-1]), sorted[i-1]->start_id, sorted[i-1]->start_id + sorted[i-1]->count - 1,
216         desc_name(desc,sorted[i  ]), sorted[i  ]->start_id, sorted[i  ]->start_id + sorted[i  ]->count - 1 );
217       ++err;
218     }
219   }
220   free(sorted);
221   return err;
222 }
223 
check_file_contains_holes(const char * filename)224 int check_file_contains_holes( const char* filename )
225 {
226 #ifndef _MSC_VER
227   const int blocksize = 512;
228 #endif
229   int errorcode;
230   struct stat buf;
231 
232   errorcode = stat( filename, &buf );
233   if (errorcode) {
234     perror(filename);
235     return 1;
236   }
237 
238 #ifndef _MSC_VER  /*Does not have st_blocks*/
239   if (buf.st_size/blocksize > buf.st_blocks+1) {
240     printf("File \"%s\" contains holes.  This indicates that portions of the file were never written.\n",
241       filename);
242     return 1;
243   }
244 #endif
245 
246   return 0;
247 }
248 
249 
check_valid_connectivity(mhdf_FileHandle file,struct mhdf_FileDesc * desc)250 int check_valid_connectivity( mhdf_FileHandle file, struct mhdf_FileDesc* desc )
251 {
252   int idx, dim, result = 0;
253 
254   for (idx = 0; idx < desc->num_elem_desc; ++idx) {
255     if (!strcmp(desc->elems[idx].type,mhdf_POLYHEDRON_TYPE_NAME))
256       dim = 2;
257     else
258       dim = 0;
259 
260     if (desc->elems[idx].desc.vals_per_ent == -1)
261       result += check_valid_poly_conn( idx, file, desc, dim );
262     else
263       result += check_valid_elem_conn( idx, file, desc, dim );
264   }
265 
266   return result;
267 }
268 
id_contained(long file_id,const long * ranges,int num_range)269 static int id_contained( long file_id, const long* ranges, int num_range )
270 {
271   const long* end = ranges + 2*num_range;
272   for (; ranges != end; ranges+=2) {
273     if (file_id >= ranges[0] && (file_id - ranges[0]) < ranges[1])
274       return 1;
275   }
276   return 0;
277 }
278 
ids_contained(const long * ids,int num_ids,const long * ranges,int num_ranges)279 static int ids_contained( const long* ids, int num_ids, const long* ranges, int num_ranges )
280 {
281   int i;
282   for (i = 0; i < num_ids; ++i)
283     if (!id_contained( ids[i], ranges, num_ranges ))
284       return 0;
285   return 1;
286 }
287 
ranges_contained(const long * id_ranges,int num_id_ranges,const long * ranges,int num_ranges)288 static int ranges_contained( const long* id_ranges, int num_id_ranges, const long* ranges, int num_ranges )
289 {
290   int i;
291   long start, count, avail;
292   const long* end = ranges + 2*num_ranges;
293   const long* iter;
294   for (i = 0; i < num_id_ranges; ++i) {
295     start = id_ranges[2*i];
296     count = id_ranges[2*i+1];
297     while (count > 0) {
298       for (iter = ranges; iter != end; iter+=2) {
299         if (start >= iter[0] && (start - iter[0]) < iter[1])
300           break;
301       }
302       if (iter == end)
303         return 0;
304       avail = iter[1] - (start - iter[0]);
305       count -= avail;
306       start += avail;
307     }
308   }
309   return 1;
310 }
311 
string_contained(const char * str,const char * const * list)312 static int string_contained( const char* str, const char* const* list )
313 {
314   for (; *list; ++list)
315     if (!strcmp(str,*list))
316       return 1;
317   return 0;
318 }
319 
get_dim_ranges(struct mhdf_FileDesc * desc,int dim,int * num_ranges_out)320 static long* get_dim_ranges( struct mhdf_FileDesc* desc, int dim, int* num_ranges_out )
321 {
322   long* ranges = 0;
323   int i, j;
324   const char* const types1D[] = { mhdf_EDGE_TYPE_NAME, 0 };
325   const char* const types2D[] = { mhdf_TRI_TYPE_NAME, mhdf_QUAD_TYPE_NAME, mhdf_POLYGON_TYPE_NAME, 0 };
326   const char* const types3D[] = { mhdf_TET_TYPE_NAME, mhdf_PYRAMID_TYPE_NAME, mhdf_PRISM_TYPE_NAME,
327                                   mdhf_KNIFE_TYPE_NAME, mdhf_HEX_TYPE_NAME, mhdf_POLYHEDRON_TYPE_NAME,
328                                   mhdf_SEPTAHEDRON_TYPE_NAME, 0 };
329 
330   char const* const* typelist;
331   switch (dim) {
332     case 0:
333       *num_ranges_out = 1;
334       ranges = malloc(2*sizeof(long));
335       ranges[0] = desc->nodes.start_id;
336       ranges[1] = desc->nodes.count;
337       return ranges;
338     case 1:
339       typelist = types1D;
340       break;
341     case 2:
342       typelist = types2D;
343       break;
344     case 3:
345       typelist = types3D;
346       break;
347     default:
348       fprintf(stderr,"Internal error at %s:%d: request for entities of dimesion %d\n",
349         __FILE__, __LINE__, dim );
350       abort();
351   }
352 
353   *num_ranges_out = 0;
354   for (i = 0; i < desc->num_elem_desc; ++i)
355     if (string_contained( desc->elems[i].type, typelist ))
356       ++*num_ranges_out;
357   ranges = malloc(*num_ranges_out * 2 * sizeof(long));
358   for (i = 0, j = 0; i < desc->num_elem_desc; ++i)
359     if (string_contained( desc->elems[i].type, typelist )) {
360       ranges[j++]   = desc->elems[i].desc.start_id;
361       ranges[j++] = desc->elems[i].desc.count;
362     }
363 
364   *num_ranges_out = merge_ranges( ranges, *num_ranges_out );
365   return ranges;
366 }
367 
check_valid_elem_conn(int idx,mhdf_FileHandle file,struct mhdf_FileDesc * desc,int conn_dim)368 int check_valid_elem_conn( int idx, mhdf_FileHandle file, struct mhdf_FileDesc* desc, int conn_dim )
369 {
370   long *ranges, *buffer, *iter;
371   int num_ranges;
372   long i, invalid = 0;
373   hid_t handle;
374   mhdf_Status status;
375   const long count = desc->elems[idx].desc.count;
376   const long len = desc->elems[idx].desc.vals_per_ent;
377 
378   ranges = get_dim_ranges( desc, conn_dim, &num_ranges );
379   handle = mhdf_openConnectivitySimple( file, desc->elems[idx].handle, &status );
380   if (mhdf_isError(&status)) {
381     fprintf(stderr,"Internal error opening connectivity for %s: %s\n",
382             desc->elems[idx].handle, mhdf_message(&status));
383     free(ranges);
384     return 1;
385   }
386 
387   buffer = malloc( sizeof(long) * count * len );
388   mhdf_readConnectivity( handle, 0, count, H5T_NATIVE_LONG, buffer, &status );
389   if (mhdf_isError(&status)) {
390     fprintf(stderr,"Internal error reading connectivity for %s: %s\n",
391             desc->elems[idx].handle, mhdf_message(&status));
392     free(ranges);
393     free(buffer);
394     mhdf_closeData( file, handle, &status );
395     return 1;
396   }
397   mhdf_closeData( file, handle, &status );
398 
399   iter = buffer;
400   for (i = 0; i < count; ++i, iter += len) {
401     if (!ids_contained( iter, len, ranges, num_ranges)) {
402       if (verbose)
403         printf("Invalid connectivity for element %ld (ID %ld) in %s\n",
404           i, i + desc->elems[idx].desc.start_id, desc->elems[idx].handle );
405       ++invalid;
406     }
407   }
408   free( buffer );
409   free( ranges );
410 
411   if (invalid) {
412     printf("%ld elements with invalid connectivity in %s\n", invalid, desc->elems[idx].handle );
413     return 1;
414   }
415   return 0;
416 }
417 
check_valid_end_indices(const long * indices,long num_idx,int min_len,long start_id,long max_value,const char * typestr,const char * name)418 static int check_valid_end_indices( const long* indices, long num_idx, int min_len,
419                                     long start_id, long max_value, const char* typestr, const char* name )
420 {
421   long i, invalid = 0, prev = -1;
422 
423   if (num_idx == 0) {
424     printf("WARNING: Empty index list for %s %s\n", name, typestr );
425     return 0;
426   }
427 
428   for (i = 0; i < num_idx; ++i) {
429     if (indices[i] < prev) {
430       if (verbose) {
431         if (start_id > 0)
432           printf("Invalid end index %ld for %s %ld (ID %ld).  Prev index is %ld\n", indices[i], name, i, i+start_id, prev );
433         else
434           printf("Invalid end index %ld for entry %ld in %s %s.  Prev index is %ld\n", indices[i], i, name, typestr, prev );
435       }
436       ++invalid;
437     }
438     else if (indices[i] - prev < min_len) {
439       if (verbose) {
440         if (start_id > 0)
441           printf("%s %ld (ID %ld) has only %ld values\n", name, i, start_id, indices[i] - prev );
442         else
443           printf("Entry %ld in %s %s has only %ld values\n", i, name, typestr, indices[i] - prev );
444       }
445       ++invalid;
446     }
447     else if (indices[i] >= max_value) {
448       if (verbose) {
449         if (start_id > 0)
450           printf("%s %ld (ID %ld) end index exceeds upper bound of %ld\n", name, i, start_id, max_value );
451         else
452           printf("Entry %ld in %s %s end index exceeds uppper bound of %ld\n", i, name, typestr, max_value );
453       }
454       ++invalid;
455     }
456     prev = indices[i];
457   }
458 
459   if (invalid) {
460     printf("%ld invalid end indices for %s %s\n", invalid, typestr, name );
461   }
462 
463   return invalid;
464 }
465 
check_valid_poly_conn(int idx,mhdf_FileHandle file,struct mhdf_FileDesc * desc,int conn_dim)466 int check_valid_poly_conn( int idx, mhdf_FileHandle file, struct mhdf_FileDesc* desc, int conn_dim )
467 {
468   long *ranges, *buffer, *indices, *iter;
469   int num_ranges;
470   long i, invalid, num_poly, num_conn, first_id, prev, len;
471   hid_t handles[2];
472   mhdf_Status status;
473   int min_conn_len;
474   if (!strcmp( mhdf_POLYHEDRON_TYPE_NAME, desc->elems[idx].type ))
475     min_conn_len = 4;
476   else
477     min_conn_len = 3;
478 
479   mhdf_openPolyConnectivity( file, desc->elems[idx].handle, &num_poly, &num_conn, &first_id, handles, &status );
480   if (mhdf_isError(&status)) {
481     fprintf(stderr,"Internal error opening connectivity for %s: %s\n",
482             desc->elems[idx].handle, mhdf_message(&status));
483     return 1;
484   }
485 
486   indices = malloc( sizeof(long) * num_poly );
487   mhdf_readPolyConnIndices( handles[0], 0, num_poly, H5T_NATIVE_LONG, indices, &status );
488   if (mhdf_isError(&status)) {
489     fprintf(stderr,"Internal error reading poly indices for %s: %s\n",
490             desc->elems[idx].handle, mhdf_message(&status));
491     free(indices);
492     mhdf_closeData( file, handles[0], &status );
493     mhdf_closeData( file, handles[1], &status );
494     return 1;
495   }
496   mhdf_closeData( file, handles[0], &status );
497 
498   invalid = check_valid_end_indices( indices, num_poly, min_conn_len, first_id, num_conn, "Connectivity", desc->elems[idx].handle );
499   if (invalid) {
500     free( indices );
501     mhdf_closeData( file, handles[1], &status );
502     return 1;
503   }
504 
505   ranges = get_dim_ranges( desc, conn_dim, &num_ranges );
506 
507   buffer = malloc( sizeof(long) * num_conn );
508   mhdf_readPolyConnIDs( handles[1], 0, num_conn, H5T_NATIVE_LONG, buffer, &status );
509   if (mhdf_isError(&status)) {
510     fprintf(stderr,"Internal error reading connectivity for %s: %s\n",
511             desc->elems[idx].handle, mhdf_message(&status));
512     free(ranges);
513     free(indices);
514     free(buffer);
515     mhdf_closeData( file, handles[1], &status );
516     return 1;
517   }
518   mhdf_closeData( file, handles[1], &status );
519 
520   prev = -1;
521   iter = buffer;
522   for (i = 0; i < num_poly; ++i) {
523     len = indices[i] - prev;
524     prev = indices[i];
525     if (!ids_contained( iter, len, ranges, num_ranges)) {
526       if (verbose)
527         printf("Invalid connectivity for element %ld (ID %ld) in %s\n",
528           i, i + first_id, desc->elems[idx].handle );
529       ++invalid;
530     }
531     iter += len;
532   }
533   free( indices );
534   free( buffer );
535   free( ranges );
536 
537   if (invalid) {
538     printf("%ld elements with invalid connectivity in %s\n", invalid, desc->elems[idx].handle );
539     return 1;
540   }
541   return 0;
542 }
543 
check_valid_adjacencies(mhdf_FileHandle file,struct mhdf_FileDesc * desc)544 int check_valid_adjacencies( mhdf_FileHandle file, struct mhdf_FileDesc* desc )
545 {
546   const int num_ranges = desc->num_elem_desc;
547   long *ranges, *buffer;
548   int i;
549   int invalid = 0;
550   long count;
551   hid_t handle;
552   mhdf_Status status;
553 
554   /* Build list of ID ranges for all elements.  Consider any element ID to be a valid
555    * thing to be adjacent to.  So we disallow adjacency to nodes, sets, or undefined IDs */
556   ranges = malloc( sizeof(long) * 2 * num_ranges );
557   for (i = 0; i < num_ranges; ++i) {
558     ranges[2*i  ] = desc->elems[i].desc.start_id;
559     ranges[2*i+1] = desc->elems[i].desc.count;
560   }
561 
562   for (i = 0; i < num_ranges; ++i) {
563     if (!desc->elems[i].have_adj)
564       continue;
565 
566     handle = mhdf_openAdjacency( file, desc->elems[i].handle, &count, &status );
567     if (mhdf_isError(&status)) {
568       fprintf(stderr,"Internal error openening adjacency list for %s: %s\n",
569               desc->elems[i].handle, mhdf_message(&status));
570       free(ranges);
571       return 1;
572     }
573 
574     buffer = malloc( sizeof(long) * count );
575     mhdf_readAdjacency( handle, 0, count, H5T_NATIVE_LONG, buffer, &status );
576     if (mhdf_isError(&status)) {
577       fprintf(stderr,"Internal error reading adjacency list for %s: %s\n",
578               desc->elems[i].handle, mhdf_message(&status));
579       free(ranges);
580       mhdf_closeData( file, handle, &status );
581       return 1;
582     }
583     mhdf_closeData( file, handle, &status );
584 
585     invalid += check_valid_adj_list( desc->elems[i].desc.start_id,
586                                      desc->elems[i].desc.count,
587                                      buffer, count,
588                                      ranges, num_ranges,
589                                      desc->elems[i].handle );
590     free(buffer);
591   }
592 
593   free(ranges);
594   return invalid;
595 }
596 
check_valid_adj_list(long start_id,long count,const long * data,long data_len,const long * valid_ranges,long num_valid_ranges,const char * name)597 static int check_valid_adj_list( long start_id, long count, const long* data, long data_len,
598                                  const long* valid_ranges, long num_valid_ranges, const char* name )
599 {
600   long i, n, id, invalid_id = 0, invalid_vals = 0;
601   const long* iter = data;
602   const long* const end = data + data_len;
603   int result = 0;
604 
605   for (i = 0; iter != end; ++i) {
606     id = *iter; ++iter;
607     if (iter == end) {
608       printf("Entry %ld in %s adjacency data (ID %ld) is truncated by the end of the adjacency list.\n",
609           i, name, id);
610       result = 1;
611       break;
612     }
613 
614     if (id < start_id || (id - start_id) >= count) {
615       if (verbose)
616         printf("Entry %ld in %s adjacency data has ID %ld outside of group range [%ld,%ld].\n",
617           i, name, id, start_id, start_id+count-1);
618       ++invalid_id;
619     }
620 
621     n = *iter; ++iter;
622     if (n < 1) {
623       printf("Entry %ld in %s adjacency data (ID %ld) has %ld adjacency entries.\n",
624           i, name, id, n);
625       result = 1;
626       if (n < 0)
627         break; /* data is corrupt.  don't proceed further */
628     }
629 
630     if (iter + n > end) {
631       printf("Entry %ld in %s adjacency data (ID %ld) is truncated by the end of the adjacency list.\n",
632           i, name, id);
633       result = 1;
634       break;
635     }
636 
637     if (!ids_contained( iter, n, valid_ranges, num_valid_ranges)) {
638       if (verbose)
639         printf("Entry %ld in %s adjacency data (ID %ld) has invalid IDs in its adjacency list.\n",
640           i, name, id);
641 
642       ++invalid_vals;
643     }
644     iter += n;
645   }
646 
647   if (invalid_id) {
648     printf("%ld entries in %s adjacency data were for entities not in %s\n", invalid_id, name, name );
649     result = 1;
650   }
651   if (invalid_vals) {
652     printf("%ld entries in %s adjacency data contained invalid adjacent entity ids\n", invalid_id, name );
653     result = 1;
654   }
655   return result;
656 }
657 
658 
check_valid_sets(mhdf_FileHandle file,struct mhdf_FileDesc * desc)659 int check_valid_sets( mhdf_FileHandle file, struct mhdf_FileDesc* desc )
660 {
661   long count, start_id, data_len;
662   mhdf_Status status;
663   hid_t meta, handle;
664   int result = 0;
665 
666   if (desc->sets.count == 0)
667     return 0;
668 
669   meta = mhdf_openSetMeta( file, &count, &start_id, &status );
670   if (mhdf_isError(&status)) {
671     fprintf(stderr,"Internal error opening set description table: %s\n", mhdf_message(&status));
672     return 1;
673   }
674 
675   if (desc->have_set_contents) {
676     handle = mhdf_openSetData( file, &data_len, &status );
677     if (mhdf_isError(&status)) {
678       fprintf(stderr,"Internal error opening set contents table: %s\n", mhdf_message(&status));
679     }
680     else {
681       result += check_valid_set_contents( desc, start_id, count, meta, handle, data_len );
682       mhdf_closeData( file, handle, &status );
683     }
684   }
685 
686   if (desc->have_set_children) {
687     handle = mhdf_openSetChildren( file, &data_len, &status );
688     if (mhdf_isError(&status)) {
689       fprintf(stderr,"Internal error opening set child table: %s\n", mhdf_message(&status));
690       mhdf_closeData( file, meta, &status );
691     }
692     else {
693       result += check_valid_parents_children( start_id, count, meta, handle, data_len, 0 );
694       mhdf_closeData( file, handle, &status );
695     }
696   }
697 
698   if (desc->have_set_parents) {
699     handle = mhdf_openSetParents( file, &data_len, &status );
700     if (mhdf_isError(&status)) {
701       fprintf(stderr,"Internal error opening set child table: %s\n", mhdf_message(&status));
702       mhdf_closeData( file, meta, &status );
703     }
704     else {
705       result += check_valid_parents_children( start_id, count, meta, handle, data_len, 1 );
706       mhdf_closeData( file, handle, &status );
707     }
708   }
709 
710   mhdf_closeData( file, meta, &status );
711   return result;
712 }
713 
contains_duplicates(long * array,long n)714 static int contains_duplicates( long* array, long n )
715 {
716   long i;
717   qsort( array, n, sizeof(long), &lcomp );
718   for (i = 1; i < n;  ++i)
719     if (array[i-1] == array[i])
720       return 1;
721   return 0;
722 }
723 
ranges_contain_duplicates(long * ranges,long nranges)724 static int ranges_contain_duplicates( long* ranges, long nranges )
725 {
726   long i;
727   qsort( ranges, nranges, 2*sizeof(long), &lcomp ); /* sort by first value in each pair */
728   for (i = 1; i < nranges;  ++i)
729     if (ranges[2*i-2] + ranges[2*i-1] > ranges[2*i])
730       return 1;
731   return 0;
732 }
733 
734 
check_valid_parents_children(long start_id,long count,hid_t meta_handle,hid_t data_handle,long data_len,int parents)735 static int check_valid_parents_children( long start_id,
736                                          long count,
737                                          hid_t meta_handle,
738                                          hid_t data_handle,
739                                          long data_len,
740                                          int parents )
741 {
742   mhdf_Status status;
743   long *indices, *contents;
744   const char parstr[] = "Parent";
745   const char cldstr[] = "Child";
746   const char* name = parents ? parstr : cldstr;
747   long i, prev, start, n;
748   long invalid = 0, invalid_dup = 0;
749   long range[2];
750   int result = 0;
751   range[0] = start_id;
752   range[1] = count;
753 
754   indices = malloc( sizeof(long) * count );
755   if (parents)
756     mhdf_readSetParentEndIndices( meta_handle, 0, count, H5T_NATIVE_LONG, indices, &status );
757   else
758     mhdf_readSetChildEndIndices( meta_handle, 0, count, H5T_NATIVE_LONG, indices, &status );
759   if (mhdf_isError(&status)) {
760     fprintf(stderr,"Internal error reading set %s end indices: %s\n", name, mhdf_message(&status));
761     free(indices);
762     return 1;
763   }
764 
765   if (check_valid_end_indices( indices, count, 0, start_id, data_len, "Set", name )) {
766     free(indices);
767     return 1;
768   }
769 
770   contents = malloc( sizeof(long) * data_len );
771   mhdf_readSetParentsChildren( data_handle, 0, data_len, H5T_NATIVE_LONG, contents, &status );
772   if (mhdf_isError(&status)) {
773     fprintf(stderr,"Internal error reading set %s IDs: %s\n", name, mhdf_message(&status));
774     free(indices);
775     free(contents);
776     return 1;
777   }
778 
779   prev = -1;
780   for (i = 0; i < count; ++i) {
781     start = prev + 1;
782     n = indices[i] - prev;
783     prev = indices[i];
784 
785     if (!ids_contained( contents+start, n, range, 1)) {
786       if (verbose)
787         printf("Set %ld (ID %ld) has invalid %s IDs.\n", i, start_id+i, name );
788       ++invalid;
789     }
790     else if (contains_duplicates( contents+start, n )) {
791       if (verbose)
792         printf("Set %ld (ID %ld) %s list contains duplicate IDs.\n", i, start_id+i, name );
793       ++invalid_dup;
794     }
795   }
796 
797   free(indices);
798   free(contents);
799 
800   if (invalid) {
801     printf( "%ld sets had invalid %s lists.\n", invalid, name );
802     result = 1;
803   }
804 
805   if (invalid_dup) {
806     printf( "%ld sets had duplicate handles in %s lists.\n", invalid_dup, name );
807     result = 1;
808   }
809 
810   return result;
811 }
812 
merge_ranges(long * ranges,int nranges)813 static int merge_ranges( long* ranges, int nranges )
814 {
815   long i, n;
816 
817   if (nranges < 1)
818     return 0;
819 
820     /* merge adjacent */
821   qsort( ranges, nranges, 2*sizeof(long), &lcomp );
822   n = 1;
823   for (i = 1; i < nranges; ++i) {
824     if (ranges[2*n-2] + ranges[2*n-1] == ranges[2*i]) {
825       ranges[2*n-1] += ranges[2*i+1]; /*compact the range*/
826     }
827     else {
828       /* do not compact, just copy, and increase number of ranges*/
829       ranges[2*n  ] = ranges[2*i];
830       ranges[2*n+1] = ranges[2*i+1];
831       ++n;
832     }
833   }
834 
835   return n;
836 }
837 
all_id_ranges(struct mhdf_FileDesc * desc,int include_null,int * num_ranges_out)838 static long* all_id_ranges( struct mhdf_FileDesc* desc, int include_null, int* num_ranges_out )
839 {
840   int i, num_ranges = 0;
841   struct mhdf_EntDesc* group;
842   long* ranges = malloc(2*sizeof(long)*(desc->num_elem_desc + 2 + !!include_null));
843   if (include_null) {
844     num_ranges = 1;
845     ranges[0] = 0;
846     ranges[1] = 1;
847   }
848   for (i = -1; i <= desc->num_elem_desc; ++i) {
849     if (i == -1)
850       group = &desc->nodes;
851     else if (i == desc->num_elem_desc)
852       group = &desc->sets;
853     else
854       group = &desc->elems[i].desc;
855 
856     if (num_ranges && ranges[2*num_ranges - 2] + ranges[2*num_ranges-1] == group->start_id) {
857       ranges[2*num_ranges-1] += group->count;
858     }
859     else {
860       ranges[2*num_ranges] = group->start_id;
861       ranges[2*num_ranges+1] = group->count;
862       ++num_ranges;
863     }
864   }
865 
866 
867   *num_ranges_out = merge_ranges( ranges, num_ranges );
868   return ranges;
869 }
870 
check_valid_set_contents(struct mhdf_FileDesc * desc,long start_id,long count,hid_t meta_handle,hid_t data_handle,long data_len)871 static int check_valid_set_contents( struct mhdf_FileDesc* desc,
872                                      long start_id,
873                                      long count,
874                                      hid_t meta_handle,
875                                      hid_t data_handle,
876                                      long data_len )
877 {
878   mhdf_Status status;
879   long *indices, *contents;
880   short* flags;
881   long i, prev, start, n;
882   long invalid_len = 0, invalid_handle = 0, invalid_dup = 0;
883   long *ranges;
884   int num_ranges;
885   int tmpresult;
886 
887   indices = malloc( sizeof(long) * count );
888   mhdf_readSetContentEndIndices( meta_handle, 0, count, H5T_NATIVE_LONG, indices, &status );
889   if (mhdf_isError(&status)) {
890     fprintf(stderr,"Internal error reading set content end indices: %s\n", mhdf_message(&status));
891     free(indices);
892     return 1;
893   }
894 
895   if (check_valid_end_indices( indices, count, 0, start_id, data_len, "Set", "Content" )) {
896     free(indices);
897     return 1;
898   }
899 
900   ranges = all_id_ranges( desc, 0, &num_ranges );
901 
902   flags = malloc( sizeof(short) * count );
903   mhdf_readSetFlags( meta_handle, 0, count, H5T_NATIVE_SHORT, flags, &status );
904   if (mhdf_isError(&status)) {
905     fprintf(stderr,"Internal error reading set flags: %s\n", mhdf_message(&status));
906     free(indices);
907     free(flags);
908     free(ranges);
909     return 1;
910   }
911 
912   contents = malloc( sizeof(long) * data_len );
913   mhdf_readSetData( data_handle, 0, data_len, H5T_NATIVE_LONG, contents, &status );
914   if (mhdf_isError(&status)) {
915     fprintf(stderr,"Internal error reading set content IDs: %s\n", mhdf_message(&status));
916     free(indices);
917     free(contents);
918     free(flags);
919     free(ranges);
920     return 1;
921   }
922 
923   prev = -1;
924   for (i = 0; i < count; ++i) {
925     start = prev + 1;
926     n = indices[i] - prev;
927     prev = indices[i];
928 
929     if (flags[i] & mhdf_SET_RANGE_BIT) {
930       if (n%2) {
931         if (verbose)
932           printf("Set %ld (ID %ld) has is marked as range-compressed but has odd number of content values.\n", i, start_id+i );
933         ++invalid_len;
934         continue;
935       }
936       tmpresult = ranges_contained( contents+start, n/2, ranges, num_ranges );
937     }
938     else {
939       tmpresult = ids_contained( contents+start, n, ranges, num_ranges);
940     }
941     if (!tmpresult) {
942       if (verbose)
943         printf("Set %ld (ID %ld) has invalid content IDs.\n", i, start_id+i );
944       ++invalid_handle;
945       continue;
946     }
947 
948     if (flags[i] & mhdf_SET_ORDER_BIT)
949       continue;
950 
951     if (flags[i] & mhdf_SET_RANGE_BIT)
952       tmpresult = ranges_contain_duplicates( contents+start, n/2 );
953     else
954       tmpresult = contains_duplicates( contents+start, n );
955     if(tmpresult) {
956       if (verbose)
957         printf("Set %ld (ID %ld) is not ordered but contains duplicate handles.\n", i, start_id+i );
958       ++invalid_dup;
959     }
960   }
961 
962   free(indices);
963   free(contents);
964   free(flags);
965   free(ranges);
966 
967   tmpresult = 0;
968   if (invalid_len) {
969     printf( "%ld ranged sets had invalid (odd) content list lengths.\n", invalid_len );
970     tmpresult = 1;
971   }
972   if (invalid_handle) {
973     printf( "%ld sets had invalid IDs in their content lists.\n", invalid_handle );
974     tmpresult = 1;
975   }
976   if (invalid_dup) {
977     printf( "%ld unordered sets had duplicate IDs in their content lists.\n", invalid_dup );
978     tmpresult = 1;
979   }
980 
981   return tmpresult;
982 }
983 
check_valid_tags(mhdf_FileHandle file,struct mhdf_FileDesc * desc)984 int check_valid_tags( mhdf_FileHandle file, struct mhdf_FileDesc* desc )
985 {
986   int i, result = 0;
987   for (i = 0; i < desc->num_tag_desc; ++i) {
988     if (desc->tags[i].size < 0)
989       result += check_valid_var_len_tag( i, file, desc );
990     else
991       result += check_valid_tag( i, file, desc );
992   }
993   return result;
994 }
995 
check_valid_tag(int tag_idx,mhdf_FileHandle file,struct mhdf_FileDesc * desc)996 static int check_valid_tag( int tag_idx, mhdf_FileHandle file, struct mhdf_FileDesc* desc )
997 {
998   long *ids = 0, count, junk;
999   long *ranges;
1000   int nranges;
1001   hid_t handles[3];
1002   mhdf_Status status;
1003   const struct mhdf_TagDesc* tag = &(desc->tags[tag_idx]);
1004   int i, result = 0;
1005   long srange[2]={0,0};
1006   const char* name;
1007   struct mhdf_EntDesc* group;
1008   hid_t h5type;
1009   hsize_t size;
1010 
1011 
1012   if (tag->have_sparse) {
1013     mhdf_openSparseTagData( file, tag->name, &count, &junk, handles, &status );
1014     if (mhdf_isError(&status)) {
1015       fprintf(stderr,"Internal error opening sparse data for tag \"%s\": %s\n", tag->name, mhdf_message(&status));
1016       return 1;
1017     }
1018 
1019     ids = malloc( sizeof(long) * count );
1020     mhdf_readSparseTagEntities( handles[0], 0, count, H5T_NATIVE_LONG, ids, &status );
1021     if (mhdf_isError(&status)) {
1022       fprintf(stderr,"Internal error reading sparse entities for tag \"%s\": %s\n", tag->name, mhdf_message(&status));
1023       mhdf_closeData( file, handles[0], &status );
1024       mhdf_closeData( file, handles[1], &status );
1025       free(ids);
1026       return 1;
1027     }
1028 
1029     mhdf_closeData( file, handles[0], &status );
1030     ranges = all_id_ranges( desc, 0, &nranges );
1031     if (!ids_contained( ids, count, ranges, nranges )) {
1032       ++result;
1033       printf("Sparse data for tag \"%s\" has values for invalid IDs\n", tag->name );
1034     }
1035     else if (contains_duplicates( ids, count )) {
1036       ++result;
1037       printf("Sparse data for tag \"%s\" has duplicate values for one or more entities\n", tag->name );
1038     }
1039     free(ranges);
1040 
1041     for (i = 0; i < tag->num_dense_indices; ++i) {
1042       if (tag->dense_elem_indices[i] == -2) {
1043         name = mhdf_set_type_handle();
1044         group = &desc->sets;
1045       }
1046       else if (tag->dense_elem_indices[i] == -1) {
1047         name = mhdf_node_type_handle();
1048         group = &desc->nodes;
1049       }
1050       else {
1051         name = desc->elems[ tag->dense_elem_indices[i] ].handle;
1052         group = &desc->elems[ tag->dense_elem_indices[i] ].desc;
1053       }
1054 
1055       srange[0] = group->start_id;
1056       srange[1] = group->count;
1057       if (ids_contained( ids, count, srange, 2 )) {
1058         ++result;
1059         printf("Tag \"%s\" has both sparse values and dense values for one or more entities in \"%s\"\n", tag->name, name );
1060       }
1061     }
1062 
1063     free(ids);
1064   }
1065 
1066 
1067   if (tag->type != mhdf_ENTITY_ID) {
1068     if (tag->have_sparse)
1069       mhdf_closeData( file, handles[1], &status );
1070     return result;
1071   }
1072 
1073   ranges = all_id_ranges( desc, 1, &nranges );
1074   if (tag->default_value && !ids_contained( tag->default_value, tag->size, ranges, nranges )) {
1075     ++result;
1076     printf("Handle tag \"%s\" has invalid ID(s) in its default value.\n", tag->name );
1077   }
1078   if (tag->global_value && !ids_contained( tag->global_value, tag->size, ranges, nranges )) {
1079     ++result;
1080     printf("Handle tag \"%s\" has invalid ID(s) in its global/mesh value.\n", tag->name );
1081   }
1082 
1083   h5type = H5T_NATIVE_LONG;
1084   if (tag->size > 1) {
1085     size = tag->size;
1086 #if defined(H5Tarray_create_vers) && H5Tarray_create_vers > 1
1087     h5type = H5Tarray_create( H5T_NATIVE_LONG, 1, &size );
1088 #else
1089     h5type = H5Tarray_create( H5T_NATIVE_LONG, 1, &size, NULL );
1090 #endif
1091   }
1092 
1093   if (tag->have_sparse) {
1094     ids = malloc( tag->size * count * sizeof(long) );
1095     mhdf_readTagValues( handles[1], 0, count, h5type, ids, &status );
1096     if (mhdf_isError(&status)) {
1097       fprintf(stderr,"Internal error reading sparse values for handle tag \"%s\": %s\n", tag->name, mhdf_message(&status));
1098       mhdf_closeData( file, handles[1], &status );
1099       free(ids);
1100       free(ranges);
1101       if (tag->size > 1)
1102         H5Tclose( h5type );
1103       return 1;
1104     }
1105     mhdf_closeData( file, handles[1], &status );
1106 
1107     if (!ids_contained( ids, tag->size * count, ranges, nranges )) {
1108       ++result;
1109       printf("Sparse data for one or more entities with handle tag \"%s\" has invalid ID(s).\n", tag->name );
1110     }
1111     free(ids);
1112   }
1113 
1114   for (i = 0; i < tag->num_dense_indices; ++i) {
1115     if (tag->dense_elem_indices[i] == -2) {
1116       name = mhdf_set_type_handle();
1117       /*group = &desc->sets;*/
1118     }
1119     else if (tag->dense_elem_indices[i] == -1) {
1120       name = mhdf_node_type_handle();
1121       /*group = &desc->nodes;*/
1122     }
1123     else {
1124       name = desc->elems[ tag->dense_elem_indices[i] ].handle;
1125       /*group = &desc->elems[ tag->dense_elem_indices[i] ].desc;*/
1126     }
1127 
1128     handles[0] = mhdf_openDenseTagData( file, tag->name, name, &count, &status );
1129     if (mhdf_isError(&status)) {
1130       fprintf(stderr,"Internal dense values for handle tag \"%s\" on \"%s\": %s\n", tag->name, name, mhdf_message(&status));
1131       ++result;
1132       continue;
1133     }
1134 
1135     ids = malloc( tag->size * count * sizeof(long) );
1136     mhdf_readTagValues( handles[0], 0, count, h5type, ids, &status );
1137     if (mhdf_isError(&status)) {
1138       fprintf(stderr,"Internal error reading dense values for handle tag \"%s\" on \"%s\": %s\n", tag->name, name, mhdf_message(&status));
1139       mhdf_closeData( file, handles[0], &status );
1140       free(ids);
1141       ++result;
1142       continue;
1143     }
1144     mhdf_closeData( file, handles[1], &status );
1145 
1146     if (!ids_contained( ids, count, ranges, nranges )) {
1147       ++result;
1148       printf("Dense data on \"%s\" for handle tag \"%s\" has invalid ID(s) for one or more entities.\n", name, tag->name );
1149     }
1150     free(ids);
1151   }
1152 
1153   if (tag->size > 1)
1154     H5Tclose( h5type );
1155 
1156   return result;
1157 }
1158 
check_valid_var_len_tag(int tag_idx,mhdf_FileHandle file,struct mhdf_FileDesc * desc)1159 static int check_valid_var_len_tag( int tag_idx, mhdf_FileHandle file, struct mhdf_FileDesc* desc )
1160 {
1161   long *ids = 0, count, num_val;
1162   long *ranges;
1163   int nranges;
1164   hid_t handles[3];
1165   mhdf_Status status;
1166   const struct mhdf_TagDesc* tag = &(desc->tags[tag_idx]);
1167   int result = 0;
1168 
1169   if (tag->num_dense_indices != 0) {
1170     printf("Dense data for tag \"%s\" not allowed for variable-length tags\n", tag->name);
1171     ++result;
1172   }
1173 
1174   if (tag->have_sparse) {
1175     mhdf_openSparseTagData( file, tag->name, &count, &num_val, handles, &status );
1176     if (mhdf_isError(&status)) {
1177       fprintf(stderr,"Internal error opening sparse data for tag \"%s\": %s\n", tag->name, mhdf_message(&status));
1178       return 1;
1179     }
1180 
1181     ids = malloc( sizeof(long) * count );
1182     mhdf_readSparseTagEntities( handles[0], 0, count, H5T_NATIVE_LONG, ids, &status );
1183     if (mhdf_isError(&status)) {
1184       fprintf(stderr,"Internal error reading sparse entities for tag \"%s\": %s\n", tag->name, mhdf_message(&status));
1185       mhdf_closeData( file, handles[0], &status );
1186       mhdf_closeData( file, handles[1], &status );
1187       mhdf_closeData( file, handles[2], &status );
1188       free(ids);
1189       return 1;
1190     }
1191 
1192     mhdf_closeData( file, handles[0], &status );
1193     ranges = all_id_ranges( desc, 0, &nranges );
1194     if (!ids_contained( ids, count, ranges, nranges )) {
1195       ++result;
1196       printf("Sparse data for tag \"%s\" has values for invalid IDs\n", tag->name );
1197     }
1198     else if (contains_duplicates( ids, count )) {
1199       ++result;
1200       printf("Sparse data for tag \"%s\" has duplicate values for one or more entities\n", tag->name );
1201     }
1202     free(ranges);
1203 
1204     mhdf_readSparseTagIndices( handles[2], 0, count, H5T_NATIVE_LONG, ids, &status );
1205     if (mhdf_isError(&status)) {
1206       fprintf(stderr,"Internal error reading indices for variable length tag \"%s\": %s\n", tag->name, mhdf_message(&status));
1207       mhdf_closeData( file, handles[1], &status );
1208       mhdf_closeData( file, handles[2], &status );
1209       free(ids);
1210       return 1;
1211     }
1212     mhdf_closeData( file, handles[2], &status );
1213 
1214     if (check_valid_end_indices( ids, count, 1, 0, num_val, "Variable-length tag", tag->name ))
1215       ++result;
1216     free(ids);
1217   }
1218 
1219 
1220   if (tag->type != mhdf_ENTITY_ID) {
1221     if (tag->have_sparse)
1222       mhdf_closeData( file, handles[1], &status );
1223     return result;
1224   }
1225 
1226   ranges = all_id_ranges( desc, 1, &nranges );
1227   if (tag->default_value && !ids_contained( tag->default_value, tag->default_value_size, ranges, nranges )) {
1228     ++result;
1229     printf("Handle tag \"%s\" has invalid ID(s) in its default value.\n", tag->name );
1230   }
1231   if (tag->global_value && !ids_contained( tag->global_value, tag->global_value_size, ranges, nranges )) {
1232     ++result;
1233     printf("Handle tag \"%s\" has invalid ID(s) in its global/mesh value.\n", tag->name );
1234   }
1235 
1236   if (tag->have_sparse) {
1237     ids = malloc( num_val * sizeof(long) );
1238     mhdf_readTagValues( handles[1], 0, num_val, H5T_NATIVE_LONG, ids, &status );
1239     if (mhdf_isError(&status)) {
1240       fprintf(stderr,"Internal error reading values for variable-length handle tag \"%s\": %s\n", tag->name, mhdf_message(&status));
1241       mhdf_closeData( file, handles[1], &status );
1242       free(ids);
1243       free(ranges);
1244       return 1;
1245     }
1246     mhdf_closeData( file, handles[1], &status );
1247 
1248     if (!ids_contained( ids, tag->size * count, ranges, nranges )) {
1249       ++result;
1250       printf("Data for one or more entities with variable-length handle tag \"%s\" has invalid ID(s).\n", tag->name );
1251     }
1252     free(ids);
1253   }
1254 
1255   return result;
1256 }
1257