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