summaryrefslogtreecommitdiffstats
path: root/unix/tkUnixButton.c
blob: 1aeefacb26e79b520afd5eadd7089b99b3a99ce5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
/*
 * tkUnixButton.c --
 *
 *	This file implements the Unix specific portion of the button widgets.
 *
 * Copyright (c) 1996-1997 by Sun Microsystems, Inc.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tkInt.h"
#include "tkButton.h"
#include "tk3d.h"

/*
 * Shared with menu widget.
 */

MODULE_SCOPE void	TkpDrawCheckIndicator(Tk_Window tkwin,
			    Display *display, Drawable d, int x, int y,
			    Tk_3DBorder bgBorder, XColor *indicatorColor,
			    XColor *selectColor, XColor *disColor, int on,
			    int disabled, int mode);

/*
 * Declaration of Unix specific button structure.
 */

typedef struct UnixButton {
    TkButton info;		/* Generic button info. */
} UnixButton;

/*
 * The class function table for the button widgets.
 */

const Tk_ClassProcs tkpButtonProcs = {
    sizeof(Tk_ClassProcs),	/* size */
    TkButtonWorldChanged,	/* worldChangedProc */
    NULL,					/* createProc */
    NULL					/* modalProc */
};

/*
 * The button image.
 * The header info here is ignored, it's the image that's important. The
 * colors will be applied as follows:
 *   A = Background
 *   B = Background
 *   C = 3D light
 *   D = selectColor
 *   E = 3D dark
 *   F = Background
 *   G = Indicator Color
 *   H = disabled Indicator Color
 */

/* XPM */
static const char *const button_images[] = {
    /* width height ncolors chars_per_pixel */
    "52 26 7 1",
    /* colors */
    "A c #808000000000",
    "B c #000080800000",
    "C c #808080800000",
    "D c #000000008080",
    "E c #808000008080",
    "F c #000080808080",
    "G c #000000000000",
    "H c #000080800000",
    /* pixels */
    "AAAAAAAAAAAABAAAAAAAAAAAABAAAAAAAAAAAABAAAAAAAAAAAAB",
    "AEEEEEEEEEECBAEEEEEEEEEECBAEEEEEEEEEECBAEEEEEEEEEECB",
    "AEDDDDDDDDDCBAEDDDDDDDDDCBAEFFFFFFFFFCBAEFFFFFFFFFCB",
    "AEDDDDDDDDDCBAEDDDDDDDGDCBAEFFFFFFFFFCBAEFFFFFFFHFCB",
    "AEDDDDDDDDDCBAEDDDDDDGGDCBAEFFFFFFFFFCBAEFFFFFFHHFCB",
    "AEDDDDDDDDDCBAEDGDDDGGGDCBAEFFFFFFFFFCBAEFHFFFHHHFCB",
    "AEDDDDDDDDDCBAEDGGDGGGDDCBAEFFFFFFFFFCBAEFHHFHHHFFCB",
    "AEDDDDDDDDDCBAEDGGGGGDDDCBAEFFFFFFFFFCBAEFHHHHHFFFCB",
    "AEDDDDDDDDDCBAEDDGGGDDDDCBAEFFFFFFFFFCBAEFFHHHFFFFCB",
    "AEDDDDDDDDDCBAEDDDGDDDDDCBAEFFFFFFFFFCBAEFFFHFFFFFCB",
    "AEDDDDDDDDDCBAEDDDDDDDDDCBAEFFFFFFFFFCBAEFFFFFFFFFCB",
    "ACCCCCCCCCCCBACCCCCCCCCCCBACCCCCCCCCCCBACCCCCCCCCCCB",
    "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
    "FFFFAAAAFFFFFFFFFAAAAFFFFFFFFFAAAAFFFFFFFFFAAAAFFFFF",
    "FFAAEEEEAAFFFFFAAEEEEAAFFFFFAAEEEEAAFFFFFAAEEEEAAFFF",
    "FAEEDDDDEEBFFFAEEDDDDEEBFFFAEEFFFFEEBFFFAEEFFFFEEBFF",
    "FAEDDDDDDCBFFFAEDDDDDDCBFFFAEFFFFFFCBFFFAEFFFFFFCBFF",
    "AEDDDDDDDDCBFAEDDDGGDDDCBFAEFFFFFFFFCBFAEFFFHHFFFCBF",
    "AEDDDDDDDDCBFAEDDGGGGDDCBFAEFFFFFFFFCBFAEFFHHHHFFCBF",
    "AEDDDDDDDDCBFAEDDGGGGDDCBFAEFFFFFFFFCBFAEFFHHHHFFCBF",
    "AEDDDDDDDDCBFAEDDDGGDDDCBFAEFFFFFFFFCBFAEFFFHHFFFCBF",
    "FAEDDDDDDCBFFFAEDDDDDDCBFFFAEFFFFFFCBFFFAEFFFFFFCBFF",
    "FACCDDDDCCBFFFACCDDDDCCBFFFACCFFFFCCBFFFACCFFFFCCBFF",
    "FFBBCCCCBBFFFFFBBCCCCBBFFFFFBBCCCCBBFFFFFBBCCCCBBFFF",
    "FFFFBBBBFFFFFFFFFBBBBFFFFFFFFFBBBBFFFFFFFFFBBBBFFFFF",
    "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
};

/*
 * Sizes and offsets into above XPM file.
 */

#define CHECK_BUTTON_DIM    13
#define CHECK_MENU_DIM       9
#define CHECK_START          9
#define CHECK_ON_OFFSET     13
#define CHECK_OFF_OFFSET     0
#define CHECK_DISON_OFFSET  39
#define CHECK_DISOFF_OFFSET 26
#define RADIO_BUTTON_DIM    12
#define RADIO_MENU_DIM       6
#define RADIO_WIDTH         13
#define RADIO_START         22
#define RADIO_ON_OFFSET     13
#define RADIO_OFF_OFFSET     0
#define RADIO_DISON_OFFSET  39
#define RADIO_DISOFF_OFFSET 26

/*
 * Indicator Draw Modes
 */

#define CHECK_BUTTON 0
#define CHECK_MENU   1
#define RADIO_BUTTON 2
#define RADIO_MENU   3

/*
 *----------------------------------------------------------------------
 *
 * TkpDrawCheckIndicator -
 *
 *	Draws the checkbox image in the drawable at the (x,y) location, value,
 *	and state given. This routine is use by the button and menu widgets
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	An image is drawn in the drawable at the location given.
 *
 *----------------------------------------------------------------------
 */

void
TkpDrawCheckIndicator(
    Tk_Window tkwin,		/* handle for resource alloc */
    Display *display,
    Drawable d,			/* what to draw on */
    int x, int y,		/* where to draw */
    Tk_3DBorder bgBorder,	/* colors of the border */
    XColor *indicatorColor,	/* color of the indicator */
    XColor *selectColor,	/* color when selected */
    XColor *disableColor,	/* color when disabled */
    int on,			/* are we on? */
    int disabled,		/* are we disabled? */
    int mode)			/* kind of indicator to draw */
{
    int ix, iy;
    int dim;
    int imgsel, imgstart;
    TkBorder *bg_brdr = (TkBorder*)bgBorder;
    XGCValues gcValues;
    GC copyGC;
    unsigned long imgColors[8];
    XImage *img;
    Pixmap pixmap;
    int depth;

    /*
     * Sanity check.
     */

    if (tkwin == NULL || display == None || d == None || bgBorder == NULL
	    || indicatorColor == NULL) {
	return;
    }

    if (disableColor == NULL) {
	disableColor = bg_brdr->bgColorPtr;
    }

    if (selectColor == NULL) {
	selectColor = bg_brdr->bgColorPtr;
    }

    depth = Tk_Depth(tkwin);

    /*
     * Compute starting point and dimensions of image inside button_images to
     * be used.
     */

    switch (mode) {
    default:
    case CHECK_BUTTON:
	imgsel = on == 2 ? CHECK_DISON_OFFSET :
		on == 1 ? CHECK_ON_OFFSET : CHECK_OFF_OFFSET;
	imgsel += disabled && on != 2 ? CHECK_DISOFF_OFFSET : 0;
	imgstart = CHECK_START;
	dim = CHECK_BUTTON_DIM;
	break;

    case CHECK_MENU:
	imgsel = on == 2 ? CHECK_DISOFF_OFFSET :
		on == 1 ? CHECK_ON_OFFSET : CHECK_OFF_OFFSET;
	imgsel += disabled && on != 2 ? CHECK_DISOFF_OFFSET : 0;
	imgstart = CHECK_START + 2;
	imgsel += 2;
	dim = CHECK_MENU_DIM;
	break;

    case RADIO_BUTTON:
	imgsel = on == 2 ? RADIO_DISON_OFFSET :
		on==1 ? RADIO_ON_OFFSET : RADIO_OFF_OFFSET;
	imgsel += disabled && on != 2 ? RADIO_DISOFF_OFFSET : 0;
	imgstart = RADIO_START;
	dim = RADIO_BUTTON_DIM;
	break;

    case RADIO_MENU:
	imgsel = on == 2 ? RADIO_DISOFF_OFFSET :
		on==1 ? RADIO_ON_OFFSET : RADIO_OFF_OFFSET;
	imgsel += disabled && on != 2 ? RADIO_DISOFF_OFFSET : 0;
	imgstart = RADIO_START + 3;
	imgsel += 3;
	dim = RADIO_MENU_DIM;
	break;
    }

    /*
     * Allocate the drawing areas to use. Note that we use double-buffering
     * here because not all code paths leading to this function do so.
     */

    pixmap = Tk_GetPixmap(display, d, dim, dim, depth);
    if (pixmap == None) {
	return;
    }

    x -= dim/2;
    y -= dim/2;

    img = XGetImage(display, pixmap, 0, 0,
	    (unsigned int)dim, (unsigned int)dim, AllPlanes, ZPixmap);
    if (img == NULL) {
	return;
    }

    /*
     * Set up the color mapping table.
     */

    TkpGetShadows(bg_brdr, tkwin);

    imgColors[0 /*A*/] =
	    Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel;
    imgColors[1 /*B*/] =
	    Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel;
    imgColors[2 /*C*/] = (bg_brdr->lightColorPtr != NULL) ?
	    Tk_GetColorByValue(tkwin, bg_brdr->lightColorPtr)->pixel :
	    WhitePixelOfScreen(bg_brdr->screen);
    imgColors[3 /*D*/] =
	    Tk_GetColorByValue(tkwin, selectColor)->pixel;
    imgColors[4 /*E*/] = (bg_brdr->darkColorPtr != NULL) ?
	    Tk_GetColorByValue(tkwin, bg_brdr->darkColorPtr)->pixel :
	    BlackPixelOfScreen(bg_brdr->screen);
    imgColors[5 /*F*/] =
	    Tk_GetColorByValue(tkwin, bg_brdr->bgColorPtr)->pixel;
    imgColors[6 /*G*/] =
	    Tk_GetColorByValue(tkwin, indicatorColor)->pixel;
    imgColors[7 /*H*/] =
	    Tk_GetColorByValue(tkwin, disableColor)->pixel;

    /*
     * Create the image, painting it into an XImage one pixel at a time.
     */

    for (iy=0 ; iy<dim ; iy++) {
	for (ix=0 ; ix<dim ; ix++) {
	    XPutPixel(img, ix, iy,
		    imgColors[button_images[imgstart+iy][imgsel+ix] - 'A']);
	}
    }

    /*
     * Copy onto our target drawable surface.
     */

    memset(&gcValues, 0, sizeof(gcValues));
    gcValues.background = bg_brdr->bgColorPtr->pixel;
    gcValues.graphics_exposures = False;
    copyGC = Tk_GetGC(tkwin, 0, &gcValues);

    XPutImage(display, pixmap, copyGC, img, 0, 0, 0, 0,
	    (unsigned)dim, (unsigned)dim);
    XCopyArea(display, pixmap, d, copyGC, 0, 0,
	    (unsigned)dim, (unsigned)dim, x, y);

    /*
     * Tidy up.
     */

    Tk_FreeGC(display, copyGC);
    XDestroyImage(img);
    Tk_FreePixmap(display, pixmap);
}

/*
 *----------------------------------------------------------------------
 *
 * TkpCreateButton --
 *
 *	Allocate a new TkButton structure.
 *
 * Results:
 *	Returns a newly allocated TkButton structure.
 *
 * Side effects:
 *	Registers an event handler for the widget.
 *
 *----------------------------------------------------------------------
 */

TkButton *
TkpCreateButton(
    Tk_Window tkwin)
{
    UnixButton *butPtr = ckalloc(sizeof(UnixButton));

    return (TkButton *) butPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkpDisplayButton --
 *
 *	This function is invoked to display a button widget. It is normally
 *	invoked as an idle handler.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Commands are output to X to display the button in its current mode.
 *	The REDRAW_PENDING flag is cleared.
 *
 *----------------------------------------------------------------------
 */

void
TkpDisplayButton(
    ClientData clientData)	/* Information about widget. */
{
    register TkButton *butPtr = (TkButton *) clientData;
    GC gc;
    Tk_3DBorder border;
    Pixmap pixmap;
    int x = 0;			/* Initialization only needed to stop compiler
				 * warning. */
    int y, relief;
    Tk_Window tkwin = butPtr->tkwin;
    int width = 0, height = 0, fullWidth, fullHeight;
    int textXOffset, textYOffset;
    int haveImage = 0, haveText = 0;
    int offset;			/* 1 means this is a button widget, so we
				 * offset the text to make the button appear
				 * to move up and down as the relief
				 * changes. */
    int imageWidth, imageHeight;
    int imageXOffset = 0, imageYOffset = 0;
				/* image information that will be used to
				 * restrict disabled pixmap as well */

    butPtr->flags &= ~REDRAW_PENDING;
    if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
	return;
    }

    border = butPtr->normalBorder;
    if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) {
	gc = butPtr->disabledGC;
    } else if ((butPtr->state == STATE_ACTIVE)
	    && !Tk_StrictMotif(butPtr->tkwin)) {
	gc = butPtr->activeTextGC;
	border = butPtr->activeBorder;
    } else {
	gc = butPtr->normalTextGC;
    }
    if ((butPtr->flags & SELECTED) && (butPtr->selectBorder != NULL)
	    && !butPtr->indicatorOn) {
	border = butPtr->selectBorder;
    }

    /*
     * Override the relief specified for the button if this is a checkbutton
     * or radiobutton and there's no indicator. The new relief is as follows:
     *      If the button is select  --> "sunken"
     *      If relief==overrelief    --> relief
     *      Otherwise                --> overrelief
     *
     * The effect we are trying to achieve is as follows:
     *
     *      value    mouse-over?   -->   relief
     *     -------  ------------        --------
     *       off        no               flat
     *       off        yes              raised
     *       on         no               sunken
     *       on         yes              sunken
     *
     * This is accomplished by configuring the checkbutton or radiobutton like
     * this:
     *
     *     -indicatoron 0 -overrelief raised -offrelief flat
     *
     * Bindings (see library/button.tcl) will copy the -overrelief into
     * -relief on mouseover. Hence, we can tell if we are in mouse-over by
     * comparing relief against overRelief. This is an aweful kludge, but it
     * gives use the desired behavior while keeping the code backwards
     * compatible.
     */

    relief = butPtr->relief;
    if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
	if (butPtr->flags & SELECTED) {
	    relief = TK_RELIEF_SUNKEN;
	} else if (butPtr->overRelief != relief) {
	    relief = butPtr->offRelief;
	}
    }

    offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin);

    /*
     * In order to avoid screen flashes, this function redraws the button in a
     * pixmap, then copies the pixmap to the screen in a single operation.
     * This means that there's no point in time where the on-screen image has
     * been cleared.
     */

    pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
	    Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
    Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
	    Tk_Height(tkwin), 0, TK_RELIEF_FLAT);

    /*
     * Display image or bitmap or text for button.
     */

    if (butPtr->image != NULL) {
	Tk_SizeOfImage(butPtr->image, &width, &height);
	haveImage = 1;
    } else if (butPtr->bitmap != None) {
	Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
	haveImage = 1;
    }
    imageWidth = width;
    imageHeight = height;

    haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0);

    if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
	textXOffset = 0;
	textYOffset = 0;
	fullWidth = 0;
	fullHeight = 0;

	switch ((enum compound) butPtr->compound) {
	case COMPOUND_TOP:
	case COMPOUND_BOTTOM:
	    /*
	     * Image is above or below text.
	     */

	    if (butPtr->compound == COMPOUND_TOP) {
		textYOffset = height + butPtr->padY;
	    } else {
		imageYOffset = butPtr->textHeight + butPtr->padY;
	    }
	    fullHeight = height + butPtr->textHeight + butPtr->padY;
	    fullWidth = (width > butPtr->textWidth ? width :
		    butPtr->textWidth);
	    textXOffset = (fullWidth - butPtr->textWidth)/2;
	    imageXOffset = (fullWidth - width)/2;
	    break;
	case COMPOUND_LEFT:
	case COMPOUND_RIGHT:
	    /*
	     * Image is left or right of text.
	     */

	    if (butPtr->compound == COMPOUND_LEFT) {
		textXOffset = width + butPtr->padX;
	    } else {
		imageXOffset = butPtr->textWidth + butPtr->padX;
	    }
	    fullWidth = butPtr->textWidth + butPtr->padX + width;
	    fullHeight = (height > butPtr->textHeight ? height :
		    butPtr->textHeight);
	    textYOffset = (fullHeight - butPtr->textHeight)/2;
	    imageYOffset = (fullHeight - height)/2;
	    break;
	case COMPOUND_CENTER:
	    /*
	     * Image and text are superimposed.
	     */

	    fullWidth = (width > butPtr->textWidth ? width :
		    butPtr->textWidth);
	    fullHeight = (height > butPtr->textHeight ? height :
		    butPtr->textHeight);
	    textXOffset = (fullWidth - butPtr->textWidth)/2;
	    imageXOffset = (fullWidth - width)/2;
	    textYOffset = (fullHeight - butPtr->textHeight)/2;
	    imageYOffset = (fullHeight - height)/2;
	    break;
	case COMPOUND_NONE:
	    break;
	}

	TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
		butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y);

	x += butPtr->indicatorSpace;

	x += offset;
	y += offset;
	if (relief == TK_RELIEF_RAISED) {
	    x -= offset;
	    y -= offset;
	} else if (relief == TK_RELIEF_SUNKEN) {
	    x += offset;
	    y += offset;
	}

	imageXOffset += x;
	imageYOffset += y;

	if (butPtr->image != NULL) {
	    /*
	     * Do boundary clipping, so that Tk_RedrawImage is passed valid
	     * coordinates. [Bug 979239]
	     */

	    if (imageXOffset < 0) {
		imageXOffset = 0;
	    }
	    if (imageYOffset < 0) {
		imageYOffset = 0;
	    }
	    if (width > Tk_Width(tkwin)) {
		width = Tk_Width(tkwin);
	    }
	    if (height > Tk_Height(tkwin)) {
		height = Tk_Height(tkwin);
	    }
	    if ((width + imageXOffset) > Tk_Width(tkwin)) {
		imageXOffset = Tk_Width(tkwin) - width;
	    }
	    if ((height + imageYOffset) > Tk_Height(tkwin)) {
		imageYOffset = Tk_Height(tkwin) - height;
	    }

	    if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
		Tk_RedrawImage(butPtr->selectImage, 0, 0,
			width, height, pixmap, imageXOffset, imageYOffset);
	    } else if ((butPtr->tristateImage != NULL) && (butPtr->flags & TRISTATED)) {
		Tk_RedrawImage(butPtr->tristateImage, 0, 0,
			width, height, pixmap, imageXOffset, imageYOffset);
	    } else {
		Tk_RedrawImage(butPtr->image, 0, 0, width,
			height, pixmap, imageXOffset, imageYOffset);
	    }
	} else {
	    XSetClipOrigin(butPtr->display, gc, imageXOffset, imageYOffset);
	    XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc,
		    0, 0, (unsigned int) width, (unsigned int) height,
		    imageXOffset, imageYOffset, 1);
	    XSetClipOrigin(butPtr->display, gc, 0, 0);
	}

	Tk_DrawTextLayout(butPtr->display, pixmap, gc,
		butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1);
	Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
		butPtr->textLayout, x + textXOffset, y + textYOffset,
		butPtr->underline);
	y += fullHeight/2;
    } else {
	if (haveImage) {
	    TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
		    butPtr->indicatorSpace + width, height, &x, &y);
	    x += butPtr->indicatorSpace;

	    x += offset;
	    y += offset;
	    if (relief == TK_RELIEF_RAISED) {
		x -= offset;
		y -= offset;
	    } else if (relief == TK_RELIEF_SUNKEN) {
		x += offset;
		y += offset;
	    }
	    imageXOffset += x;
	    imageYOffset += y;
	    if (butPtr->image != NULL) {
		/*
		 * Do boundary clipping, so that Tk_RedrawImage is passed
		 * valid coordinates. [Bug 979239]
		 */

		if (imageXOffset < 0) {
		    imageXOffset = 0;
		}
		if (imageYOffset < 0) {
		    imageYOffset = 0;
		}
		if (width > Tk_Width(tkwin)) {
		    width = Tk_Width(tkwin);
		}
		if (height > Tk_Height(tkwin)) {
		    height = Tk_Height(tkwin);
		}
		if ((width + imageXOffset) > Tk_Width(tkwin)) {
		    imageXOffset = Tk_Width(tkwin) - width;
		}
		if ((height + imageYOffset) > Tk_Height(tkwin)) {
		    imageYOffset = Tk_Height(tkwin) - height;
		}

		if ((butPtr->selectImage != NULL) &&
			(butPtr->flags & SELECTED)) {
		    Tk_RedrawImage(butPtr->selectImage, 0, 0, width,
			    height, pixmap, imageXOffset, imageYOffset);
		} else if ((butPtr->tristateImage != NULL) &&
			(butPtr->flags & TRISTATED)) {
		    Tk_RedrawImage(butPtr->tristateImage, 0, 0, width,
			    height, pixmap, imageXOffset, imageYOffset);
		} else {
		    Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
			    imageXOffset, imageYOffset);
		}
	    } else {
		XSetClipOrigin(butPtr->display, gc, x, y);
		XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
			(unsigned int) width, (unsigned int) height, x, y, 1);
		XSetClipOrigin(butPtr->display, gc, 0, 0);
	    }
	    y += height/2;
	} else {
 	    TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
		    butPtr->indicatorSpace + butPtr->textWidth,
		    butPtr->textHeight, &x, &y);

	    x += butPtr->indicatorSpace;

	    x += offset;
	    y += offset;
	    if (relief == TK_RELIEF_RAISED) {
		x -= offset;
		y -= offset;
	    } else if (relief == TK_RELIEF_SUNKEN) {
		x += offset;
		y += offset;
	    }
	    Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
		    x, y, 0, -1);
	    Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
		    butPtr->textLayout, x, y, butPtr->underline);
	    y += butPtr->textHeight/2;
	}
    }

    /*
     * Draw the indicator for check buttons and radio buttons. At this point,
     * x and y refer to the top-left corner of the text or image or bitmap.
     */

    if ((butPtr->type == TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
	if (butPtr->indicatorDiameter > 2*butPtr->borderWidth) {
	    TkBorder *selBorder = (TkBorder *) butPtr->selectBorder;
	    XColor *selColor = NULL;

	    if (selBorder != NULL) {
		selColor = selBorder->bgColorPtr;
	    }
	    x -= butPtr->indicatorSpace/2;
	    y = Tk_Height(tkwin)/2;
	    TkpDrawCheckIndicator(tkwin, butPtr->display, pixmap, x, y,
		    border, butPtr->normalFg, selColor, butPtr->disabledFg,
		    ((butPtr->flags & SELECTED) ? 1 :
			    (butPtr->flags & TRISTATED) ? 2 : 0),
		    (butPtr->state == STATE_DISABLED), CHECK_BUTTON);
	}
    } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
	if (butPtr->indicatorDiameter > 2*butPtr->borderWidth) {
	    TkBorder *selBorder = (TkBorder *) butPtr->selectBorder;
	    XColor *selColor = NULL;

	    if (selBorder != NULL) {
		selColor = selBorder->bgColorPtr;
	    }
	    x -= butPtr->indicatorSpace/2;
	    y = Tk_Height(tkwin)/2;
	    TkpDrawCheckIndicator(tkwin, butPtr->display, pixmap, x, y,
		    border, butPtr->normalFg, selColor, butPtr->disabledFg,
		    ((butPtr->flags & SELECTED) ? 1 :
			    (butPtr->flags & TRISTATED) ? 2 : 0),
		    (butPtr->state == STATE_DISABLED), RADIO_BUTTON);
	}
    }

    /*
     * If the button is disabled with a stipple rather than a special
     * foreground color, generate the stippled effect. If the widget is
     * selected and we use a different background color when selected, must
     * temporarily modify the GC so the stippling is the right color.
     */

    if ((butPtr->state == STATE_DISABLED)
	    && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
	if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
		&& (butPtr->selectBorder != NULL)) {
	    XSetForeground(butPtr->display, butPtr->stippleGC,
		    Tk_3DBorderColor(butPtr->selectBorder)->pixel);
	}

	/*
	 * Stipple the whole button if no disabledFg was specified, otherwise
	 * restrict stippling only to displayed image
	 */

	if (butPtr->disabledFg == NULL) {
	    XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 0, 0,
		    (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin));
	} else {
	    XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC,
		    imageXOffset, imageYOffset,
		    (unsigned) imageWidth, (unsigned) imageHeight);
	}
	if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
		&& (butPtr->selectBorder != NULL)) {
	    XSetForeground(butPtr->display, butPtr->stippleGC,
		    Tk_3DBorderColor(butPtr->normalBorder)->pixel);
	}
    }

    /*
     * Draw the border and traversal highlight last. This way, if the button's
     * contents overflow they'll be covered up by the border. This code is
     * complicated by the possible combinations of focus highlight and default
     * rings. We draw the focus and highlight rings using the highlight border
     * and highlight foreground color.
     */

    if (relief != TK_RELIEF_FLAT) {
	int inset = butPtr->highlightWidth;

	if (butPtr->defaultState == DEFAULT_ACTIVE) {
	    /*
	     * Draw the default ring with 2 pixels of space between the
	     * default ring and the button and the default ring and the focus
	     * ring. Note that we need to explicitly draw the space in the
	     * highlightBorder color to ensure that we overwrite any overflow
	     * text and/or a different button background color.
	     */

	    Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
		    inset, Tk_Width(tkwin) - 2*inset,
		    Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
	    inset += 2;
	    Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
		    inset, Tk_Width(tkwin) - 2*inset,
		    Tk_Height(tkwin) - 2*inset, 1, TK_RELIEF_SUNKEN);
	    inset++;
	    Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
		    inset, Tk_Width(tkwin) - 2*inset,
		    Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);

	    inset += 2;
	} else if (butPtr->defaultState == DEFAULT_NORMAL) {
	    /*
	     * Leave room for the default ring and write over any text or
	     * background color.
	     */

	    Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0,
		    0, Tk_Width(tkwin), Tk_Height(tkwin), 5, TK_RELIEF_FLAT);
	    inset += 5;
	}

	/*
	 * Draw the button border.
	 */

	Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
		Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
		butPtr->borderWidth, relief);
    }
    if (butPtr->highlightWidth > 0) {
	GC gc;

	if (butPtr->flags & GOT_FOCUS) {
	    gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
	} else {
	    gc = Tk_GCForColor(Tk_3DBorderColor(butPtr->highlightBorder),
		    pixmap);
	}

	/*
	 * Make sure the focus ring shrink-wraps the actual button, not the
	 * padding space left for a default ring.
	 */

	if (butPtr->defaultState == DEFAULT_NORMAL) {
	    TkDrawInsetFocusHighlight(tkwin, gc, butPtr->highlightWidth,
		    pixmap, 5);
	} else {
	    Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
	}
    }

    /*
     * Copy the information from the off-screen pixmap onto the screen, then
     * delete the pixmap.
     */

    XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
	    butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
	    (unsigned) Tk_Height(tkwin), 0, 0);
    Tk_FreePixmap(butPtr->display, pixmap);
}

/*
 *----------------------------------------------------------------------
 *
 * TkpComputeButtonGeometry --
 *
 *	After changes in a button's text or bitmap, this function recomputes
 *	the button's geometry and passes this information along to the
 *	geometry manager for the window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The button's window may change size.
 *
 *----------------------------------------------------------------------
 */

void
TkpComputeButtonGeometry(
    register TkButton *butPtr)	/* Button whose geometry may have changed. */
{
    int width, height, avgWidth, txtWidth, txtHeight;
    int haveImage = 0, haveText = 0;
    Tk_FontMetrics fm;

    butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;

    /*
     * Leave room for the default ring if needed.
     */

    if (butPtr->defaultState != DEFAULT_DISABLED) {
	butPtr->inset += 5;
    }
    butPtr->indicatorSpace = 0;

    width = 0;
    height = 0;
    txtWidth = 0;
    txtHeight = 0;
    avgWidth = 0;

    if (butPtr->image != NULL) {
	Tk_SizeOfImage(butPtr->image, &width, &height);
	haveImage = 1;
    } else if (butPtr->bitmap != None) {
	Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
	haveImage = 1;
    }

    if (haveImage == 0 || butPtr->compound != COMPOUND_NONE) {
	Tk_FreeTextLayout(butPtr->textLayout);

	butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
		Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength,
		butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight);

	txtWidth = butPtr->textWidth;
	txtHeight = butPtr->textHeight;
	avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
	Tk_GetFontMetrics(butPtr->tkfont, &fm);
	haveText = (txtWidth != 0 && txtHeight != 0);
    }

    /*
     * If the button is compound (i.e., it shows both an image and text), the
     * new geometry is a combination of the image and text geometry. We only
     * honor the compound bit if the button has both text and an image,
     * because otherwise it is not really a compound button.
     */

    if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) {
	switch ((enum compound) butPtr->compound) {
	case COMPOUND_TOP:
	case COMPOUND_BOTTOM:
	    /*
	     * Image is above or below text.
	     */

	    height += txtHeight + butPtr->padY;
	    width = (width > txtWidth ? width : txtWidth);
	    break;
	case COMPOUND_LEFT:
	case COMPOUND_RIGHT:
	    /*
	     * Image is left or right of text.
	     */

	    width += txtWidth + butPtr->padX;
	    height = (height > txtHeight ? height : txtHeight);
	    break;
	case COMPOUND_CENTER:
	    /*
	     * Image and text are superimposed.
	     */

	    width = (width > txtWidth ? width : txtWidth);
	    height = (height > txtHeight ? height : txtHeight);
	    break;
	case COMPOUND_NONE:
	    break;
	}
	if (butPtr->width > 0) {
	    width = butPtr->width;
	}
	if (butPtr->height > 0) {
	    height = butPtr->height;
	}

	if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
	    butPtr->indicatorSpace = height;
	    if (butPtr->type == TYPE_CHECK_BUTTON) {
		butPtr->indicatorDiameter = (65*height)/100;
	    } else {
		butPtr->indicatorDiameter = (75*height)/100;
	    }
	}

	width += 2*butPtr->padX;
	height += 2*butPtr->padY;
    } else {
	if (haveImage) {
	    if (butPtr->width > 0) {
		width = butPtr->width;
	    }
	    if (butPtr->height > 0) {
		height = butPtr->height;
	    }

	    if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
		butPtr->indicatorSpace = height;
		if (butPtr->type == TYPE_CHECK_BUTTON) {
		    butPtr->indicatorDiameter = (65*height)/100;
		} else {
		    butPtr->indicatorDiameter = (75*height)/100;
		}
	    }
	} else {
	    width = txtWidth;
	    height = txtHeight;

	    if (butPtr->width > 0) {
		width = butPtr->width * avgWidth;
	    }
	    if (butPtr->height > 0) {
		height = butPtr->height * fm.linespace;
	    }
	    if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
		butPtr->indicatorDiameter = fm.linespace;
		if (butPtr->type == TYPE_CHECK_BUTTON) {
		    butPtr->indicatorDiameter =
			(80*butPtr->indicatorDiameter)/100;
		}
		butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
	    }
	}
    }

    /*
     * When issuing the geometry request, add extra space for the indicator,
     * if any, and for the border and padding, plus two extra pixels so the
     * display can be offset by 1 pixel in either direction for the raised or
     * lowered effect.
     */

    if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
	width += 2*butPtr->padX;
	height += 2*butPtr->padY;
    }
    if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
	width += 2;
	height += 2;
    }
    Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
	    + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
    Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
}

/*
 * Local Variables:
 * mode: c
 * c-basic-offset: 4
 * fill-column: 78
 * End:
 */
a free list to manage the H5D_t and H5D_shared_t structs */ H5FL_DEFINE_STATIC(H5D_t); H5FL_DEFINE_STATIC(H5D_shared_t); /* Declare the external PQ free list for the sieve buffer information */ H5FL_BLK_EXTERN(sieve_buf); /* Declare the external free list to manage the H5D_chunk_info_t struct */ H5FL_EXTERN(H5D_chunk_info_t); /* Declare extern the free list to manage blocks of type conversion data */ H5FL_BLK_EXTERN(type_conv); /* Disable warning for intentional identical branches here -QAK */ H5_GCC_DIAG_OFF(larger-than=) /* Define a static "default" dataset structure to use to initialize new datasets */ static H5D_shared_t H5D_def_dset; H5_GCC_DIAG_ON(larger-than=) /* Dataset ID class */ static const H5I_class_t H5I_DATASET_CLS[1] = {{ H5I_DATASET, /* ID class value */ 0, /* Class flags */ 0, /* # of reserved IDs for class */ (H5I_free_t)H5D__close_cb /* Callback routine for closing objects of this class */ }}; /* Flag indicating "top" of interface has been initialized */ static hbool_t H5D_top_package_initialize_s = FALSE; /* Prefixes of VDS and external file from the environment variables * HDF5_EXTFILE_PREFIX and HDF5_VDS_PREFIX */ static const char *H5D_prefix_ext_env = NULL; static const char *H5D_prefix_vds_env = NULL; /*------------------------------------------------------------------------- * Function: H5D_init * * Purpose: Initialize the interface from some other layer. * * Return: Success: non-negative * * Failure: negative *------------------------------------------------------------------------- */ herr_t H5D_init(void) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* FUNC_ENTER() does all the work */ done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5D_init() */ /*-------------------------------------------------------------------------- NAME H5D__init_package -- Initialize interface-specific information USAGE herr_t H5D__init_package() RETURNS Non-negative on success/Negative on failure DESCRIPTION Initializes any interface-specific data or routines. NOTES Care must be taken when using the H5P functions, since they can cause a deadlock in the library when the library is attempting to terminate -QAK --------------------------------------------------------------------------*/ herr_t H5D__init_package(void) { H5P_genplist_t *def_dcpl; /* Default Dataset Creation Property list */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_PACKAGE /* Initialize the atom group for the dataset IDs */ if(H5I_register_type(H5I_DATASET_CLS) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to initialize interface") /* Reset the "default dataset" information */ HDmemset(&H5D_def_dset, 0, sizeof(H5D_shared_t)); /* Get the default dataset creation property list values and initialize the * default dataset with them. */ if(NULL == (def_dcpl = (H5P_genplist_t *)H5I_object(H5P_LST_DATASET_CREATE_ID_g))) HGOTO_ERROR(H5E_DATASET, H5E_BADTYPE, FAIL, "can't get default dataset creation property list") /* Get the default data storage layout */ if(H5P_get(def_dcpl, H5D_CRT_LAYOUT_NAME, &H5D_def_dset.layout) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't retrieve layout") /* Get the default dataset creation properties */ if(H5P_get(def_dcpl, H5D_CRT_EXT_FILE_LIST_NAME, &H5D_def_dset.dcpl_cache.efl) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't retrieve external file list") if(H5P_get(def_dcpl, H5D_CRT_FILL_VALUE_NAME, &H5D_def_dset.dcpl_cache.fill) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't retrieve fill value") if(H5P_get(def_dcpl, H5O_CRT_PIPELINE_NAME, &H5D_def_dset.dcpl_cache.pline) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't retrieve pipeline filter") /* Mark "top" of interface as initialized, too */ H5D_top_package_initialize_s = TRUE; /* Retrieve the prefixes of VDS and external file from the environment variable */ H5D_prefix_vds_env = HDgetenv("HDF5_VDS_PREFIX"); H5D_prefix_ext_env = HDgetenv("HDF5_EXTFILE_PREFIX"); done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__init_package() */ /*------------------------------------------------------------------------- * Function: H5D_top_term_package * * Purpose: Close the "top" of the interface, releasing IDs, etc. * * Return: Success: Positive if anything was done that might * affect other interfaces; zero otherwise. * Failure: Negative. *------------------------------------------------------------------------- */ int H5D_top_term_package(void) { int n = 0; FUNC_ENTER_NOAPI_NOINIT_NOERR if(H5D_top_package_initialize_s) { if(H5I_nmembers(H5I_DATASET) > 0) { /* The dataset API uses the "force" flag set to true because it * is using the "file objects" (H5FO) API functions to track open * objects in the file. Using the H5FO code means that dataset * IDs can have reference counts >1, when an existing dataset is * opened more than once. However, the H5I code does not attempt * to close objects with reference counts>1 unless the "force" flag * is set to true. * * At some point (probably after the group and datatypes use the * the H5FO code), the H5FO code might need to be switched around * to storing pointers to the objects being tracked (H5D_t, H5G_t, * etc) and reference count those itself instead of relying on the * reference counting in the H5I layer. Then, the "force" flag can * be put back to false. * * Setting the "force" flag to true for all the interfaces won't * work because the "file driver" (H5FD) APIs use the H5I reference * counting to avoid closing a file driver out from underneath an * open file... * * QAK - 5/13/03 */ (void)H5I_clear_type(H5I_DATASET, TRUE, FALSE); n++; /*H5I*/ } /* end if */ /* Mark closed */ if(0 == n) H5D_top_package_initialize_s = FALSE; } /* end if */ FUNC_LEAVE_NOAPI(n) } /* end H5D_top_term_package() */ /*------------------------------------------------------------------------- * Function: H5D_term_package * * Purpose: Terminate this interface. * * Note: Finishes shutting down the interface, after * H5D_top_term_package() is called * * Return: Success: Positive if anything was done that might * affect other interfaces; zero otherwise. * Failure: Negative. *------------------------------------------------------------------------- */ int H5D_term_package(void) { int n = 0; FUNC_ENTER_NOAPI_NOINIT_NOERR if(H5_PKG_INIT_VAR) { /* Sanity checks */ HDassert(0 == H5I_nmembers(H5I_DATASET)); HDassert(FALSE == H5D_top_package_initialize_s); /* Destroy the dataset object id group */ n += (H5I_dec_type_ref(H5I_DATASET) > 0); /* Mark closed */ if(0 == n) H5_PKG_INIT_VAR = FALSE; } /* end if */ FUNC_LEAVE_NOAPI(n) } /* end H5D_term_package() */ /*------------------------------------------------------------------------- * Function: H5D__close_cb * * Purpose: Called when the ref count reaches zero on the dataset's ID * * Return: SUCCEED/FAIL * *------------------------------------------------------------------------- */ static herr_t H5D__close_cb(H5VL_object_t *dset_vol_obj) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC /* Sanity check */ HDassert(dset_vol_obj); /* Close the dataset */ if(H5VL_dataset_close(dset_vol_obj, H5P_DATASET_XFER_DEFAULT, H5_REQUEST_NULL) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "unable to close dataset"); done: /* XXX: (MSC) Weird thing for datasets and filters: * Always decrement the ref count on the VOL for datasets, since * the ID is removed even if the close fails. */ /* Free the VOL object */ if(H5VL_free_object(dset_vol_obj) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTDEC, FAIL, "unable to free VOL object"); FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__close_cb() */ /*------------------------------------------------------------------------- * Function: H5D__create_named * * Purpose: Internal routine to create a new dataset. * * Return: Success: Non-NULL, pointer to new dataset object. * * Failure: NULL *------------------------------------------------------------------------- */ H5D_t * H5D__create_named(const H5G_loc_t *loc, const char *name, hid_t type_id, const H5S_t *space, hid_t lcpl_id, hid_t dcpl_id, hid_t dapl_id) { H5O_obj_create_t ocrt_info; /* Information for object creation */ H5D_obj_create_t dcrt_info; /* Information for dataset creation */ H5D_t *ret_value = NULL; /* Return value */ FUNC_ENTER_PACKAGE /* Check arguments */ HDassert(loc); HDassert(name && *name); HDassert(type_id != H5P_DEFAULT); HDassert(space); HDassert(lcpl_id != H5P_DEFAULT); HDassert(dcpl_id != H5P_DEFAULT); HDassert(dapl_id != H5P_DEFAULT); /* Set up dataset creation info */ dcrt_info.type_id = type_id; dcrt_info.space = space; dcrt_info.dcpl_id = dcpl_id; dcrt_info.dapl_id = dapl_id; /* Set up object creation information */ ocrt_info.obj_type = H5O_TYPE_DATASET; ocrt_info.crt_info = &dcrt_info; ocrt_info.new_obj = NULL; /* Create the new dataset and link it to its parent group */ if(H5L_link_object(loc, name, &ocrt_info, lcpl_id) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to create and link to dataset") HDassert(ocrt_info.new_obj); /* Set the return value */ ret_value = (H5D_t *)ocrt_info.new_obj; done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__create_named() */ /*------------------------------------------------------------------------- * Function: H5D__get_space_status * * Purpose: Returns the status of dataspace allocation. * * Return: * Success: Non-negative * Failture: Negative *------------------------------------------------------------------------- */ herr_t H5D__get_space_status(const H5D_t *dset, H5D_space_status_t *allocation) { herr_t ret_value = SUCCEED; FUNC_ENTER_PACKAGE HDassert(dset); /* Check for chunked layout */ if(dset->shared->layout.type == H5D_CHUNKED) { hsize_t space_allocated; /* The number of bytes allocated for chunks */ hssize_t snelmts; /* Temporary holder for number of elements in dataspace */ hsize_t nelmts; /* Number of elements in dataspace */ size_t dt_size; /* Size of datatype */ hsize_t full_size; /* The number of bytes in the dataset when fully populated */ /* For chunked layout set the space status by the storage size */ /* Get the dataset's dataspace */ HDassert(dset->shared->space); /* Get the total number of elements in dataset's dataspace */ if((snelmts = H5S_GET_EXTENT_NPOINTS(dset->shared->space)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to retrieve number of elements in dataspace") nelmts = (hsize_t)snelmts; /* Get the size of the dataset's datatype */ if(0 == (dt_size = H5T_GET_SIZE(dset->shared->type))) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to retrieve size of datatype") /* Compute the maximum size of the dataset in bytes */ full_size = nelmts * dt_size; /* Check for overflow during multiplication */ if(nelmts != (full_size / dt_size)) HGOTO_ERROR(H5E_DATASET, H5E_OVERFLOW, FAIL, "size of dataset's storage overflowed") /* Difficult to error check, since the error value is 0 and 0 is a valid value... :-/ */ if(H5D__get_storage_size(dset, &space_allocated) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get size of dataset's storage") /* Decide on how much of the space is allocated */ if(space_allocated == 0) *allocation = H5D_SPACE_STATUS_NOT_ALLOCATED; else if(space_allocated == full_size) *allocation = H5D_SPACE_STATUS_ALLOCATED; else *allocation = H5D_SPACE_STATUS_PART_ALLOCATED; } /* end if */ else { /* For non-chunked layouts set space status by result of is_space_alloc * function */ if(dset->shared->layout.ops->is_space_alloc(&dset->shared->layout.storage)) *allocation = H5D_SPACE_STATUS_ALLOCATED; else *allocation = H5D_SPACE_STATUS_NOT_ALLOCATED; } /* end else */ done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__get_space_status() */ /*------------------------------------------------------------------------- * Function: H5D__new * * Purpose: Creates a new, empty dataset structure * * Return: Success: Pointer to a new dataset descriptor. * Failure: NULL *------------------------------------------------------------------------- */ static H5D_shared_t * H5D__new(hid_t dcpl_id, hid_t dapl_id, hbool_t creating, hbool_t vl_type) { H5D_shared_t *new_dset = NULL; /* New dataset object */ H5P_genplist_t *plist; /* Property list created */ H5D_shared_t *ret_value = NULL; /* Return value */ FUNC_ENTER_STATIC /* Allocate new shared dataset structure */ if(NULL == (new_dset = H5FL_MALLOC(H5D_shared_t))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") /* Copy the default dataset information */ H5MM_memcpy(new_dset, &H5D_def_dset, sizeof(H5D_shared_t)); /* If we are using the default dataset creation property list, during creation * don't bother to copy it, just increment the reference count */ if(!vl_type && creating && dcpl_id == H5P_DATASET_CREATE_DEFAULT) { if(H5I_inc_ref(dcpl_id, FALSE) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINC, NULL, "can't increment default DCPL ID") new_dset->dcpl_id = dcpl_id; } /* end if */ else { /* Get the property list */ if(NULL == (plist = (H5P_genplist_t *)H5I_object(dcpl_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a property list") new_dset->dcpl_id = H5P_copy_plist(plist, FALSE); } /* end else */ if(!vl_type && creating && dapl_id == H5P_DATASET_ACCESS_DEFAULT) { if(H5I_inc_ref(dapl_id, FALSE) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINC, NULL, "can't increment default DAPL ID") new_dset->dapl_id = dapl_id; } /* end if */ else { /* Get the property list */ if(NULL == (plist = (H5P_genplist_t *)H5I_object(dapl_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a property list") new_dset->dapl_id = H5P_copy_plist(plist, FALSE); } /* end else */ /* Set return value */ ret_value = new_dset; done: if(ret_value == NULL) if(new_dset != NULL) { if(new_dset->dcpl_id != 0 && H5I_dec_ref(new_dset->dcpl_id) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTDEC, NULL, "can't decrement temporary datatype ID") if(new_dset->dapl_id != 0 && H5I_dec_ref(new_dset->dapl_id) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTDEC, NULL, "can't decrement temporary datatype ID") new_dset = H5FL_FREE(H5D_shared_t, new_dset); } /* end if */ FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__new() */ /*------------------------------------------------------------------------- * Function: H5D__init_type * * Purpose: Copy a datatype for a dataset's use, performing all the * necessary adjustments, etc. * * Return: Success: SUCCEED * Failure: FAIL *------------------------------------------------------------------------- */ static herr_t H5D__init_type(H5F_t *file, const H5D_t *dset, hid_t type_id, const H5T_t *type) { htri_t relocatable; /* Flag whether the type is relocatable */ htri_t immutable; /* Flag whether the type is immutable */ hbool_t use_at_least_v18; /* Flag indicating to use at least v18 format versions */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC /* Sanity checking */ HDassert(file); HDassert(dset); HDassert(type); /* Check whether the datatype is relocatable */ if((relocatable = H5T_is_relocatable(type)) < 0) HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "can't check datatype?") /* Check whether the datatype is immutable */ if((immutable = H5T_is_immutable(type)) < 0) HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "can't check datatype?") /* To use at least v18 format versions or not */ use_at_least_v18 = (H5F_LOW_BOUND(file) >= H5F_LIBVER_V18); /* Copy the datatype if it's a custom datatype or if it'll change when its location is changed */ if(!immutable || relocatable || use_at_least_v18) { /* Copy datatype for dataset */ if((dset->shared->type = H5T_copy(type, H5T_COPY_ALL)) == NULL) HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "can't copy datatype") /* Convert a datatype (if committed) to a transient type if the committed datatype's file * location is different from the file location where the dataset will be created. */ if(H5T_convert_committed_datatype(dset->shared->type, file) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "can't get shared datatype info") /* Mark any datatypes as being on disk now */ if(H5T_set_loc(dset->shared->type, H5F_VOL_OBJ(file), H5T_LOC_DISK) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "can't set datatype location") /* Set the version for datatype */ if(H5T_set_version(file, dset->shared->type) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, FAIL, "can't set version of datatype") /* Get a datatype ID for the dataset's datatype */ if((dset->shared->type_id = H5I_register(H5I_DATATYPE, dset->shared->type, FALSE)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTREGISTER, FAIL, "unable to register type") } /* end if */ /* Not a custom datatype, just use it directly */ else { if(H5I_inc_ref(type_id, FALSE) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINC, FAIL, "Can't increment datatype ID") /* Use existing datatype */ dset->shared->type_id = type_id; dset->shared->type = (H5T_t *)type; /* (Cast away const OK - QAK) */ } /* end else */ done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__init_type() */ /*------------------------------------------------------------------------- * Function: H5D__cache_dataspace_info * * Purpose: Cache dataspace info for a dataset * * Return: Success: SUCCEED * Failure: FAIL *------------------------------------------------------------------------- */ static herr_t H5D__cache_dataspace_info(const H5D_t *dset) { int sndims; /* Signed number of dimensions of dataspace rank */ unsigned u; /* Local index value */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC /* Sanity checking */ HDassert(dset); /* Cache info for dataset's dataspace */ if((sndims = H5S_get_simple_extent_dims(dset->shared->space, dset->shared->curr_dims, dset->shared->max_dims)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't cache dataspace dimensions") dset->shared->ndims = (unsigned)sndims; /* Compute the initial 'power2up' values */ for(u = 0; u < dset->shared->ndims; u++) { hsize_t scaled_power2up; /* Scaled value, rounded to next power of 2 */ if( !(scaled_power2up = H5VM_power2up(dset->shared->curr_dims[u])) ) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to get the next power of 2") dset->shared->curr_power2up[u] = scaled_power2up; } done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__cache_dataspace_info() */ /*------------------------------------------------------------------------- * Function: H5D__init_space * * Purpose: Copy a dataspace for a dataset's use, performing all the * necessary adjustments, etc. * * Return: Success: SUCCEED * Failure: FAIL *------------------------------------------------------------------------- */ static herr_t H5D__init_space(H5F_t *file, const H5D_t *dset, const H5S_t *space) { herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC /* Sanity checking */ HDassert(file); HDassert(dset); HDassert(space); /* Copy dataspace for dataset */ if(NULL == (dset->shared->space = H5S_copy(space, FALSE, TRUE))) HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "can't copy dataspace") /* Cache the dataset's dataspace info */ if(H5D__cache_dataspace_info(dset) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "can't cache dataspace info") /* Set the version for dataspace */ if(H5S_set_version(file, dset->shared->space) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, FAIL, "can't set latest version of datatype") /* Set the dataset's dataspace to 'all' selection */ if(H5S_select_all(dset->shared->space, TRUE) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, FAIL, "unable to set all selection") done: FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__init_space() */ /*------------------------------------------------------------------------- * Function: H5D__use_minimized_dset_headers * * Purpose: Compartmentalize check for file- or dcpl-set values indicating * to create a "minimized" dataset object header. * Upon success, write resulting value to out pointer `minimize`. * * Return: Success: SUCCEED (0) (non-negative value) * Failure: FAIL (-1) (negative value) * * Programmer: Jacob Smith * 16 August 2018 *------------------------------------------------------------------------- */ static herr_t H5D__use_minimized_dset_headers(H5F_t *file, hbool_t *minimize) { herr_t ret_value = SUCCEED; FUNC_ENTER_NOAPI_NOINIT; HDassert(file); HDassert(minimize); /* Get the dataset object header minimize flag for this call */ if(H5CX_get_dset_min_ohdr_flag(minimize) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get dataset object header minimize flag from API context") if(FALSE == *minimize) *minimize = H5F_get_min_dset_ohdr(file); done: if(FAIL == ret_value) *minimize = FALSE; FUNC_LEAVE_NOAPI(ret_value); } /* H5D__use_minimized_dset_headers */ /*------------------------------------------------------------------------- * Function: H5D__calculate_minimium_header_size * * Purpose: Calculate the size required for the minimized object header. * * Return: Success: Positive value > 0 * Failure: 0 * * Programmer: Jacob Smith * 16 August 2018 *------------------------------------------------------------------------- */ static size_t H5D__calculate_minimum_header_size(H5F_t *file, H5D_t *dset, H5O_t *ohdr) { H5T_t *type = NULL; H5O_fill_t *fill_prop = NULL; hbool_t use_at_least_v18 = FALSE; const char continuation[1] = ""; /* requred for work-around */ size_t get_value = 0; size_t ret_value = 0; FUNC_ENTER_NOAPI_NOINIT; HDassert(file); HDassert(dset); HDassert(ohdr); type = dset->shared->type; fill_prop = &(dset->shared->dcpl_cache.fill); use_at_least_v18 = (H5F_LOW_BOUND(file) >= H5F_LIBVER_V18); /* Datatype message size */ get_value = H5O_msg_size_oh(file, ohdr, H5O_DTYPE_ID, type, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "Can't get size of datatype message") ret_value += get_value; /* Shared Dataspace message size */ get_value = H5O_msg_size_oh(file, ohdr, H5O_SDSPACE_ID, dset->shared->space, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't get size of dataspace message") ret_value += get_value; /* "Layout" message size */ get_value = H5O_msg_size_oh(file, ohdr, H5O_LAYOUT_ID, &dset->shared->layout, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't get size of layout message") ret_value += get_value; /* Fill Value message size */ get_value = H5O_msg_size_oh(file, ohdr, H5O_FILL_NEW_ID, fill_prop, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't get size of fill value message") ret_value += get_value; /* "Continuation" message size */ /* message pointer "continuation" is unused by raw get function, however, * a null pointer would be intercepted by an assert in H5O_msg_size_oh(). */ get_value = H5O_msg_size_oh(file, ohdr, H5O_CONT_ID, continuation, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't get size of continuation message") ret_value += get_value; /* Fill Value (backwards compatability) message size */ if(fill_prop->buf && !use_at_least_v18) { H5O_fill_t old_fill_prop; /* Copy for writing "old" fill value */ /* Shallow copy the fill value property */ /* guards against shared component modification */ H5MM_memcpy(&old_fill_prop, fill_prop, sizeof(old_fill_prop)); if (H5O_msg_reset_share(H5O_FILL_ID, &old_fill_prop) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't reset the copied fill property") get_value = H5O_msg_size_oh(file, ohdr, H5O_FILL_ID, &old_fill_prop, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't get size of fill value (backwards compat) message") ret_value += get_value; } /* Filter/Pipeline message size */ if(H5D_CHUNKED == dset->shared->layout.type) { H5O_pline_t *pline = &dset->shared->dcpl_cache.pline; if(pline->nused > 0) { get_value = H5O_msg_size_oh(file, ohdr, H5O_PLINE_ID, pline, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't get size of filter message") ret_value += get_value; } } /* External File Link message size */ if(dset->shared->dcpl_cache.efl.nused > 0) { get_value = H5O_msg_size_oh(file, ohdr, H5O_EFL_ID, &dset->shared->dcpl_cache.efl, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't get size of external file link message") ret_value += get_value; } /* Modification Time message size */ if(H5O_HDR_STORE_TIMES & H5O_OH_GET_FLAGS(ohdr)) { HDassert(H5O_OH_GET_VERSION(ohdr) >= 1); /* 1 :: H5O_VERSION_1 (H5Opkg.h) */ if(H5O_OH_GET_VERSION(ohdr) == 1) { /* v1 object headers store modification time as a message */ time_t mtime; get_value = H5O_msg_size_oh(file, ohdr, H5O_MTIME_NEW_ID, &mtime, 0); if (get_value == 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, 0, "can't get size of modification time message") ret_value += get_value; } } done: FUNC_LEAVE_NOAPI(ret_value); } /* H5D__calculate_minimum_header_size */ /*------------------------------------------------------------------------- * Function: H5D__prepare_minimized_oh * * Purpose: Create an object header (H5O_t) allocated with the smallest * possible size. * * Return: Success: SUCCEED (0) (non-negative value) * Failure: FAIL (-1) (negative value) * * Programmer: Jacob Smith * 16 August 2018 *------------------------------------------------------------------------- */ static herr_t H5D__prepare_minimized_oh(H5F_t *file, H5D_t *dset, H5O_loc_t *oloc) { H5O_t *oh = NULL; size_t ohdr_size = 0; herr_t ret_value = SUCCEED; FUNC_ENTER_NOAPI_NOINIT; HDassert(file); HDassert(dset); HDassert(oloc); oh = H5O__create_ohdr(file, dset->shared->dcpl_id); if(NULL == oh) HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "can't instantiate object header") ohdr_size = H5D__calculate_minimum_header_size(file, dset, oh); if (ohdr_size == 0) HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "computed header size is invalid") /* Special allocation of space for compact datsets is handled by the call here. */ if(H5O__apply_ohdr(file, oh, dset->shared->dcpl_id, ohdr_size, (size_t)1, oloc) == FAIL) HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "can't apply object header to file") done: FUNC_LEAVE_NOAPI(ret_value); } /* H5D__prepare_minimized_oh */ /*------------------------------------------------------------------------- * Function: H5D__update_oh_info * * Purpose: Create and fill object header for dataset * * Return: Success: SUCCEED * Failure: FAIL *------------------------------------------------------------------------- */ static herr_t H5D__update_oh_info(H5F_t *file, H5D_t *dset, hid_t dapl_id) { H5O_t *oh = NULL; /* Pointer to dataset's object header */ size_t ohdr_size = H5D_MINHDR_SIZE; /* Size of dataset's object header */ H5O_loc_t *oloc = NULL; /* Dataset's object location */ H5O_layout_t *layout; /* Dataset's layout information */ H5T_t *type; /* Dataset's datatype */ H5O_fill_t *fill_prop; /* Pointer to dataset's fill value information */ H5D_fill_value_t fill_status; /* Fill value status */ hbool_t fill_changed = FALSE; /* Flag indicating the fill value was changed */ hbool_t layout_init = FALSE; /* Flag to indicate that chunk information was initialized */ hbool_t use_at_least_v18; /* Flag indicating to use at least v18 format versions */ hbool_t minimize_header = FALSE; herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC /* Sanity checking */ HDassert(file); HDassert(dset); /* Set some local variables, for convenience */ oloc = &dset->oloc; layout = &dset->shared->layout; type = dset->shared->type; fill_prop = &dset->shared->dcpl_cache.fill; /* To use at least v18 format versions or not */ use_at_least_v18 = (H5F_LOW_BOUND(file) >= H5F_LIBVER_V18); /* Retrieve "defined" status of fill value */ if(H5P_is_fill_value_defined(fill_prop, &fill_status) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't tell if fill value defined") /* Special case handling for variable-length types */ if(H5T_detect_class(type, H5T_VLEN, FALSE)) { /* If the default fill value is chosen for variable-length types, always write it */ if(fill_prop->fill_time == H5D_FILL_TIME_IFSET && fill_status == H5D_FILL_VALUE_DEFAULT) { /* Update dataset creation property */ fill_prop->fill_time = H5D_FILL_TIME_ALLOC; /* Note that the fill value changed */ fill_changed = TRUE; } /* end if */ /* Don't allow never writing fill values with variable-length types */ if(fill_prop->fill_time == H5D_FILL_TIME_NEVER) HGOTO_ERROR(H5E_DATASET, H5E_UNSUPPORTED, FAIL, "Dataset doesn't support VL datatype when fill value is not defined") } /* end if */ /* Determine whether fill value is defined or not */ if(fill_status == H5D_FILL_VALUE_DEFAULT || fill_status == H5D_FILL_VALUE_USER_DEFINED) { /* Convert fill value buffer to dataset's datatype */ if(fill_prop->buf && fill_prop->size > 0 && H5O_fill_convert(fill_prop, type, &fill_changed) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to convert fill value to dataset type") fill_prop->fill_defined = TRUE; } else if(fill_status == H5D_FILL_VALUE_UNDEFINED) fill_prop->fill_defined = FALSE; else HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to determine if fill value is defined") /* Check for invalid fill & allocation time setting */ if(fill_prop->fill_defined == FALSE && fill_prop->fill_time == H5D_FILL_TIME_ALLOC) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "fill value writing on allocation set, but no fill value defined") /* Check if the fill value info changed */ if(fill_changed) { H5P_genplist_t *dc_plist; /* Dataset's creation property list */ /* Get dataset's property list object */ HDassert(dset->shared->dcpl_id != H5P_DATASET_CREATE_DEFAULT); if(NULL == (dc_plist = (H5P_genplist_t *)H5I_object(dset->shared->dcpl_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "can't get dataset creation property list") /* Update dataset creation property */ if(H5P_set(dc_plist, H5D_CRT_FILL_VALUE_NAME, fill_prop) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTSET, FAIL, "can't set fill value info") } /* end if */ if(H5D__use_minimized_dset_headers(file, &minimize_header) == FAIL) HGOTO_ERROR(H5E_ARGS, H5E_CANTGET, FAIL, "can't get minimize settings") if(TRUE == minimize_header) { if(H5D__prepare_minimized_oh(file, dset, oloc) == FAIL) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "can't create minimized dataset object header") } else { /* Add the dataset's raw data size to the size of the header, if the * raw data will be stored as compact */ if(H5D_COMPACT == layout->type) ohdr_size += layout->storage.u.compact.size; /* Create an object header for the dataset */ if(H5O_create(file, ohdr_size, (size_t)1, dset->shared->dcpl_id, oloc/*out*/) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to create dataset object header") } /* If use minimum/standard object header space */ HDassert(file == dset->oloc.file); /* Pin the object header */ if(NULL == (oh = H5O_pin(oloc))) HGOTO_ERROR(H5E_DATASET, H5E_CANTPIN, FAIL, "unable to pin dataset object header") /* Write the dataspace header message */ if(H5S_append(file, oh, dset->shared->space) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to update dataspace header message") /* Write the datatype header message */ if(H5O_msg_append_oh(file, oh, H5O_DTYPE_ID, H5O_MSG_FLAG_CONSTANT, 0, type) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to update datatype header message") /* Write new fill value message */ if(H5O_msg_append_oh(file, oh, H5O_FILL_NEW_ID, H5O_MSG_FLAG_CONSTANT, 0, fill_prop) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to update new fill value header message") /* If there is valid information for the old fill value struct, add it */ /* (only if we aren't using v18 format versions and above */ if(fill_prop->buf && !use_at_least_v18) { H5O_fill_t old_fill_prop; /* Copy of fill value property, for writing as "old" fill value */ /* Shallow copy the fill value property */ /* (we only want to make certain that the shared component isn't modified) */ H5MM_memcpy(&old_fill_prop, fill_prop, sizeof(old_fill_prop)); /* Reset shared component info */ H5O_msg_reset_share(H5O_FILL_ID, &old_fill_prop); /* Write old fill value */ if(H5O_msg_append_oh(file, oh, H5O_FILL_ID, H5O_MSG_FLAG_CONSTANT, 0, &old_fill_prop) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to update old fill value header message") } /* end if */ /* Update/create the layout (and I/O pipeline & EFL) messages */ if(H5D__layout_oh_create(file, oh, dset, dapl_id) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to update layout/pline/efl header message") /* Indicate that the layout information was initialized */ layout_init = TRUE; #ifdef H5O_ENABLE_BOGUS { H5P_genplist_t *dc_plist; /* Dataset's creation property list */ /* Get dataset's property list object */ if(NULL == (dc_plist = (H5P_genplist_t *)H5I_object(dset->shared->dcpl_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "can't get dataset creation property list") /* Check whether to add a "bogus" message */ if( (H5P_exist_plist(dc_plist, H5O_BOGUS_MSG_FLAGS_NAME) > 0) && (H5P_exist_plist(dc_plist, H5O_BOGUS_MSG_ID_NAME) > 0) ) { uint8_t bogus_flags = 0; /* Flags for creating "bogus" message */ unsigned bogus_id; /* "bogus" ID */ /* Retrieve "bogus" message ID */ if(H5P_get(dc_plist, H5O_BOGUS_MSG_ID_NAME, &bogus_id) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get bogus ID options") /* Retrieve "bogus" message flags */ if(H5P_get(dc_plist, H5O_BOGUS_MSG_FLAGS_NAME, &bogus_flags) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get bogus message options") /* Add a "bogus" message (for error testing). */ if(H5O_bogus_oh(file, oh, bogus_id, (unsigned)bogus_flags) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to create 'bogus' message") } /* end if */ } #endif /* H5O_ENABLE_BOGUS */ /* Add a modification time message, if using older format. */ /* (If using v18 format versions and above, the the modification time is part of the object * header and doesn't use a separate message -QAK) */ if(!use_at_least_v18) if(H5O_touch_oh(file, oh, TRUE) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to update modification time message") done: /* Release pointer to object header itself */ if(oh != NULL) if(H5O_unpin(oh) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTUNPIN, FAIL, "unable to unpin dataset object header") /* Error cleanup */ if(ret_value < 0) if(layout_init) /* Destroy the layout information for the dataset */ if(dset->shared->layout.ops->dest && (dset->shared->layout.ops->dest)(dset) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTRELEASE, FAIL, "unable to destroy layout info") FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__update_oh_info() */ /*-------------------------------------------------------------------------- * Function: H5D__build_file_prefix * * Purpose: Determine the file prefix to be used and store * it in file_prefix. Stores an empty string if no prefix * should be used. * * Return: SUCCEED/FAIL *-------------------------------------------------------------------------- */ static herr_t H5D__build_file_prefix(const H5D_t *dset, H5F_prefix_open_t prefix_type, char **file_prefix /*out*/) { const char *prefix = NULL; /* prefix used to look for the file */ char *filepath = NULL; /* absolute path of directory the HDF5 file is in */ size_t filepath_len; /* length of file path */ size_t prefix_len; /* length of prefix */ size_t file_prefix_len; /* length of expanded prefix */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC /* Sanity checks */ HDassert(dset); HDassert(dset->oloc.file); filepath = H5F_EXTPATH(dset->oloc.file); HDassert(filepath); /* XXX: Future thread-safety note - getenv is not required * to be reentrant. */ if(H5F_PREFIX_VDS == prefix_type) { prefix = H5D_prefix_vds_env; if(prefix == NULL || *prefix == '\0') if(H5CX_get_vds_prefix(&prefix) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get the prefix for vds file") } /* end if */ else if(H5F_PREFIX_EFILE == prefix_type) { prefix = H5D_prefix_ext_env; if(prefix == NULL || *prefix == '\0') if(H5CX_get_ext_file_prefix(&prefix) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get the prefix for the external file") } /* end else-if */ else HGOTO_ERROR(H5E_DATASET, H5E_BADTYPE, FAIL, "prefix name is not sensible") /* Prefix has to be checked for NULL / empty string again because the * code above might have updated it. */ if(prefix == NULL || *prefix == '\0' || HDstrcmp(prefix, ".") == 0) { /* filename is interpreted as relative to the current directory, * does not need to be expanded */ *file_prefix = NULL; } /* end if */ else { if(HDstrncmp(prefix, "${ORIGIN}", HDstrlen("${ORIGIN}")) == 0) { /* Replace ${ORIGIN} at beginning of prefix by directory of HDF5 file */ filepath_len = HDstrlen(filepath); prefix_len = HDstrlen(prefix); file_prefix_len = filepath_len + prefix_len - HDstrlen("${ORIGIN}") + 1; if(NULL == (*file_prefix = (char *)H5MM_malloc(file_prefix_len))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "unable to allocate buffer") HDsnprintf(*file_prefix, file_prefix_len, "%s%s", filepath, prefix + HDstrlen("${ORIGIN}")); } /* end if */ else { if(NULL == (*file_prefix = (char *)H5MM_strdup(prefix))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") } /* end else */ } /* end else */ done: FUNC_LEAVE_NOAPI(ret_value) } /* H5D__build_file_prefix() */ /*------------------------------------------------------------------------- * Function: H5D__create * * Purpose: Creates a new dataset with name NAME in file F and associates * with it a datatype TYPE for each element as stored in the * file, dimensionality information or dataspace SPACE, and * other miscellaneous properties CREATE_PARMS. All arguments * are deep-copied before being associated with the new dataset, * so the caller is free to subsequently modify them without * affecting the dataset. * * Return: Success: Pointer to a new dataset * Failure: NULL *------------------------------------------------------------------------- */ H5D_t * H5D__create(H5F_t *file, hid_t type_id, const H5S_t *space, hid_t dcpl_id, hid_t dapl_id) { H5T_t *type = NULL; /* Datatype for dataset (VOL pointer) */ H5T_t *dt = NULL; /* Datatype for dataset (non-VOL pointer) */ H5D_t *new_dset = NULL; H5P_genplist_t *dc_plist = NULL; /* New Property list */ hbool_t has_vl_type = FALSE; /* Flag to indicate a VL-type for dataset */ hbool_t layout_init = FALSE; /* Flag to indicate that chunk information was initialized */ hbool_t layout_copied = FALSE; /* Flag to indicate that layout message was copied */ hbool_t fill_copied = FALSE; /* Flag to indicate that fill-value message was copied */ hbool_t pline_copied = FALSE; /* Flag to indicate that pipeline message was copied */ hbool_t efl_copied = FALSE; /* Flag to indicate that external file list message was copied */ H5G_loc_t dset_loc; /* Dataset location */ H5D_t *ret_value = NULL; /* Return value */ FUNC_ENTER_PACKAGE /* check args */ HDassert(file); HDassert(H5I_DATATYPE == H5I_get_type(type_id)); HDassert(space); HDassert(H5I_GENPROP_LST == H5I_get_type(dcpl_id)); /* Get the dataset's datatype */ if(NULL == (dt = (H5T_t *)H5I_object(type_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "not a datatype") /* If this is a named datatype, get the pointer via the VOL plugin */ type = H5T_get_actual_type(dt); /* Check if the datatype is "sensible" for use in a dataset */ if(H5T_is_sensible(type) != TRUE) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "datatype is not sensible") /* Check if the datatype is/contains a VL-type */ if(H5T_detect_class(type, H5T_VLEN, FALSE)) has_vl_type = TRUE; /* Check if the dataspace has an extent set (or is NULL) */ if(!H5S_has_extent(space)) HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, NULL, "dataspace extent has not been set.") /* Initialize the dataset object */ if(NULL == (new_dset = H5FL_CALLOC(H5D_t))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") /* Set up & reset dataset location */ dset_loc.oloc = &(new_dset->oloc); dset_loc.path = &(new_dset->path); H5G_loc_reset(&dset_loc); /* Initialize the shared dataset space */ if(NULL == (new_dset->shared = H5D__new(dcpl_id, dapl_id, TRUE, has_vl_type))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") /* Copy & initialize datatype for dataset */ if(H5D__init_type(file, new_dset, type_id, type) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "can't copy datatype") /* Copy & initialize dataspace for dataset */ if(H5D__init_space(file, new_dset, space) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "can't copy dataspace") /* Set the dataset's checked_filters flag to enable writing */ new_dset->shared->checked_filters = TRUE; /* Check if the dataset has a non-default DCPL & get important values, if so */ if(new_dset->shared->dcpl_id != H5P_DATASET_CREATE_DEFAULT) { H5O_layout_t *layout; /* Dataset's layout information */ H5O_pline_t *pline; /* Dataset's I/O pipeline information */ H5O_fill_t *fill; /* Dataset's fill value info */ H5O_efl_t *efl; /* Dataset's external file list info */ /* Check if the filters in the DCPL can be applied to this dataset */ if(H5Z_can_apply(new_dset->shared->dcpl_id, new_dset->shared->type_id) < 0) HGOTO_ERROR(H5E_ARGS, H5E_CANTINIT, NULL, "I/O filters can't operate on this dataset") /* Make the "set local" filter callbacks for this dataset */ if(H5Z_set_local(new_dset->shared->dcpl_id, new_dset->shared->type_id) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to set local filter parameters") /* Get new dataset's property list object */ if(NULL == (dc_plist = (H5P_genplist_t *)H5I_object(new_dset->shared->dcpl_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, NULL, "can't get dataset creation property list") /* Retrieve the properties we need */ pline = &new_dset->shared->dcpl_cache.pline; if(H5P_get(dc_plist, H5O_CRT_PIPELINE_NAME, pline) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, NULL, "can't retrieve pipeline filter") pline_copied = TRUE; layout = &new_dset->shared->layout; if(H5P_get(dc_plist, H5D_CRT_LAYOUT_NAME, layout) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, NULL, "can't retrieve layout") layout_copied = TRUE; fill = &new_dset->shared->dcpl_cache.fill; if(H5P_get(dc_plist, H5D_CRT_FILL_VALUE_NAME, fill) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, NULL, "can't retrieve fill value info") fill_copied = TRUE; efl = &new_dset->shared->dcpl_cache.efl; if(H5P_get(dc_plist, H5D_CRT_EXT_FILE_LIST_NAME, efl) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, NULL, "can't retrieve external file list") efl_copied = TRUE; /* Check that chunked layout is used if filters are enabled */ if(pline->nused > 0 && H5D_CHUNKED != layout->type) HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, NULL, "filters can only be used with chunked layout") /* Check if the alloc_time is the default and error out */ if(fill->alloc_time == H5D_ALLOC_TIME_DEFAULT) HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, NULL, "invalid space allocation state") /* Don't allow compact datasets to allocate space later */ if(layout->type == H5D_COMPACT && fill->alloc_time != H5D_ALLOC_TIME_EARLY) HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, NULL, "compact dataset must have early space allocation") } /* end if */ /* Set the version for the I/O pipeline message */ if(H5O_pline_set_version(file, &new_dset->shared->dcpl_cache.pline) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, NULL, "can't set latest version of I/O filter pipeline") /* Set the version for the fill message */ if(H5O_fill_set_version(file, &new_dset->shared->dcpl_cache.fill) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, NULL, "can't set latest version of fill value") /* Set the latest version for the layout message */ if(H5D__layout_set_version(file, &new_dset->shared->layout) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, NULL, "can't set latest version of layout") if(new_dset->shared->layout.version >= H5O_LAYOUT_VERSION_4) { /* Use latest indexing type for layout message version >= 4 */ if(H5D__layout_set_latest_indexing(&new_dset->shared->layout, new_dset->shared->space, &new_dset->shared->dcpl_cache) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, NULL, "can't set latest indexing") } /* end if */ /* Check if this dataset is going into a parallel file and set space allocation time */ if(H5F_HAS_FEATURE(file, H5FD_FEAT_ALLOCATE_EARLY)) new_dset->shared->dcpl_cache.fill.alloc_time = H5D_ALLOC_TIME_EARLY; /* Set the dataset's I/O operations */ if(H5D__layout_set_io_ops(new_dset) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to initialize I/O operations") /* Create the layout information for the new dataset */ if(new_dset->shared->layout.ops->construct && (new_dset->shared->layout.ops->construct)(file, new_dset) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to construct layout information") /* Update the dataset's object header info. */ if(H5D__update_oh_info(file, new_dset, new_dset->shared->dapl_id) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "can't update the metadata cache") /* Indicate that the layout information was initialized */ layout_init = TRUE; /* Set up append flush parameters for the dataset */ if(H5D__append_flush_setup(new_dset, new_dset->shared->dapl_id) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to set up flush append property") /* Set the external file prefix */ if(H5D__build_file_prefix(new_dset, H5F_PREFIX_EFILE, &new_dset->shared->extfile_prefix) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to initialize external file prefix") /* Set the VDS file prefix */ if(H5D__build_file_prefix(new_dset, H5F_PREFIX_VDS, &new_dset->shared->vds_prefix) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to initialize VDS prefix") /* Add the dataset to the list of opened objects in the file */ if(H5FO_top_incr(new_dset->oloc.file, new_dset->oloc.addr) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINC, NULL, "can't incr object ref. count") if(H5FO_insert(new_dset->oloc.file, new_dset->oloc.addr, new_dset->shared, TRUE) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINSERT, NULL, "can't insert dataset into list of open objects") new_dset->shared->fo_count = 1; /* Success */ ret_value = new_dset; done: if(!ret_value && new_dset && new_dset->shared) { if(new_dset->shared) { if(layout_init) if(new_dset->shared->layout.ops->dest && (new_dset->shared->layout.ops->dest)(new_dset) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTRELEASE, NULL, "unable to destroy layout info") if(pline_copied) if(H5O_msg_reset(H5O_PLINE_ID, &new_dset->shared->dcpl_cache.pline) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTRESET, NULL, "unable to reset I/O pipeline info") if(layout_copied) if(H5O_msg_reset(H5O_LAYOUT_ID, &new_dset->shared->layout) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTRESET, NULL, "unable to reset layout info") if(fill_copied) if(H5O_msg_reset(H5O_FILL_ID, &new_dset->shared->dcpl_cache.fill) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTRESET, NULL, "unable to reset fill-value info") if(efl_copied) if(H5O_msg_reset(H5O_EFL_ID, &new_dset->shared->dcpl_cache.efl) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTRESET, NULL, "unable to reset external file list info") if(new_dset->shared->space && H5S_close(new_dset->shared->space) < 0) HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, NULL, "unable to release dataspace") if(new_dset->shared->type && H5I_dec_ref(new_dset->shared->type_id) < 0) HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, NULL, "unable to release datatype") if(H5F_addr_defined(new_dset->oloc.addr)) { if(H5O_dec_rc_by_loc(&(new_dset->oloc)) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTDEC, NULL, "unable to decrement refcount on newly created object") if(H5O_close(&(new_dset->oloc), NULL) < 0) HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, NULL, "unable to release object header") if(file) { if(H5O_delete(file, new_dset->oloc.addr) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTDELETE, NULL, "unable to delete object header") } /* end if */ } /* end if */ if(new_dset->shared->dcpl_id != 0 && H5I_dec_ref(new_dset->shared->dcpl_id) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTDEC, NULL, "unable to decrement ref count on property list") if(new_dset->shared->dapl_id != 0 && H5I_dec_ref(new_dset->shared->dapl_id) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTDEC, NULL, "unable to decrement ref count on property list") new_dset->shared->extfile_prefix = (char *)H5MM_xfree(new_dset->shared->extfile_prefix); new_dset->shared->vds_prefix = (char *)H5MM_xfree(new_dset->shared->vds_prefix); new_dset->shared = H5FL_FREE(H5D_shared_t, new_dset->shared); } /* end if */ new_dset->oloc.file = NULL; new_dset = H5FL_FREE(H5D_t, new_dset); } /* end if */ FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__create() */ /*------------------------------------------------------------------------- * Function: H5D__open_name * * Purpose: Opens an existing dataset by name. * * Return: Success: Ptr to a new dataset. * Failure: NULL *------------------------------------------------------------------------- */ H5D_t * H5D__open_name(const H5G_loc_t *loc, const char *name, hid_t dapl_id) { H5D_t *dset = NULL; H5G_loc_t dset_loc; /* Object location of dataset */ H5G_name_t path; /* Dataset group hier. path */ H5O_loc_t oloc; /* Dataset object location */ H5O_type_t obj_type; /* Type of object at location */ hbool_t loc_found = FALSE; /* Location at 'name' found */ H5D_t *ret_value = NULL; /* Return value */ FUNC_ENTER_PACKAGE /* Check args */ HDassert(loc); HDassert(name); /* Set up dataset location to fill in */ dset_loc.oloc = &oloc; dset_loc.path = &path; H5G_loc_reset(&dset_loc); /* Find the dataset object */ if(H5G_loc_find(loc, name, &dset_loc) < 0) HGOTO_ERROR(H5E_DATASET, H5E_NOTFOUND, NULL, "not found") loc_found = TRUE; /* Check that the object found is the correct type */ if(H5O_obj_type(&oloc, &obj_type) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, NULL, "can't get object type") if(obj_type != H5O_TYPE_DATASET) HGOTO_ERROR(H5E_DATASET, H5E_BADTYPE, NULL, "not a dataset") /* Open the dataset */ if(NULL == (dset = H5D_open(&dset_loc, dapl_id))) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "can't open dataset") /* Set return value */ ret_value = dset; done: if(!ret_value) if(loc_found && H5G_loc_free(&dset_loc) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTRELEASE, NULL, "can't free location") FUNC_LEAVE_NOAPI(ret_value) } /* end H5D__open_name() */ /* *------------------------------------------------------------------------- * Function: H5D_open * * Purpose: Checks if dataset is already open, or opens a dataset for * access. * * Return: Success: Dataset ID * Failure: FAIL *------------------------------------------------------------------------- */ H5D_t * H5D_open(const H5G_loc_t *loc, hid_t dapl_id) { H5D_shared_t *shared_fo = NULL; H5D_t *dataset = NULL; char *extfile_prefix = NULL; /* Expanded external file prefix */ char *vds_prefix = NULL; /* Expanded vds prefix */ H5D_t *ret_value = NULL; /* Return value */ FUNC_ENTER_NOAPI(NULL) /* check args */ HDassert(loc); /* Allocate the dataset structure */ if(NULL == (dataset = H5FL_CALLOC(H5D_t))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") /* Shallow copy (take ownership) of the object location object */ if(H5O_loc_copy(&(dataset->oloc), loc->oloc, H5_COPY_SHALLOW) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, NULL, "can't copy object location") /* Shallow copy (take ownership) of the group hier. path */ if(H5G_name_copy(&(dataset->path), loc->path, H5_COPY_SHALLOW) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, NULL, "can't copy path") /* Get the external file prefix */ if(H5D__build_file_prefix(dataset, H5F_PREFIX_EFILE, &extfile_prefix) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to initialize external file prefix") /* Get the VDS prefix */ if(H5D__build_file_prefix(dataset, H5F_PREFIX_VDS, &vds_prefix) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, NULL, "unable to initialize VDS prefix") /* Check if dataset was already open */ if(NULL == (shared_fo = (H5D_shared_t *)H5FO_opened(dataset->oloc.file, dataset->oloc.addr))) { /* Clear any errors from H5FO_opened() */ H5E_clear_stack(NULL); /* Open the dataset object */ if(H5D__open_oid(dataset, dapl_id) < 0) HGOTO_ERROR(H5E_DATASET, H5E_NOTFOUND, NULL, "not found") /* Add the dataset to the list of opened objects in the file */ if(H5FO_insert(dataset->oloc.file, dataset->oloc.addr, dataset->shared, FALSE) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINSERT, NULL, "can't insert dataset into list of open objects") /* Increment object count for the object in the top file */ if(H5FO_top_incr(dataset->oloc.file, dataset->oloc.addr) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINC, NULL, "can't increment object count") /* We're the first dataset to use the the shared info */ dataset->shared->fo_count = 1; /* Set the external file prefix */ dataset->shared->extfile_prefix = extfile_prefix; /* Prevent string from being freed during done: */ extfile_prefix = NULL; /* Set the vds file prefix */ dataset->shared->vds_prefix = vds_prefix; /* Prevent string from being freed during done: */ vds_prefix = NULL; } /* end if */ else { /* Point to shared info */ dataset->shared = shared_fo; /* Increment # of datasets using shared information */ shared_fo->fo_count++; /* Check whether the external file prefix of the already open dataset * matches the new external file prefix */ if(extfile_prefix && dataset->shared->extfile_prefix) { if(HDstrcmp(extfile_prefix, dataset->shared->extfile_prefix) != 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, NULL, "new external file prefix does not match external file prefix of already open dataset") } else { if(extfile_prefix || dataset->shared->extfile_prefix) HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, NULL, "new external file prefix does not match external file prefix of already open dataset") } /* Check if the object has been opened through the top file yet */ if(H5FO_top_count(dataset->oloc.file, dataset->oloc.addr) == 0) { /* Open the object through this top file */ if(H5O_open(&(dataset->oloc)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, NULL, "unable to open object header") } /* end if */ /* Increment object count for the object in the top file */ if(H5FO_top_incr(dataset->oloc.file, dataset->oloc.addr) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINC, NULL, "can't increment object count") } /* end else */ /* Set the dataset to return */ ret_value = dataset; done: extfile_prefix = (char *)H5MM_xfree(extfile_prefix); vds_prefix = (char *)H5MM_xfree(vds_prefix); if(ret_value == NULL) { /* Free the location--casting away const*/ if(dataset) { if(shared_fo == NULL && dataset->shared) { /* Need to free shared fo */ dataset->shared->extfile_prefix = (char *)H5MM_xfree(dataset->shared->extfile_prefix); dataset->shared->vds_prefix = (char *)H5MM_xfree(dataset->shared->vds_prefix); dataset->shared = H5FL_FREE(H5D_shared_t, dataset->shared); } H5O_loc_free(&(dataset->oloc)); H5G_name_free(&(dataset->path)); dataset = H5FL_FREE(H5D_t, dataset); } /* end if */ if(shared_fo) shared_fo->fo_count--; } /* end if */ FUNC_LEAVE_NOAPI(ret_value) } /* end H5D_open() */ /* *------------------------------------------------------------------------- * Function: H5D__flush_append_setup * * Purpose: Set the append flush parameters for a dataset * * Return: Non-negative on success/Negative on failure *------------------------------------------------------------------------- */ static herr_t H5D__append_flush_setup(H5D_t *dset, hid_t dapl_id) { herr_t ret_value = SUCCEED; /* return value */ FUNC_ENTER_STATIC /* Check args */ HDassert(dset); HDassert(dset->shared); /* Set default append flush values */ HDmemset(&dset->shared->append_flush, 0, sizeof(dset->shared->append_flush)); /* If the dataset is chunked and there is a non-default DAPL */ if(dapl_id != H5P_DATASET_ACCESS_DEFAULT && dset->shared->layout.type == H5D_CHUNKED) { H5P_genplist_t *dapl; /* data access property list object pointer */ /* Get dataset access property list */ if(NULL == (dapl = (H5P_genplist_t *)H5I_object(dapl_id))) HGOTO_ERROR(H5E_ATOM, H5E_BADATOM, FAIL, "can't find object for dapl ID"); /* Check if append flush property exists */ if(H5P_exist_plist(dapl, H5D_ACS_APPEND_FLUSH_NAME) > 0) { H5D_append_flush_t info; /* Get append flush property */ if(H5P_get(dapl, H5D_ACS_APPEND_FLUSH_NAME, &info) < 0) HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, FAIL, "can't get append flush info") if(info.ndims > 0) { hsize_t curr_dims[H5S_MAX_RANK]; /* current dimension sizes */ hsize_t max_dims[H5S_MAX_RANK]; /* current dimension sizes */ int rank; /* dataspace # of dimensions */ unsigned u; /* local index variable */ /* Get dataset rank */ if((rank = H5S_get_simple_extent_dims(dset->shared->space, curr_dims, max_dims)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get dataset dimensions") if(info.ndims != (unsigned)rank) HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "boundary dimension rank does not match dataset rank") /* Validate boundary sizes */ for(u = 0; u < info.ndims; u++) if(info.boundary[u] != 0) /* when a non-zero boundary is set */ /* the dimension is extendible? */ if(max_dims[u] != H5S_UNLIMITED && max_dims[u] == curr_dims[u]) break; /* At least one boundary dimension is not extendible */ if(u != info.ndims) HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "boundary dimension is not valid") /* Copy append flush settings */ dset->shared->append_flush.ndims = info.ndims; dset->shared->append_flush.func = info.func; dset->shared->append_flush.udata = info.udata; H5MM_memcpy(dset->shared->append_flush.boundary, info.boundary, sizeof(info.boundary)); } /* end if */ } /* end if */ } /* end if */ done: FUNC_LEAVE_NOAPI(ret_value) } /* H5D__append_flush_setup() */ /*------------------------------------------------------------------------- * Function: H5D__open_oid * * Purpose: Opens a dataset for access. * * Return: Dataset pointer on success, NULL on failure *------------------------------------------------------------------------- */ static herr_t H5D__open_oid(H5D_t *dataset, hid_t dapl_id) { H5P_genplist_t *plist; /* Property list */ H5O_fill_t *fill_prop; /* Pointer to dataset's fill value info */ unsigned alloc_time_state; /* Allocation time state */ htri_t msg_exists; /* Whether a particular type of message exists */ hbool_t layout_init = FALSE; /* Flag to indicate that chunk information was initialized */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_STATIC_TAG(dataset->oloc.addr) /* check args */ HDassert(dataset); /* (Set the 'vl_type' parameter to FALSE since it doesn't matter from here) */ if(NULL == (dataset->shared = H5D__new(H5P_DATASET_CREATE_DEFAULT, dapl_id, FALSE, FALSE))) HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed") /* Open the dataset object */ if(H5O_open(&(dataset->oloc)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTOPENOBJ, FAIL, "unable to open") /* Get the type and space */ if(NULL == (dataset->shared->type = (H5T_t *)H5O_msg_read(&(dataset->oloc), H5O_DTYPE_ID, NULL))) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to load type info from dataset header") if(H5T_set_loc(dataset->shared->type, H5F_VOL_OBJ(dataset->oloc.file), H5T_LOC_DISK) < 0) HGOTO_ERROR(H5E_DATATYPE, H5E_CANTINIT, FAIL, "invalid datatype location") if(NULL == (dataset->shared->space = H5S_read(&(dataset->oloc)))) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to load dataspace info from dataset header") /* Cache the dataset's dataspace info */ if(H5D__cache_dataspace_info(dataset) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTCOPY, FAIL, "can't cache dataspace info") /* Get a datatype ID for the dataset's datatype */ if((dataset->shared->type_id = H5I_register(H5I_DATATYPE, dataset->shared->type, FALSE)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTREGISTER, FAIL, "unable to register type") /* Get dataset creation property list object */ if(NULL == (plist = (H5P_genplist_t *)H5I_object(dataset->shared->dcpl_id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "can't get dataset creation property list") /* Get the layout/pline/efl message information */ if(H5D__layout_oh_read(dataset, dapl_id, plist) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get layout/pline/efl info") /* Indicate that the layout information was initialized */ layout_init = TRUE; /* Set up flush append property */ if(H5D__append_flush_setup(dataset, dapl_id)) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, FAIL, "unable to set up flush append property") /* Point at dataset's copy, to cache it for later */ fill_prop = &dataset->shared->dcpl_cache.fill; /* Try to get the new fill value message from the object header */ if((msg_exists = H5O_msg_exists(&(dataset->oloc), H5O_FILL_NEW_ID)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't check if message exists") if(msg_exists) { if(NULL == H5O_msg_read(&(dataset->oloc), H5O_FILL_NEW_ID, fill_prop)) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't retrieve message") } /* end if */ else { /* For backward compatibility, try to retrieve the old fill value message */ if((msg_exists = H5O_msg_exists(&(dataset->oloc), H5O_FILL_ID)) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't check if message exists") if(msg_exists) { if(NULL == H5O_msg_read(&(dataset->oloc), H5O_FILL_ID, fill_prop)) HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't retrieve message") } /* end if */ else { /* Set the space allocation time appropriately, based on the type of dataset storage */ switch(dataset->shared->layout.type) { case H5D_COMPACT: fill_prop->alloc_time = H5D_ALLOC_TIME_EARLY; break; case H5D_CONTIGUOUS: fill_prop->alloc_time = H5D_ALLOC_TIME_LATE; break; case H5D_CHUNKED: fill_prop->alloc_time = H5D_ALLOC_TIME_INCR; break; case H5D_VIRTUAL: fill_prop->alloc_time = H5D_ALLOC_TIME_INCR; break; case H5D_LAYOUT_ERROR: case H5D_NLAYOUTS: default: HGOTO_ERROR(H5E_DATASET, H5E_UNSUPPORTED, FAIL, "not implemented yet") } /* end switch */ /*lint !e788 All appropriate cases are covered */ } /* end else */ /* If "old" fill value size is 0 (undefined), map it to -1 */ if(fill_prop->size == 0) fill_prop->size = (ssize_t)-1; } /* end if */ alloc_time_state = 0; if((dataset->shared->layout.type == H5D_COMPACT && fill_prop->alloc_time == H5D_ALLOC_TIME_EARLY) || (dataset->shared->layout.type == H5D_CONTIGUOUS && fill_prop->alloc_time == H5D_ALLOC_TIME_LATE) || (dataset->shared->layout.type == H5D_CHUNKED && fill_prop->alloc_time == H5D_ALLOC_TIME_INCR) || (dataset->shared->layout.type == H5D_VIRTUAL && fill_prop->alloc_time == H5D_ALLOC_TIME_INCR)) alloc_time_state = 1; /* Set revised fill value properties, if they are different from the defaults */ if(H5P_fill_value_cmp(&H5D_def_dset.dcpl_cache.fill, fill_prop, sizeof(H5O_fill_t))) { if(H5P_set(plist, H5D_CRT_FILL_VALUE_NAME, fill_prop) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, FAIL, "can't set fill value") if(H5P_set(plist, H5D_CRT_ALLOC_TIME_STATE_NAME, &alloc_time_state) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTSET, FAIL, "can't set allocation time state") } /* end if */ /* * Make sure all storage is properly initialized. * This is important only for parallel I/O where the space must * be fully allocated before I/O can happen. */ if((H5F_INTENT(dataset->oloc.file) & H5F_ACC_RDWR) && !(*dataset->shared->layout.ops->is_space_alloc)(&dataset->shared->layout.storage) && H5F_HAS_FEATURE(dataset->oloc.file, H5FD_FEAT_ALLOCATE_EARLY)) { H5D_io_info_t io_info; io_info.dset = dataset; if(H5D__alloc_storage(&io_info, H5D_ALLOC_OPEN, FALSE, NULL) < 0) HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to initialize file storage") } /* end if */ done: if(ret_value < 0) { if(H5F_addr_defined(dataset->oloc.addr) && H5O_close(&(dataset->oloc), NULL) < 0) HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "unable to release object header") if(dataset->shared) { if(layout_init) if(dataset->shared->layout.ops->dest && (dataset->shared->layout.ops->dest)(dataset) < 0) HDONE_ERROR(H5E_DATASET, H5E_CANTRELEASE, FAIL, "unable to destroy layout info") if(dataset->shared->space && H5S_close(dataset->shared->space) < 0) HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "unable to release dataspace") if(dataset->shared->type) { if(dataset->shared->type_id > 0) { if(H5I_dec_ref(dataset->shared->type_id) < 0) HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "unable to release datatype") } /* end if */ else { if(H5T_close_real(dataset->shared->type) < 0) HDONE_ERROR(H5E_DATASET, H5E_CLOSEERROR, FAIL, "unable to release datatype") } /* end else */ } /* end if */ } /* end if */ } /* end if */ FUNC_LEAVE_NOAPI_TAG(ret_value) } /* end H5D__open_oid() */ /*------------------------------------------------------------------------- * Function: H5D_close * * Purpose: Insures that all data has been saved to the file, closes the * dataset object header, and frees all resources used by the * descriptor. * * Return: Non-negative on success/Negative on failure *------------------------------------------------------------------------- */ herr_t H5D_close(H5D_t *dataset) { hbool_t free_failed = FALSE; /* Set if freeing sub-components failed */ hbool_t corked; /* Whether the dataset is corked or not */ hbool_t file_closed = TRUE; /* H5O_close also closed the file? */ herr_t ret_value = SUCCEED; /* Return value */ FUNC_ENTER_NOAPI(FAIL) /* check args */ HDassert(dataset && dataset->oloc.file && dataset->shared); HDassert(dataset->shared->fo_count > 0);