1 /* This is part of the netCDF package.
2    Copyright 2018 University Corporation for Atmospheric Research/Unidata
3    See COPYRIGHT file for conditions of use.
4 
5    Test HDF5 file code. These are not intended to be exhaustive tests,
6    but they use HDF5 the same way that netCDF-4 does, so if these
7    tests don't work, than netCDF-4 won't work either.
8 
9    $Id: tst_h_dimscales.c,v 1.15 2010/06/01 15:34:51 ed Exp $
10 */
11 #include "h5_err_macros.h"
12 #include <hdf5.h>
13 #include <H5DSpublic.h>
14 
15 #define FILE_NAME "tst_h_dimscales.h5"
16 #define STR_LEN 255
17 #define MAX_DIMS 255
18 
alien_visitor(hid_t did,unsigned dim,hid_t dsid,void * visitor_data)19 herr_t alien_visitor(hid_t did, unsigned dim, hid_t dsid,
20 		     void *visitor_data)
21 {
22    char name1[STR_LEN], name2[STR_LEN];
23    H5G_stat_t statbuf;
24 
25    (*(hid_t *)visitor_data) = dsid;
26    if (H5Iget_name(did, name1, STR_LEN) < 0) ERR;
27    if (H5Iget_name(dsid, name2, STR_LEN) < 0) ERR;
28 /*    printf("visiting did 0x%x dim %d dsid 0x%x name of did %s \n",  */
29 /* 	  did, dim, dsid, name1); */
30 /*    printf("name of dsid: %s\n", name2); */
31 
32    if (H5Gget_objinfo(did, ".", 1, &statbuf) < 0) ERR;
33 /*   printf("statbuf.fileno = %d statbuf.objno = %d\n",
34      statbuf.fileno, statbuf.objno);*/
35 
36    return 0;
37 }
38 
39 int
rec_scan_group(hid_t grpid)40 rec_scan_group(hid_t grpid)
41 {
42    hid_t spaceid, datasetid = 0, child_grpid;
43    hsize_t num_obj, i;
44    int obj_class;
45    char obj_name[STR_LEN + 1];
46    htri_t is_scale;
47    int num_scales;
48    hsize_t dims[MAX_DIMS], max_dims[MAX_DIMS];
49    int ndims, d;
50 
51    /* Loop through datasets to find variables. */
52    if (H5Gget_num_objs(grpid, &num_obj) < 0) ERR;
53    for (i=0; i<num_obj; i++)
54    {
55       /* Get the type (i.e. group, dataset, etc.), and the name of
56        * the object. */
57       if ((obj_class = H5Gget_objtype_by_idx(grpid, i)) < 0) ERR;
58       if (H5Gget_objname_by_idx(grpid, i, obj_name, STR_LEN) < 0) ERR;
59       /*printf("\nEncountered: HDF5 object obj_class %d obj_name %s\n",
60 	obj_class, obj_name);*/
61 
62       /* Deal with groups and datasets, ignore the rest. */
63       switch(obj_class)
64       {
65 	 case H5G_GROUP:
66 	    if ((child_grpid = H5Gopen(grpid, obj_name)) < 0) ERR;
67 	    if (rec_scan_group(child_grpid)) ERR;
68 	    break;
69 	 case H5G_DATASET:
70 	    /* Open the dataset. */
71 	    if ((datasetid = H5Dopen(grpid, obj_name)) < 0) ERR;
72 	    /*printf("\nobj_name %s\n", obj_name);*/
73 
74 	    /* Get the dimensions of this dataset. */
75 	    if ((spaceid = H5Dget_space(datasetid)) < 0) ERR;
76 	    if ((ndims = H5Sget_simple_extent_ndims(spaceid)) < 0) ERR;
77 	    if (ndims > MAX_DIMS) ERR;
78 	    if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0) ERR;
79 
80 	    /* Is this a dimscale? */
81 	    if ((is_scale = H5DSis_scale(datasetid)) < 0) ERR;
82 	    if (is_scale)
83 	    {
84 	       /*printf("dimension scale! Hoorah for the Pirate King!\n");*/
85 	    }
86 	    else
87 	    {
88 		hid_t visitor_data = 0;
89 
90 	       /* Here's how to get the number of scales attached
91 		* to the dataset's dimension 0. */
92 	       if ((num_scales = H5DSget_num_scales(datasetid, 0)) < 0) ERR;
93 	       if (num_scales != 1) ERR;
94 
95 	       /* Go through all dimscales for this var and learn about them. */
96 	       for (d = 0; d < ndims; d++)
97 		  if (H5DSiterate_scales(datasetid, d, NULL, alien_visitor,
98 					 &visitor_data) < 0) ERR;
99 	    }
100 	    if (H5Dclose(datasetid) < 0) ERR;
101 	    break;
102 	 case H5G_TYPE:
103 	    printf("found a type!\n");
104 	    break;
105 	 case H5G_LINK:
106 	    printf("found a link! Yikes!\n");
107 	    break;
108 	 default:
109 	    printf("Unknown object class %d!", obj_class);
110       }
111    }
112 
113    return 0;
114 }
115 
116 int
main()117 main()
118 {
119    printf("\n*** Checking HDF5 dimension scales.\n");
120 #define GRP_NAME "simple_scales"
121 #define DIMSCALE_NAME "dimscale"
122 #define NAME_ATTRIBUTE "Billy-Bob"
123 #define VAR1_NAME "var1"
124 #define VAR2_NAME "var2"
125 #define VAR3_NAME "var3"
126 #define DIM1_LEN 3
127 #define DIM2_LEN 2
128 #define FIFTIES_SONG "Mamma said they'll be days like this. They'll be days like this, my mamma said."
129 
130    printf("*** Creating simple dimension scales file...");
131    {
132       hid_t fileid, grpid, dimscaleid;
133       hid_t dimscale_spaceid, var1_spaceid, var3_spaceid;
134       hid_t var1_datasetid, var2_datasetid, var3_datasetid;
135       hsize_t dims[2] = {DIM1_LEN, DIM2_LEN};
136       hsize_t dimscale_dims[1] = {DIM1_LEN};
137 
138       /* Open file and create group. */
139       if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT,
140 			      H5P_DEFAULT)) < 0) ERR;
141       if ((grpid = H5Gcreate(fileid, GRP_NAME, 0)) < 0) ERR;
142 
143       /* Create our dimension scale. Use the built-in NAME attribute
144        * on the dimscale. */
145       if ((dimscale_spaceid = H5Screate_simple(1, dimscale_dims,
146 					       dimscale_dims)) < 0) ERR;
147       if ((dimscaleid = H5Dcreate(grpid, DIMSCALE_NAME, H5T_NATIVE_INT,
148 				  dimscale_spaceid, H5P_DEFAULT)) < 0) ERR;
149       if (H5DSset_scale(dimscaleid, NAME_ATTRIBUTE) < 0) ERR;
150 
151       /* Create a 1D variable which uses the dimscale. Attach a label
152        * to this scale. */
153       if ((var1_spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;
154       if ((var1_datasetid = H5Dcreate(grpid, VAR1_NAME, H5T_NATIVE_INT,
155 				      var1_spaceid, H5P_DEFAULT)) < 0) ERR;
156       if (H5DSattach_scale(var1_datasetid, dimscaleid, 0) < 0) ERR;
157       if (H5DSset_label(var1_datasetid, 0, FIFTIES_SONG) < 0) ERR;
158 
159       /* Create a 1D variabls that doesn't use the dimension scale. */
160       if ((var2_datasetid = H5Dcreate(grpid, VAR2_NAME, H5T_NATIVE_INT,
161 				      var1_spaceid, H5P_DEFAULT)) < 0) ERR;
162 
163       /* Create a 2D dataset which uses the scale for one of its
164        * dimensions. */
165       if ((var3_spaceid = H5Screate_simple(2, dims, dims)) < 0) ERR;
166       if ((var3_datasetid = H5Dcreate(grpid, VAR3_NAME, H5T_NATIVE_INT,
167 				      var3_spaceid, H5P_DEFAULT)) < 0) ERR;
168       if (H5DSattach_scale(var3_datasetid, dimscaleid, 0) < 0) ERR;
169 
170       /* Close up the shop. */
171       if (H5Dclose(dimscaleid) < 0 ||
172 	  H5Dclose(var1_datasetid) < 0 ||
173 	  H5Dclose(var2_datasetid) < 0 ||
174 	  H5Dclose(var3_datasetid) < 0 ||
175 	  H5Sclose(var1_spaceid) < 0 ||
176 	  H5Sclose(var3_spaceid) < 0 ||
177 	  H5Sclose(dimscale_spaceid) < 0 ||
178 	  H5Gclose(grpid) < 0 ||
179 	  H5Fclose(fileid) < 0) ERR;
180 
181       /* HELP! If you are reading this in the future, and time
182        * machines have been invented, please come back to July 10,
183        * 2005, the Java Java coffee shop in Lafayette, 8:00 am MST +-
184        * 20 minutes. Bring back some advanced weapons systems to
185        * destroy the sound system here, which is playing 50's rock and
186        * roll. Do-op, do-op, la-ma la-ma, ding dong. Save me!!! (Mind
187        * you, James Brown is a different story!) */
188    }
189    SUMMARIZE_ERR;
190    printf("*** Checking that simple dimscale file can be read...");
191    {
192       hid_t fileid, grpid, datasetid = 0;
193       hsize_t num_obj, i;
194       int obj_class;
195       char obj_name[STR_LEN + 1];
196       htri_t is_scale;
197       int num_scales;
198 
199       /* Reopen the file and group. */
200       if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDWR, H5P_DEFAULT)) < 0) ERR;
201       if ((grpid = H5Gopen(fileid, GRP_NAME)) < 0) ERR;
202 
203       /* Loop through datasets to find variables. */
204       if (H5Gget_num_objs(grpid, &num_obj) < 0) ERR;
205       for (i=0; i<num_obj; i++)
206       {
207 	 /* Get the type (i.e. group, dataset, etc.), and the name of the
208 	  * object. Confusingly, this is a different type than the type
209 	  * of a variable. This type might be better called "class" or
210 	  * "type of type"  */
211 	 if ((obj_class = H5Gget_objtype_by_idx(grpid, i)) < 0) ERR;
212 	 if (H5Gget_objname_by_idx(grpid, i, obj_name, STR_LEN) < 0) ERR;
213 	 /*printf("\nEncountered: HDF5 object obj_class %d obj_name %s\n", obj_class, obj_name);*/
214 
215 	 /* Deal with groups and datasets. */
216 	 switch(obj_class)
217 	 {
218 	    case H5G_GROUP:
219 	       break;
220 	    case H5G_DATASET:
221 
222 	       /*Close the last datasetid, if one is open. */
223 	       if (datasetid > 0)
224 	       {
225 		  H5Dclose(datasetid);
226 	       }
227 
228 	       if ((datasetid = H5Dopen(grpid, obj_name)) < 0) ERR;
229 	       if ((is_scale = H5DSis_scale(datasetid)) < 0) ERR;
230 	       if (is_scale && strcmp(obj_name, DIMSCALE_NAME)) ERR;
231 	       if (is_scale)
232 	       {
233 		  char nom_de_quincey[STR_LEN+1];
234 
235 		  /* A dimscale comes with a NAME attribute, in
236 		   * addition to its real name. */
237 		  if (H5DSget_scale_name(datasetid, nom_de_quincey,
238 					 STR_LEN) < 0) ERR;
239 		  if (strcmp(nom_de_quincey, NAME_ATTRIBUTE)) ERR;
240 
241 		  /*printf("found scale %s, NAME %s\n", obj_name, nom_de_quincey);*/
242 
243 	       }
244 	       else
245 	       {
246 		  char label[STR_LEN+1];
247 
248 		  /* Here's how to get the number of scales attached
249 		   * to the dataset. I would think that this would
250 		   * return 0 scales for a dataset that doesn't have
251 		   * scales, but instead it errors. So take an error
252 		   * to be the same as no dimension scales. */
253 		  num_scales = H5DSget_num_scales(datasetid, 0);
254 		  if (strcmp(obj_name, VAR1_NAME) == 0 && num_scales != 1) ERR;
255 		  if (strcmp(obj_name, VAR2_NAME) == 0 && num_scales > 0) ERR;
256 		  if (strcmp(obj_name, VAR3_NAME) == 0 && num_scales != 1) ERR;
257 
258 		  /* There's also a label for dimension 0 of var1. */
259 		  if (strcmp(obj_name, VAR1_NAME) == 0)
260 		  {
261 		     if (H5DSget_label(datasetid, 0, label, STR_LEN) < 0) ERR;
262 		     if (strcmp(label, FIFTIES_SONG)) ERR;
263 		  }
264 	       }
265 	       break;
266 	    case H5G_TYPE:
267 	       break;
268 	    case H5G_LINK:
269 	       break;
270 	    default:
271 	       printf("Unknown object class %d!", obj_class);
272 	 }
273       }
274 
275       /* Close up the shop. */
276       if (H5Dclose(datasetid) < 0 ||
277 	  H5Gclose(grpid) < 0 ||
278 	  H5Fclose(fileid) < 0) ERR;
279    }
280 
281    SUMMARIZE_ERR;
282    printf("*** Creating simple dimension scales file with lots of datasets...");
283 
284 #define NUM_DATASETS 500
285    {
286       hid_t fileid, grpid, dimscaleid;
287       hid_t dimscale_spaceid, var1_spaceid;
288       hid_t var1_datasetid[NUM_DATASETS];
289       hsize_t dims[2] = {DIM1_LEN, DIM2_LEN};
290       hsize_t dimscale_dims[1] = {DIM1_LEN};
291       char var_name[STR_LEN + 1];
292       int v;
293 
294       /* Open file and create group. */
295       if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT,
296 			      H5P_DEFAULT)) < 0) ERR;
297       if ((grpid = H5Gcreate(fileid, GRP_NAME, 0)) < 0) ERR;
298 
299       /* Create our dimension scale. Use the built-in NAME attribute
300        * on the dimscale. */
301       if ((dimscale_spaceid = H5Screate_simple(1, dimscale_dims,
302 					       dimscale_dims)) < 0) ERR;
303       if ((dimscaleid = H5Dcreate(grpid, DIMSCALE_NAME, H5T_NATIVE_INT,
304 				  dimscale_spaceid, H5P_DEFAULT)) < 0) ERR;
305       if (H5DSset_scale(dimscaleid, NAME_ATTRIBUTE) < 0) ERR;
306 
307       /* Create many 1D datasets which use the dimscale. */
308       if ((var1_spaceid = H5Screate_simple(1, dims, dims)) < 0) ERR;
309       for (v = 0; v < NUM_DATASETS; v++)
310       {
311 	 sprintf(var_name, "var_%d", v);
312 	 if ((var1_datasetid[v] = H5Dcreate(grpid, var_name, H5T_NATIVE_INT,
313 					    var1_spaceid, H5P_DEFAULT)) < 0) ERR;
314 	 if (H5DSattach_scale(var1_datasetid[v], dimscaleid, 0) < 0) ERR;
315       }
316 
317       /* Close up the shop. */
318       for (v = 0; v < NUM_DATASETS; v++)
319 	 if (H5Dclose(var1_datasetid[v]) < 0) ERR;
320       if (H5Dclose(dimscaleid) < 0 ||
321 	  H5Sclose(var1_spaceid) < 0 ||
322 	  H5Sclose(dimscale_spaceid) < 0 ||
323 	  H5Gclose(grpid) < 0 ||
324 	  H5Fclose(fileid) < 0) ERR;
325    }
326 
327    SUMMARIZE_ERR;
328    printf("*** Creating a file with an unlimited dimension scale...");
329 
330    {
331       hid_t fileid, grpid, spaceid, datasetid, dimscaleid, cparmsid;
332       hsize_t dims[1] = {1}, maxdims[1] = {H5S_UNLIMITED};
333 
334       /* Create file and group. */
335       if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT,
336 			      H5P_DEFAULT)) < 0) ERR;
337       if ((grpid = H5Gcreate(fileid, GRP_NAME, 0)) < 0) ERR;
338 
339       if ((spaceid = H5Screate_simple(1, dims, maxdims)) < 0) ERR;
340 
341       /* Modify dataset creation properties, i.e. enable chunking  */
342       if ((cparmsid = H5Pcreate(H5P_DATASET_CREATE)) < 0) ERR;
343       if (H5Pset_chunk(cparmsid, 1, dims) < 0) ERR;
344 
345       /* Create our dimension scale, as an unlimited dataset. */
346       if ((dimscaleid = H5Dcreate(grpid, DIMSCALE_NAME, H5T_NATIVE_INT,
347 				  spaceid, cparmsid)) < 0) ERR;
348       if (H5DSset_scale(dimscaleid, NAME_ATTRIBUTE) < 0) ERR;
349 
350       /* Create a variable which uses it. */
351       if ((datasetid = H5Dcreate(grpid, VAR1_NAME, H5T_NATIVE_INT,
352 				 spaceid, cparmsid)) < 0) ERR;
353       if (H5DSattach_scale(datasetid, dimscaleid, 0) < 0) ERR;
354       if (H5DSset_label(datasetid, 0, "dimension label") < 0) ERR;
355 
356       /* Close up the shop. */
357       if (H5Dclose(dimscaleid) < 0 ||
358 	  H5Dclose(datasetid) < 0 ||
359 	  H5Sclose(spaceid) < 0 ||
360 	  H5Gclose(grpid) < 0 ||
361 	  H5Fclose(fileid) < 0) ERR;
362    }
363 
364    SUMMARIZE_ERR;
365    printf("*** Checking that unlimited dimscale file can be read...");
366 
367    {
368       hid_t fileid, grpid, spaceid = 0, datasetid = 0;
369       hsize_t volatile num_obj, i;
370       int obj_class;
371       char obj_name[STR_LEN + 1];
372       htri_t is_scale;
373       int num_scales;
374       hsize_t dims[1], maxdims[1];
375 
376       /* Reopen the file and group. */
377       if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDWR, H5P_DEFAULT)) < 0) ERR;
378       if ((grpid = H5Gopen(fileid, GRP_NAME)) < 0) ERR;
379 
380       /* Loop through datasets to find variables. */
381       if (H5Gget_num_objs(grpid, (hsize_t *)&num_obj) < 0) ERR;
382       for (i=0; i<num_obj; i++)
383       {
384 	 /* Get the type (i.e. group, dataset, etc.), and the name of
385 	  * the object. */
386 	 if ((obj_class = H5Gget_objtype_by_idx(grpid, i)) < 0) ERR;
387 	 if (H5Gget_objname_by_idx(grpid, i, obj_name, STR_LEN) < 0) ERR;
388 	 /*printf("\nEncountered: HDF5 object obj_class %d obj_name %s\n", obj_class, obj_name);*/
389 
390 	 /* Deal with groups and datasets. */
391 	 switch(obj_class)
392 	 {
393 	    case H5G_GROUP:
394 	       break;
395 	    case H5G_DATASET:
396 
397 	       /*Close the last datasetid, if one is open. */
398 	       if (datasetid > 0)
399 	       {
400 		  H5Dclose(datasetid);
401 		  datasetid = 0;
402 	       }
403 
404 	       /* Open the dataset. */
405 	       if ((datasetid = H5Dopen(grpid, obj_name)) < 0) ERR;
406 
407 	       /* This should be an unlimited dataset. */
408 	       if ((spaceid = H5Dget_space(datasetid)) < 0) ERR;
409 	       if (H5Sget_simple_extent_dims(spaceid, dims, maxdims) < 0) ERR;
410 	       if (maxdims[0] != H5S_UNLIMITED) ERR;
411 
412 	       /* Is this a dimscale? */
413 	       if ((is_scale = H5DSis_scale(datasetid)) < 0) ERR;
414 	       if (is_scale && strcmp(obj_name, DIMSCALE_NAME)) ERR;
415 	       if (is_scale)
416 	       {
417 		  char nom_de_quincey[STR_LEN+1];
418 
419 		  /* A dimscale comes with a NAME attribute, in
420 		   * addition to its real name. */
421 		  if (H5DSget_scale_name(datasetid, nom_de_quincey, STR_LEN) < 0) ERR;
422 		  /*printf("found scale %s, NAME %s\n", obj_name, nom_de_quincey);*/
423 
424 	       }
425 	       else
426 	       {
427 		  char label[STR_LEN+1];
428 		  hid_t visitor_data = 0;
429 
430 		  /* Here's how to get the number of scales attached
431 		   * to the dataset's dimension 0. */
432 		  if ((num_scales = H5DSget_num_scales(datasetid, 0)) < 0) ERR;
433 		  if (num_scales != 1) ERR;
434 
435 		  /* Go through all dimscales for this var and learn about them. */
436 		  if (H5DSiterate_scales(datasetid, 0, NULL, alien_visitor,
437 					 &visitor_data) < 0) ERR;
438 
439 		  /* There's also a label for dimension 0. */
440 		  if (H5DSget_label(datasetid, 0, label, STR_LEN) < 0) ERR;
441 
442 		  /*printf("found non-scale dataset %s, label %s\n", obj_name, label);*/
443 	       }
444 	       break;
445 	    case H5G_TYPE:
446 	       break;
447 	    case H5G_LINK:
448 	       break;
449 	    default:
450 	       printf("Unknown object class %d!", obj_class);
451 	 }
452       }
453 
454       /* Close up the shop. */
455       if (H5Dclose(datasetid) < 0 ||
456 	  H5Sclose(spaceid) < 0 ||
457 	  H5Gclose(grpid) < 0 ||
458 	  H5Fclose(fileid) < 0) ERR;
459    }
460 
461    SUMMARIZE_ERR;
462    printf("*** Creating some 3D datasets using shared dimscales...");
463 
464    {
465 #define NDIMS 3
466 #define TIME_DIM 0
467 #define LAT_DIM 1
468 #define LON_DIM 2
469 #define LAT_LEN 2
470 #define LON_LEN 3
471 #define LAT_NAME "Lat"
472 #define LON_NAME "Lon"
473 #define TIME_NAME "Time"
474 #define PRES_NAME "Pressure"
475 #define TEMP_NAME "Temperature"
476 
477       hid_t fileid, grpid, lat_spaceid, lon_spaceid, time_spaceid, spaceid;
478       hid_t lat_scaleid, lon_scaleid, time_scaleid;
479       hid_t pres_dsid, temp_dsid, cparmsid;
480       hsize_t dims[NDIMS], max_dims[NDIMS];
481 
482       /* Create file and group. */
483       if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT,
484 			      H5P_DEFAULT)) < 0) ERR;
485       if ((grpid = H5Gcreate(fileid, GRP_NAME, 0)) < 0) ERR;
486 
487       /* Create 3 1D spaces for the 3 dimension scale datasets. Time
488        * starts out as size 0. It's an unlimited dimension scale. */
489       dims[0] = 0;
490       max_dims[0] = H5S_UNLIMITED;
491       if ((time_spaceid = H5Screate_simple(1, dims, max_dims)) < 0) ERR;
492       dims[0] = LAT_LEN;
493       max_dims[0] = LAT_LEN;
494       if ((lat_spaceid = H5Screate_simple(1, dims, max_dims)) < 0) ERR;
495       dims[0] = LON_LEN;
496       max_dims[0] = LON_LEN;
497       if ((lon_spaceid = H5Screate_simple(1, dims, max_dims)) < 0) ERR;
498 
499       /* Enable chunking for unlimited time scale.  */
500       if ((cparmsid = H5Pcreate(H5P_DATASET_CREATE)) < 0) ERR;
501       dims[TIME_DIM] = 1;
502       if (H5Pset_chunk(cparmsid, 1, dims) < 0) ERR;
503 
504       /* Create our dimension scales. */
505       if ((time_scaleid = H5Dcreate(grpid, TIME_NAME, H5T_NATIVE_INT,
506 				    time_spaceid, cparmsid)) < 0) ERR;
507       if (H5DSset_scale(time_scaleid, TIME_NAME) < 0) ERR;
508       if ((lat_scaleid = H5Dcreate(grpid, LAT_NAME, H5T_NATIVE_FLOAT,
509 				   lat_spaceid, H5P_DEFAULT)) < 0) ERR;
510       if (H5DSset_scale(lat_scaleid, LAT_NAME) < 0) ERR;
511       if ((lon_scaleid = H5Dcreate(grpid, LON_NAME, H5T_NATIVE_FLOAT,
512 				   lon_spaceid, H5P_DEFAULT)) < 0) ERR;
513       if (H5DSset_scale(lon_scaleid, LON_NAME) < 0) ERR;
514 
515       /* Create a space corresponding to these three dimensions. */
516       dims[TIME_DIM] = 0;
517       dims[LAT_DIM] = LAT_LEN;
518       dims[LON_DIM] = LON_LEN;
519       max_dims[TIME_DIM] = H5S_UNLIMITED;
520       max_dims[LAT_DIM] = LAT_LEN;
521       max_dims[LON_DIM] = LON_LEN;
522       if ((spaceid = H5Screate_simple(NDIMS, dims, max_dims)) < 0) ERR;
523 
524       /* Create two variables which use them, and attach the dimension scales. */
525       dims[TIME_DIM] = 1;
526       if (H5Pset_chunk(cparmsid, NDIMS, dims) < 0) ERR;
527       if ((pres_dsid = H5Dcreate(grpid, PRES_NAME, H5T_NATIVE_FLOAT,
528 				 spaceid, cparmsid)) < 0) ERR;
529       if (H5DSattach_scale(pres_dsid, time_scaleid, 0) < 0) ERR;
530       if (H5DSattach_scale(pres_dsid, lat_scaleid, 1) < 0) ERR;
531       if (H5DSattach_scale(pres_dsid, lon_scaleid, 2) < 0) ERR;
532       if (H5DSset_label(pres_dsid, TIME_DIM, TIME_NAME) < 0) ERR;
533       if (H5DSset_label(pres_dsid, LAT_DIM, LAT_NAME) < 0) ERR;
534       if (H5DSset_label(pres_dsid, LON_DIM, LON_NAME) < 0) ERR;
535       if ((temp_dsid = H5Dcreate(grpid, TEMP_NAME, H5T_NATIVE_FLOAT,
536 				 spaceid, cparmsid)) < 0) ERR;
537       if (H5DSattach_scale(temp_dsid, time_scaleid, 0) < 0) ERR;
538       if (H5DSattach_scale(temp_dsid, lat_scaleid, 1) < 0) ERR;
539       if (H5DSattach_scale(temp_dsid, lon_scaleid, 2) < 0) ERR;
540       if (H5DSset_label(temp_dsid, TIME_DIM, TIME_NAME) < 0) ERR;
541       if (H5DSset_label(temp_dsid, LAT_DIM, LAT_NAME) < 0) ERR;
542       if (H5DSset_label(temp_dsid, LON_DIM, LON_NAME) < 0) ERR;
543 
544       /* Close up the shop. */
545       if (H5Dclose(pres_dsid) < 0 ||
546 	  H5Dclose(temp_dsid) < 0 ||
547 	  H5Dclose(lat_scaleid) < 0 ||
548 	  H5Dclose(lon_scaleid) < 0 ||
549 	  H5Dclose(time_scaleid) < 0 ||
550 	  H5Sclose(spaceid) < 0 ||
551 	  H5Gclose(grpid) < 0 ||
552 	  H5Fclose(fileid) < 0) ERR;
553    }
554 
555    SUMMARIZE_ERR;
556    printf("*** Checking 3D datasets created with shared dimscales...");
557 
558    {
559       hid_t fileid, grpid, spaceid = 0, datasetid = 0;
560       hsize_t num_obj, i;
561       int obj_class;
562       char obj_name[STR_LEN + 1];
563       htri_t is_scale;
564       int num_scales;
565       hsize_t dims[NDIMS], max_dims[NDIMS];
566       int d;
567 
568       /* Reopen the file and group. */
569       if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDWR, H5P_DEFAULT)) < 0) ERR;
570       if ((grpid = H5Gopen(fileid, GRP_NAME)) < 0) ERR;
571 
572       /* Loop through datasets to find variables. */
573       if (H5Gget_num_objs(grpid, &num_obj) < 0) ERR;
574       for (i=0; i<num_obj; i++)
575       {
576 	 /* Get the type (i.e. group, dataset, etc.), and the name of
577 	  * the object. */
578 	 if ((obj_class = H5Gget_objtype_by_idx(grpid, i)) < 0) ERR;
579 	 if (H5Gget_objname_by_idx(grpid, i, obj_name, STR_LEN) < 0) ERR;
580 	 /*printf("\nEncountered: HDF5 object obj_class %d obj_name %s\n", obj_class, obj_name);*/
581 
582 	 /* Deal with groups and datasets. */
583 	 switch(obj_class)
584 	 {
585 	    case H5G_GROUP:
586 	       break;
587 	    case H5G_DATASET:
588 	       /* Open the dataset. */
589 	       if ((datasetid = H5Dopen(grpid, obj_name)) < 0) ERR;
590 	       /*printf("\nobj_name %s\n", obj_name);*/
591 
592 	       /* Get the dimensions of this dataset. */
593 	       if ((spaceid = H5Dget_space(datasetid)) < 0) ERR;
594 	       if (H5Sget_simple_extent_dims(spaceid, dims, max_dims) < 0) ERR;
595 
596 	       /* Is this a dimscale? */
597 	       if ((is_scale = H5DSis_scale(datasetid)) < 0) ERR;
598 	       if (is_scale)
599 	       {
600 		  char nom_de_quincey[STR_LEN+1];
601 
602 		  /* A dimscale comes with a NAME attribute, in
603 		   * addition to its real name. */
604 		  if (H5DSget_scale_name(datasetid, nom_de_quincey,
605 					 STR_LEN) < 0) ERR;
606 		  /*printf("found scale %s, NAME %s id 0x%x\n", obj_name,
607 		    nom_de_quincey, datasetid);*/
608 
609 		  /* Check size depending on name. */
610 		  if ((!strcmp(obj_name, LAT_NAME) && dims[TIME_DIM] != LAT_LEN) ||
611 		      (!strcmp(obj_name, LON_NAME) && dims[TIME_DIM] != LON_LEN) ||
612 		      (!strcmp(obj_name, TIME_NAME) &&
613 		       max_dims[TIME_DIM] != H5S_UNLIMITED)) ERR;
614 
615 	       }
616 	       else
617 	       {
618 		  char label[STR_LEN+1];
619 		  hid_t visitor_data = 0;
620 
621 		  /* SHould have these dimensions... */
622 		  if (dims[TIME_DIM] != 0 || dims[LAT_DIM] != LAT_LEN ||
623 		      dims[LON_DIM] != LON_LEN) ERR;
624 		  if (max_dims[TIME_DIM] != H5S_UNLIMITED) ERR;
625 
626 		  /* Here's how to get the number of scales attached
627 		   * to the dataset's dimension 0. */
628 		  if ((num_scales = H5DSget_num_scales(datasetid, 0)) < 0) ERR;
629 		  if (num_scales != 1) ERR;
630 
631 		  /* Go through all dimscales for this var and learn
632 		   * about them. What I want is the dataset id of each
633 		   * dimscale. Then... */
634 		  for (d = 0; d < NDIMS; d++)
635 		     if (H5DSiterate_scales(datasetid, d, NULL, alien_visitor,
636 					    &visitor_data) < 0) ERR;
637 		  /*printf("visitor_data: 0x%x\n", visitor_data);*/
638 
639 		  /* There's also a label for each dimension. */
640 		  if (H5DSget_label(datasetid, 0, label, STR_LEN) < 0) ERR;
641 		  if (strcmp(label, TIME_NAME)) ERR;
642 		  if (H5DSget_label(datasetid, 1, label, STR_LEN) < 0) ERR;
643 		  if (strcmp(label, LAT_NAME)) ERR;
644 		  if (H5DSget_label(datasetid, 2, label, STR_LEN) < 0) ERR;
645 		  if (strcmp(label, LON_NAME)) ERR;
646 	       }
647 	       if (H5Dclose(datasetid) < 0) ERR;
648 	       break;
649 	    case H5G_TYPE:
650 	       break;
651 	    case H5G_LINK:
652 	       break;
653 	    default:
654 	       printf("Unknown object class %d!", obj_class);
655 	 }
656       }
657 
658       /* Close up the shop. */
659       if (H5Sclose(spaceid) < 0 ||
660 	  H5Gclose(grpid) < 0 ||
661 	  H5Fclose(fileid) < 0) ERR;
662    }
663 
664    SUMMARIZE_ERR;
665    printf("*** Creating 3D datasets using shared dimscales in groups...");
666 
667    {
668 #define FATHER "Adam"
669 #define GOOD_CHILD "Able"
670 #define BAD_CHILD "Cain"
671 #define DISTANCE_LEN 3
672 #define SMELLINESS_NAME "Smelliness"
673 #define DISTANCE_NAME "Distance"
674 #define TIME_NAME "Time"
675 #define TIME_DIM 0
676 #define SMELLINESS_DIM 1
677 #define DISTANCE_DIM 2
678 #define GOAT_NAME "Billy_goat_gruff"
679 #define CAMEL_NAME "Grumpy_the_camel"
680 
681       hid_t fileid, smelliness_spaceid, distance_spaceid, time_spaceid, spaceid;
682       hid_t adam_grpid, able_grpid, cain_grpid;
683       hid_t time_scaleid, smelliness_scaleid, distance_scaleid;
684       hid_t goat_dsid, camel_dsid, cparmsid;
685       hsize_t dims[NDIMS], max_dims[NDIMS];
686 
687       /* Create file and group. */
688       if ((fileid = H5Fcreate(FILE_NAME, H5F_ACC_TRUNC, H5P_DEFAULT,
689 			      H5P_DEFAULT)) < 0) ERR;
690       if ((adam_grpid = H5Gcreate(fileid, FATHER, 0)) < 0) ERR;
691       if ((able_grpid = H5Gcreate(adam_grpid, GOOD_CHILD, 0)) < 0) ERR;
692       if ((cain_grpid = H5Gcreate(adam_grpid, BAD_CHILD, 0)) < 0) ERR;
693 
694       /* Create 3 1D spaces for the 3 dimension scale datasets. Time
695        * and smelliness starts out as 0. They are unlimited dimension
696        * scales. */
697       dims[0] = 0;
698       max_dims[0] = H5S_UNLIMITED;
699       if ((time_spaceid = H5Screate_simple(1, dims, max_dims)) < 0) ERR;
700       dims[0] = 0;
701       max_dims[0] = H5S_UNLIMITED;
702       if ((smelliness_spaceid = H5Screate_simple(1, dims, max_dims)) < 0) ERR;
703       dims[0] = DISTANCE_LEN;
704       max_dims[0] = DISTANCE_LEN;
705       if ((distance_spaceid = H5Screate_simple(1, dims, max_dims)) < 0) ERR;
706 
707       /* Enable chunking for unlimited time and smelliness scale. */
708       if ((cparmsid = H5Pcreate(H5P_DATASET_CREATE)) < 0) ERR;
709       dims[0] = 1;
710       if (H5Pset_chunk(cparmsid, 1, dims) < 0) ERR;
711 
712       /* Create our dimension scales. */
713       if ((time_scaleid = H5Dcreate(adam_grpid, TIME_NAME, H5T_NATIVE_INT,
714 				    time_spaceid, cparmsid)) < 0) ERR;
715       if (H5DSset_scale(time_scaleid, TIME_NAME) < 0) ERR;
716       if ((smelliness_scaleid = H5Dcreate(adam_grpid, SMELLINESS_NAME, H5T_NATIVE_FLOAT,
717 					  smelliness_spaceid, cparmsid)) < 0) ERR;
718       if (H5DSset_scale(smelliness_scaleid, SMELLINESS_NAME) < 0) ERR;
719       if ((distance_scaleid = H5Dcreate(adam_grpid, DISTANCE_NAME, H5T_NATIVE_FLOAT,
720 					distance_spaceid, H5P_DEFAULT)) < 0) ERR;
721       if (H5DSset_scale(distance_scaleid, DISTANCE_NAME) < 0) ERR;
722 
723       /* Create a space corresponding to these three dimensions. */
724       dims[TIME_DIM] = 0;
725       dims[SMELLINESS_DIM] = 0;
726       dims[DISTANCE_DIM] = DISTANCE_LEN;
727       max_dims[TIME_DIM] = H5S_UNLIMITED;
728       max_dims[SMELLINESS_DIM] = H5S_UNLIMITED;
729       max_dims[DISTANCE_DIM] = DISTANCE_LEN;
730       if ((spaceid = H5Screate_simple(NDIMS, dims, max_dims)) < 0) ERR;
731 
732       /* Set up chunking for our 3D vars. */
733       dims[TIME_DIM] = 1;
734       dims[SMELLINESS_DIM] = 1;
735       if (H5Pset_chunk(cparmsid, NDIMS, dims) < 0) ERR;
736 
737       /* Create two variables which use them, and attach the dimension scales. */
738       if ((goat_dsid = H5Dcreate(able_grpid, GOAT_NAME, H5T_NATIVE_FLOAT,
739 				 spaceid, cparmsid)) < 0) ERR;
740       if (H5DSattach_scale(goat_dsid, time_scaleid, 0) < 0) ERR;
741       if (H5DSattach_scale(goat_dsid, smelliness_scaleid, 1) < 0) ERR;
742       if (H5DSattach_scale(goat_dsid, distance_scaleid, 2) < 0) ERR;
743       if ((camel_dsid = H5Dcreate(cain_grpid, CAMEL_NAME, H5T_NATIVE_FLOAT,
744 				  spaceid, cparmsid)) < 0) ERR;
745       if (H5DSattach_scale(camel_dsid, time_scaleid, 0) < 0) ERR;
746       if (H5DSattach_scale(camel_dsid, smelliness_scaleid, 1) < 0) ERR;
747       if (H5DSattach_scale(camel_dsid, distance_scaleid, 2) < 0) ERR;
748 
749       /* Close up the shop. */
750       if (H5Dclose(goat_dsid) < 0 ||
751 	  H5Dclose(camel_dsid) < 0 ||
752 	  H5Dclose(smelliness_scaleid) < 0 ||
753 	  H5Dclose(distance_scaleid) < 0 ||
754 	  H5Dclose(time_scaleid) < 0 ||
755 	  H5Sclose(spaceid) < 0 ||
756 	  H5Gclose(cain_grpid) < 0 ||
757 	  H5Gclose(able_grpid) < 0 ||
758 	  H5Gclose(adam_grpid) < 0 ||
759 	  H5Fclose(fileid) < 0) ERR;
760    }
761 
762    SUMMARIZE_ERR;
763    printf("*** Checking 3D datasets in groups created with shared dimscales...");
764 
765    {
766       hid_t fileid, grpid;
767 
768       /* Reopen the file and group. */
769       if ((fileid = H5Fopen(FILE_NAME, H5F_ACC_RDWR, H5P_DEFAULT)) < 0) ERR;
770       if ((grpid = H5Gopen(fileid, FATHER)) < 0) ERR;
771 
772       /* If we can't scan the group, crash into a flaming heap of
773        * smoking, smoldering rubbish. */
774       if (rec_scan_group(grpid)) ERR;
775 
776       /* Close up the shop. */
777       if (H5Gclose(grpid) < 0 ||
778 	  H5Fclose(fileid) < 0) ERR;
779    }
780 
781    SUMMARIZE_ERR;
782    FINAL_RESULTS;
783 }
784