[svn-r2482] Bringing "HDF5 Technical Notes" into development branch (from R1.2 branch)

This commit is contained in:
Frank Baker
2000-08-25 12:42:24 -05:00
parent 3ff571ab58
commit 2e8cd59163
35 changed files with 4108 additions and 0 deletions

View File

@@ -0,0 +1,122 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Big Datasets on Small Machines</title>
</head>
<body>
<h1>Big Datasets on Small Machines</h1>
<h2>1. Introduction</h2>
<p>The HDF5 library is able to handle files larger than the
maximum file size, and datasets larger than the maximum memory
size. For instance, a machine where <code>sizeof(off_t)</code>
and <code>sizeof(size_t)</code> are both four bytes can handle
datasets and files as large as 18x10^18 bytes. However, most
Unix systems limit the number of concurrently open files, so a
practical file size limit is closer to 512GB or 1TB.
<p>Two "tricks" must be imployed on these small systems in order
to store large datasets. The first trick circumvents the
<code>off_t</code> file size limit and the second circumvents
the <code>size_t</code> main memory limit.
<h2>2. File Size Limits</h2>
<p>Systems that have 64-bit file addresses will be able to access
those files automatically. One should see the following output
from configure:
<p><code><pre>
checking size of off_t... 8
</pre></code>
<p>Also, some 32-bit operating systems have special file systems
that can support large (&gt;2GB) files and HDF5 will detect
these and use them automatically. If this is the case, the
output from configure will show:
<p><code><pre>
checking for lseek64... yes
checking for fseek64... yes
</pre></code>
<p>Otherwise one must use an HDF5 file family. Such a family is
created by setting file family properties in a file access
property list and then supplying a file name that includes a
<code>printf</code>-style integer format. For instance:
<p><code><pre>
hid_t plist, file;
plist = H5Pcreate (H5P_FILE_ACCESS);
H5Pset_family (plist, 1&lt;&lt;30, H5P_DEFAULT);
file = H5Fcreate ("big%03d.h5", H5F_ACC_TRUNC, H5P_DEFAULT, plist);
</code></pre>
<p>The second argument (<code>1&lt;&lt;30</code>) to
<code>H5Pset_family()</code> indicates that the family members
are to be 2^30 bytes (1GB) each although we could have used any
reasonably large value. In general, family members cannot be
2GB because writes to byte number 2,147,483,647 will fail, so
the largest safe value for a family member is 2,147,483,647.
HDF5 will create family members on demand as the HDF5 address
space increases, but since most Unix systems limit the number of
concurrently open files the effective maximum size of the HDF5
address space will be limited (the system on which this was
developed allows 1024 open files, so if each family member is
approx 2GB then the largest HDF5 file is approx 2TB).
<p>If the effective HDF5 address space is limited then one may be
able to store datasets as external datasets each spanning
multiple files of any length since HDF5 opens external dataset
files one at a time. To arrange storage for a 5TB dataset split
among 1GB files one could say:
<p><code><pre>
hid_t plist = H5Pcreate (H5P_DATASET_CREATE);
for (i=0; i&lt;5*1024; i++) {
sprintf (name, "velocity-%04d.raw", i);
H5Pset_external (plist, name, 0, (size_t)1&lt;&lt;30);
}
</code></pre>
<h2>3. Dataset Size Limits</h2>
<p>The second limit which must be overcome is that of
<code>sizeof(size_t)</code>. HDF5 defines a data type called
<code>hsize_t</code> which is used for sizes of datasets and is,
by default, defined as <code>unsigned long long</code>.
<p>To create a dataset with 8*2^30 4-byte integers for a total of
32GB one first creates the dataspace. We give two examples
here: a 4-dimensional dataset whose dimension sizes are smaller
than the maximum value of a <code>size_t</code>, and a
1-dimensional dataset whose dimension size is too large to fit
in a <code>size_t</code>.
<p><code><pre>
hsize_t size1[4] = {8, 1024, 1024, 1024};
hid_t space1 = H5Screate_simple (4, size1, size1);
hsize_t size2[1] = {8589934592LL};
hid_t space2 = H5Screate_simple (1, size2, size2};
</pre></code>
<p>However, the <code>LL</code> suffix is not portable, so it may
be better to replace the number with
<code>(hsize_t)8*1024*1024*1024</code>.
<p>For compilers that don't support <code>long long</code> large
datasets will not be possible. The library performs too much
arithmetic on <code>hsize_t</code> types to make the use of a
struct feasible.
<hr>
<address><a href="mailto:matzke@llnl.gov">Robb Matzke</a></address>
<!-- Created: Fri Apr 10 13:26:04 EDT 1998 -->
<!-- hhmts start -->
Last modified: Sun Jul 19 11:37:25 EDT 1998
<!-- hhmts end -->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

@@ -0,0 +1,113 @@
%TGIF 3.0-p5
state(0,33,100,0,0,0,16,1,9,1,1,0,0,3,7,1,1,'Helvetica',0,24,0,0,0,10,0,0,1,1,0,16,0,0,1,1,1,0,1088,1408,0,0,2880).
%
% @(#)$Header$
% %W%
%
unit("1 pixel/pixel").
page(1,"",1).
box('black',64,64,384,384,0,1,1,22,0,0,0,0,0,'1',[
]).
poly('black',2,[
128,64,128,384],0,1,1,23,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
192,64,192,384],0,1,1,24,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
256,64,256,384],0,1,1,25,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
320,64,320,384],0,1,1,26,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
64,128,384,128],0,1,1,27,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
64,192,384,192],0,1,1,28,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
64,256,384,256],0,1,1,29,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
64,320,384,320],0,1,1,30,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
text('black',96,80,'Courier',0,17,1,1,0,1,7,14,37,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1"]).
text('black',160,80,'Courier',0,17,1,1,0,1,7,14,39,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"2"]).
text('black',224,80,'Courier',0,17,1,1,0,1,7,14,41,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"3"]).
text('black',288,80,'Courier',0,17,1,1,0,1,7,14,43,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"4"]).
text('black',352,80,'Courier',0,17,1,1,0,1,7,14,47,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"5"]).
text('black',96,144,'Courier',0,17,1,1,0,1,7,14,51,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"6"]).
text('black',160,144,'Courier',0,17,1,1,0,1,7,14,53,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"7"]).
text('black',224,144,'Courier',0,17,1,1,0,1,7,14,55,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"8"]).
text('black',288,144,'Courier',0,17,1,1,0,1,7,14,57,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"9"]).
text('black',352,144,'Courier',0,17,1,1,0,1,14,14,59,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"10"]).
text('black',96,208,'Courier',0,17,1,1,0,1,14,14,61,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"11"]).
text('black',160,208,'Courier',0,17,1,1,0,1,14,14,63,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"12"]).
text('black',224,208,'Courier',0,17,1,1,0,1,14,14,65,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"13"]).
text('black',288,208,'Courier',0,17,1,1,0,1,14,14,67,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"14"]).
text('black',352,208,'Courier',0,17,1,1,0,1,14,14,71,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"15"]).
text('black',96,272,'Courier',0,17,1,1,0,1,14,14,75,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"16"]).
text('black',160,272,'Courier',0,17,1,1,0,1,14,14,77,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"17"]).
text('black',224,272,'Courier',0,17,1,1,0,1,14,14,79,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"18"]).
text('black',288,272,'Courier',0,17,1,1,0,1,14,14,81,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"19"]).
text('black',352,272,'Courier',0,17,1,1,0,1,14,14,83,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"20"]).
text('black',96,336,'Courier',0,17,1,1,0,1,14,14,87,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"21"]).
text('black',160,336,'Courier',0,17,1,1,0,1,14,14,89,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"22"]).
text('black',224,336,'Courier',0,17,1,1,0,1,14,14,91,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"23"]).
text('black',288,336,'Courier',0,17,1,1,0,1,14,14,93,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"24"]).
text('black',352,336,'Courier',0,17,1,1,0,1,14,14,95,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"25"]).
poly('black',2,[
416,64,416,384],3,1,1,100,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
64,416,384,416],3,1,1,101,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
text('black',390,228,'Courier',0,17,1,0,0,1,14,35,102,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,1,0,[
390,228,390,228,425,242,0,-1000,1000,0,34,18,389,227,426,243],[
"5,000"]).
text('black',224,432,'Courier',0,17,1,1,0,1,35,14,116,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"5,000"]).
text('black',160,512,'Courier',0,17,1,0,0,1,105,14,131,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"= 1,000 x 1,000"]).
box('black',80,480,144,544,7,1,1,134,0,0,0,0,0,'1',[
]).
text('black',224,16,'Helvetica',0,24,1,1,0,1,296,29,144,0,24,5,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Order that data was written"]).
box('black',32,0,464,576,0,1,1,149,0,0,0,0,0,'1',[
]).

View File

@@ -0,0 +1,190 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Testing the chunked layout of HDF5</title>
</head>
<body>
<i>
This document is of interest primarily for its discussion of the
HDF team's motivation for implementing raw data caching.
At a more abstract level, the discussion of the principles of
data chunking is also of interest, but a more recent discussion
of that topic can be found in
<a href="../Chunking.html">Dataset Chunking Issues</a> in the
<cite><a href="../H5.user.html">HDF5 User's Guide</a></cite>.
The performance study described here predates the current chunking
implementation in the HDF5 library, so the particular performance data
is no longer apropos.
&nbsp;&nbsp;&nbsp;&nbsp; --&nbsp;the Editor
</i>
<h1>Testing the chunked layout of HDF5</h1>
<p>This is the results of studying the chunked layout policy in
HDF5. A 1000 by 1000 array of integers was written to a file
dataset extending the dataset with each write to create, in the
end, a 5000 by 5000 array of 4-byte integers for a total data
storage size of 100 million bytes.
<p>
<center>
<img alt="Order that data was written" src="ChStudy_p1.gif">
<br><b>Fig 1: Write-order of Output Blocks</b>
</center>
<p>After the array was written, it was read back in blocks that
were 500 by 500 bytes in row major order (that is, the top-left
quadrant of output block one, then the top-right quadrant of
output block one, then the top-left quadrant of output block 2,
etc.).
<p>I tried to answer two questions:
<ul>
<li>How does the storage overhead change as the chunk size
changes?
<li>What does the disk seek pattern look like as the chunk size
changes?
</ul>
<p>I started with chunk sizes that were multiples of the read
block size or k*(500, 500).
<p>
<center>
<table border>
<caption align=bottom>
<b>Table 1: Total File Overhead</b>
</caption>
<tr>
<th>Chunk Size (elements)</th>
<th>Meta Data Overhead (ppm)</th>
<th>Raw Data Overhead (ppm)</th>
</tr>
<tr align=center>
<td>500 by 500</td>
<td>85.84</td>
<td>0.00</td>
</tr>
<tr align=center>
<td>1000 by 1000</td>
<td>23.08</td>
<td>0.00</td>
</tr>
<tr align=center>
<td>5000 by 1000</td>
<td>23.08</td>
<td>0.00</td>
</tr>
<tr align=center>
<td>250 by 250</td>
<td>253.30</td>
<td>0.00</td>
</tr>
<tr align=center>
<td>499 by 499</td>
<td>85.84</td>
<td>205164.84</td>
</tr>
</table>
</center>
<hr>
<p>
<center>
<img alt="500x500" src="ChStudy_500x500.gif">
<br><b>Fig 2: Chunk size is 500x500</b>
</center>
<p>The first half of Figure 2 shows output to the file while the
second half shows input. Each dot represents a file-level I/O
request and the lines that connect the dots are for visual
clarity. The size of the request is not indicated in the
graph. The output block size is four times the chunk size which
results in four file-level write requests per block for a total
of 100 requests. Since file space for the chunks was allocated
in output order, and the input block size is 1/4 the output
block size, the input shows a staircase effect. Each input
request results in one file-level read request. The downward
spike at about the 60-millionth byte is probably the result of a
cache miss for the B-tree and the downward spike at the end is
probably a cache flush or file boot block update.
<hr>
<p>
<center>
<img alt="1000x1000" src="ChStudy_1000x1000.gif">
<br><b>Fig 2: Chunk size is 1000x1000</b>
</center>
<p>In this test I increased the chunk size to match the output
chunk size and one can see from the first half of the graph that
25 file-level write requests were issued, one for each output
block. The read half of the test shows that four times the
amount of data was read as written. This results from the fact
that HDF5 must read the entire chunk for any request that falls
within that chunk, which is done because (1) if the data is
compressed the entire chunk must be decompressed, and (2) the
library assumes that a chunk size was chosen to optimize disk
performance.
<hr>
<p>
<center>
<img alt="5000x1000" src="ChStudy_5000x1000.gif">
<br><b>Fig 3: Chunk size is 5000x1000</b>
</center>
<p>Increasing the chunk size further results in even worse
performance since both the read and write halves of the test are
re-reading and re-writing vast amounts of data. This proves
that one should be careful that chunk sizes are not much larger
than the typical partial I/O request.
<hr>
<p>
<center>
<img alt="250x250" src="ChStudy_250x250.gif">
<br><b>Fig 4: Chunk size is 250x250</b>
</center>
<p>If the chunk size is decreased then the amount of data
transfered between the disk and library is optimal for no
caching, but the amount of meta data required to describe the
chunk locations increases to 250 parts per million. One can
also see that the final downward spike contains more file-level
write requests as the meta data is flushed to disk just before
the file is closed.
<hr>
<p>
<center>
<img alt="499x499" src="ChStudy_499x499.gif">
<br><b>Fig 4: Chunk size is 499x499</b>
</center>
<p>This test shows the result of choosing a chunk size which is
close to the I/O block size. Because the total size of the
array isn't a multiple of the chunk size, the library allocates
an extra zone of chunks around the top and right edges of the
array which are only partially filled. This results in
20,516,484 extra bytes of storage, a 20% increase in the total
raw data storage size. But the amount of meta data overhead is
the same as for the 500 by 500 test. In addition, the mismatch
causes entire chunks to be read in order to update a few
elements along the edge or the chunk which results in a 3.6-fold
increase in the amount of data transfered.
<hr>
<address><a href="mailto:hdfhelp@ncsa.uiuc.edu">HDF Help Desk</a></address>
<!-- Created: Fri Jan 30 21:04:49 EST 1998 -->
<!-- hhmts start -->
Last modified: 30 Jan 1998 (technical content)
<br>
Last modified: 9 May 2000 (editor's note)
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,300 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Code Review</title>
</head>
<body>
<center><h1>Code Review 1</h1></center>
<h3>Some background...</h3>
<p>This is one of the functions exported from the
<code>H5B.c</code> file that implements a B-link-tree class
without worrying about concurrency yet (thus the `Note:' in the
function prologue). The <code>H5B.c</code> file provides the
basic machinery for operating on generic B-trees, but it isn't
much use by itself. Various subclasses of the B-tree (like
symbol tables or indirect storage) provide their own interface
and back end to this function. For instance,
<code>H5G_stab_find()</code> takes a symbol table OID and a name
and calls <code>H5B_find()</code> with an appropriate
<code>udata</code> argument that eventually gets passed to the
<code>H5G_stab_find()</code> function.
<p><code><pre>
1 /*-------------------------------------------------------------------------
2 * Function: H5B_find
3 *
4 * Purpose: Locate the specified information in a B-tree and return
5 * that information by filling in fields of the caller-supplied
6 * UDATA pointer depending on the type of leaf node
7 * requested. The UDATA can point to additional data passed
8 * to the key comparison function.
9 *
10 * Note: This function does not follow the left/right sibling
11 * pointers since it assumes that all nodes can be reached
12 * from the parent node.
13 *
14 * Return: Success: SUCCEED if found, values returned through the
15 * UDATA argument.
16 *
17 * Failure: FAIL if not found, UDATA is undefined.
18 *
19 * Programmer: Robb Matzke
20 * matzke@llnl.gov
21 * Jun 23 1997
22 *
23 * Modifications:
24 *
25 *-------------------------------------------------------------------------
26 */
27 herr_t
28 H5B_find (H5F_t *f, const H5B_class_t *type, const haddr_t *addr, void *udata)
29 {
30 H5B_t *bt=NULL;
31 intn idx=-1, lt=0, rt, cmp=1;
32 int ret_value = FAIL;
</pre></code>
<p>All pointer arguments are initialized when defined. I don't
worry much about non-pointers because it's usually obvious when
the value isn't initialized.
<p><code><pre>
33
34 FUNC_ENTER (H5B_find, NULL, FAIL);
35
36 /*
37 * Check arguments.
38 */
39 assert (f);
40 assert (type);
41 assert (type->decode);
42 assert (type->cmp3);
43 assert (type->found);
44 assert (addr && H5F_addr_defined (addr));
</pre></code>
<p>I use <code>assert</code> to check invariant conditions. At
this level of the library, none of these assertions should fail
unless something is majorly wrong. The arguments should have
already been checked by higher layers. It also provides
documentation about what arguments might be optional.
<p><code><pre>
45
46 /*
47 * Perform a binary search to locate the child which contains
48 * the thing for which we're searching.
49 */
50 if (NULL==(bt=H5AC_protect (f, H5AC_BT, addr, type, udata))) {
51 HGOTO_ERROR (H5E_BTREE, H5E_CANTLOAD, FAIL);
52 }
</pre></code>
<p>You'll see this quite often in the low-level stuff and it's
documented in the <code>H5AC.c</code> file. The
<code>H5AC_protect</code> insures that the B-tree node (which
inherits from the H5AC package) whose OID is <code>addr</code>
is locked into memory for the duration of this function (see the
<code>H5AC_unprotect</code> on line 90). Most likely, if this
node has been accessed in the not-to-distant past, it will still
be in memory and the <code>H5AC_protect</code> is almost a
no-op. If cache debugging is compiled in, then the protect also
prevents other parts of the library from accessing the node
while this function is protecting it, so this function can allow
the node to be in an inconsistent state while calling other
parts of the library.
<p>The alternative is to call the slighlty cheaper
<code>H5AC_find</code> and assume that the pointer it returns is
valid only until some other library function is called, but
since we're accessing the pointer throughout this function, I
chose to use the simpler protect scheme. All protected objects
<em>must be unprotected</em> before the file is closed, thus the
use of <code>HGOTO_ERROR</code> instead of
<code>HRETURN_ERROR</code>.
<p><code><pre>
53 rt = bt->nchildren;
54
55 while (lt&lt;rt && cmp) {
56 idx = (lt + rt) / 2;
57 if (H5B_decode_keys (f, bt, idx)&lt;0) {
58 HGOTO_ERROR (H5E_BTREE, H5E_CANTDECODE, FAIL);
59 }
60
61 /* compare */
62 if ((cmp=(type-&gt;cmp3)(f, bt->key[idx].nkey, udata,
63 bt->key[idx+1].nkey))&lt;0) {
64 rt = idx;
65 } else {
66 lt = idx+1;
67 }
68 }
69 if (cmp) {
70 HGOTO_ERROR (H5E_BTREE, H5E_NOTFOUND, FAIL);
71 }
</pre></code>
<p>Code is arranged in paragraphs with a comment starting each
paragraph. The previous paragraph is a standard binary search
algorithm. The <code>(type-&gt;cmp3)()</code> is an indirect
function call into the subclass of the B-tree. All indirect
function calls have the function part in parentheses to document
that it's indirect (quite obvious here, but not so obvious when
the function is a variable).
<p>It's also my standard practice to have side effects in
conditional expressions because I can write code faster and it's
more apparent to me what the condition is testing. But if I
have an assignment in a conditional expr, then I use an extra
set of parens even if they're not required (usually they are, as
in this case) so it's clear that I meant <code>=</code> instead
of <code>==</code>.
<p><code><pre>
72
73 /*
74 * Follow the link to the subtree or to the data node.
75 */
76 assert (idx&gt;=0 && idx<bt->nchildren);
77 if (bt->level > 0) {
78 if ((ret_value = H5B_find (f, type, bt->child+idx, udata))&lt;0) {
79 HGOTO_ERROR (H5E_BTREE, H5E_NOTFOUND, FAIL);
80 }
81 } else {
82 ret_value = (type-&gt;found)(f, bt->child+idx, bt->key[idx].nkey,
83 udata, bt->key[idx+1].nkey);
84 if (ret_value&lt;0) {
85 HGOTO_ERROR (H5E_BTREE, H5E_NOTFOUND, FAIL);
86 }
87 }
</pre></code>
<p>Here I broke the "side effect in conditional" rule, which I
sometimes do if the expression is so long that the
<code>&lt;0</code> gets lost at the end. Another thing to note is
that success/failure is always determined by comparing with zero
instead of <code>SUCCEED</code> or <code>FAIL</code>. I do this
because occassionally one might want to return other meaningful
values (always non-negative) or distinguish between various types of
failure (always negative).
<p><code><pre>
88
89 done:
90 if (bt && H5AC_unprotect (f, H5AC_BT, addr, bt)&lt;0) {
91 HRETURN_ERROR (H5E_BTREE, H5E_PROTECT, FAIL);
92 }
93 FUNC_LEAVE (ret_value);
94 }
</pre></code>
<p>For lack of a better way to handle errors during error cleanup,
I just call the <code>HRETURN_ERROR</code> macro even though it
will make the error stack not quite right. I also use short
circuiting boolean operators instead of nested <code>if</code>
statements since that's standard C practice.
<center><h1>Code Review 2</h1></center>
<p>The following code is an API function from the H5F package...
<p><code><pre>
1 /*--------------------------------------------------------------------------
2 NAME
3 H5Fflush
4
5 PURPOSE
6 Flush all cached data to disk and optionally invalidates all cached
7 data.
8
9 USAGE
10 herr_t H5Fflush(fid, invalidate)
11 hid_t fid; IN: File ID of file to close.
12 hbool_t invalidate; IN: Invalidate all of the cache?
13
14 ERRORS
15 ARGS BADTYPE Not a file atom.
16 ATOM BADATOM Can't get file struct.
17 CACHE CANTFLUSH Flush failed.
18
19 RETURNS
20 SUCCEED/FAIL
21
22 DESCRIPTION
23 This function flushes all cached data to disk and, if INVALIDATE
24 is non-zero, removes cached objects from the cache so they must be
25 re-read from the file on the next access to the object.
26
27 MODIFICATIONS:
28 --------------------------------------------------------------------------*/
</pre></code>
<p>An API prologue is used for each API function instead of my
normal function prologue. I use the prologue from Code Review 1
for non-API functions because it's more suited to C programmers,
it requires less work to keep it synchronized with the code, and
I have better editing tools for it.
<p><code><pre>
29 herr_t
30 H5Fflush (hid_t fid, hbool_t invalidate)
31 {
32 H5F_t *file = NULL;
33
34 FUNC_ENTER (H5Fflush, H5F_init_interface, FAIL);
35 H5ECLEAR;
</pre></code>
<p>API functions are never called internally, therefore I always
clear the error stack before doing anything.
<p><code><pre>
36
37 /* check arguments */
38 if (H5_FILE!=H5Aatom_group (fid)) {
39 HRETURN_ERROR (H5E_ARGS, H5E_BADTYPE, FAIL); /*not a file atom*/
40 }
41 if (NULL==(file=H5Aatom_object (fid))) {
42 HRETURN_ERROR (H5E_ATOM, H5E_BADATOM, FAIL); /*can't get file struct*/
43 }
</pre></code>
<p>If something is wrong with the arguments then we raise an
error. We never <code>assert</code> arguments at this level.
We also convert atoms to pointers since atoms are really just a
pointer-hiding mechanism. Functions that can be called
internally always have pointer arguments instead of atoms
because (1) then they don't have to always convert atoms to
pointers, and (2) the various pointer data types provide more
documentation and type checking than just an <code>hid_t</code>
type.
<p><code><pre>
44
45 /* do work */
46 if (H5F_flush (file, invalidate)&lt;0) {
47 HRETURN_ERROR (H5E_CACHE, H5E_CANTFLUSH, FAIL); /*flush failed*/
48 }
</pre></code>
<p>An internal version of the function does the real work. That
internal version calls <code>assert</code> to check/document
it's arguments and can be called from other library functions.
<p><code><pre>
49
50 FUNC_LEAVE (SUCCEED);
51 }
</pre></code>
<hr>
<address><a href="mailto:matzke@llnl.gov">Robb Matzke</a></address>
<!-- Created: Sat Nov 8 17:09:33 EST 1997 -->
<!-- hhmts start -->
Last modified: Mon Nov 10 15:33:33 EST 1997
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,279 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>External Files in HDF5</title>
</head>
<body>
<center><h1>External Files in HDF5</h1></center>
<h3>Overview of Layers</h3>
<p>This table shows some of the layers of HDF5. Each layer calls
functions at the same or lower layers and never functions at
higher layers. An object identifier (OID) takes various forms
at the various layers: at layer 0 an OID is an absolute physical
file address; at layers 1 and 2 it's an absolute virtual file
address. At layers 3 through 6 it's a relative address, and at
layers 7 and above it's an object handle.
<p><center>
<table border cellpadding=4 width="60%">
<tr align=center>
<td>Layer-7</td>
<td>Groups</td>
<td>Datasets</td>
</tr>
<tr align=center>
<td>Layer-6</td>
<td>Indirect Storage</td>
<td>Symbol Tables</td>
</tr>
<tr align=center>
<td>Layer-5</td>
<td>B-trees</td>
<td>Object Hdrs</td>
<td>Heaps</td>
</tr>
<tr align=center>
<td>Layer-4</td>
<td>Caching</td>
</tr>
<tr align=center>
<td>Layer-3</td>
<td>H5F chunk I/O</td>
</tr>
<tr align=center>
<td>Layer-2</td>
<td>H5F low</td>
</tr>
<tr align=center>
<td>Layer-1</td>
<td>File Family</td>
<td>Split Meta/Raw</td>
</tr>
<tr align=center>
<td>Layer-0</td>
<td>Section-2 I/O</td>
<td>Standard I/O</td>
<td>Malloc/Free</td>
</tr>
</table>
</center>
<h3>Single Address Space</h3>
<p>The simplest form of hdf5 file is a single file containing only
hdf5 data. The file begins with the boot block, which is
followed until the end of the file by hdf5 data. The next most
complicated file allows non-hdf5 data (user defined data or
internal wrappers) to appear before the boot block and after the
end of the hdf5 data. The hdf5 data is treated as a single
linear address space in both cases.
<p>The next level of complexity comes when non-hdf5 data is
interspersed with the hdf5 data. We handle that by including
the non-hdf5 interspersed data in the hdf5 address space and
simply not referencing it (eventually we might add those
addresses to a "do-not-disturb" list using the same mechanism as
the hdf5 free list, but it's not absolutely necessary). This is
implemented except for the "do-not-disturb" list.
<p>The most complicated single address space hdf5 file is when we
allow the address space to be split among multiple physical
files. For instance, a >2GB file can be split into smaller
chunks and transfered to a 32 bit machine, then accessed as a
single logical hdf5 file. The library already supports >32 bit
addresses, so at layer 1 we split a 64-bit address into a 32-bit
file number and a 32-bit offset (the 64 and 32 are
arbitrary). The rest of the library still operates with a linear
address space.
<p>Another variation might be a family of two files where all the
meta data is stored in one file and all the raw data is stored
in another file to allow the HDF5 wrapper to be easily replaced
with some other wrapper.
<p>The <code>H5Fcreate</code> and <code>H5Fopen</code> functions
would need to be modified to pass file-type info down to layer 2
so the correct drivers can be called and parameters passed to
the drivers to initialize them.
<h4>Implementation</h4>
<p>I've implemented fixed-size family members. The entire hdf5
file is partitioned into members where each member is the same
size. The family scheme is used if one passes a name to
<code>H5F_open</code> (which is called by <code>H5Fopen()</code>
and <code>H5Fcreate</code>) that contains a
<code>printf(3c)</code>-style integer format specifier.
Currently, the default low-level file driver is used for all
family members (H5F_LOW_DFLT, usually set to be Section 2 I/O or
Section 3 stdio), but we'll probably eventually want to pass
that as a parameter of the file access property list, which
hasn't been implemented yet. When creating a family, a default
family member size is used (defined at the top H5Ffamily.c,
currently 64MB) but that also should be settable in the file
access property list. When opening an existing family, the size
of the first member is used to determine the member size
(flushing/closing a family ensures that the first member is the
correct size) but the other family members don't have to be that
large (the local address space, however, is logically the same
size for all members).
<p>I haven't implemented a split meta/raw family yet but am rather
curious to see how it would perform. I was planning to use the
`.h5' extension for the meta data file and `.raw' for the raw
data file. The high-order bit in the address would determine
whether the address refers to meta data or raw data. If the user
passes a name that ends with `.raw' to <code>H5F_open</code>
then we'll chose the split family and use the default low level
driver for each of the two family members. Eventually we'll
want to pass these kinds of things through the file access
property list instead of relying on naming convention.
<h3>External Raw Data</h3>
<p>We also need the ability to point to raw data that isn't in the
HDF5 linear address space. For instance, a dataset might be
striped across several raw data files.
<p>Fortunately, the only two packages that need to be aware of
this are the packages for reading/writing contiguous raw data
and discontiguous raw data. Since contiguous raw data is a
special case, I'll discuss how to implement external raw data in
the discontiguous case.
<p>Discontiguous data is stored as a B-tree whose keys are the
chunk indices and whose leaf nodes point to the raw data by
storing a file address. So what we need is some way to name the
external files, and a way to efficiently store the external file
name for each chunk.
<p>I propose adding to the object header an <em>External File
List</em> message that is a 1-origin array of file names.
Then, in the B-tree, each key has an index into the External
File List (or zero for the HDF5 file) for the file where the
chunk can be found. The external file index is only used at
the leaf nodes to get to the raw data (the entire B-tree is in
the HDF5 file) but because of the way keys are copied among
the B-tree nodes, it's much easier to store the index with
every key.
<h3>Multiple HDF5 Files</h3>
<p>One might also want to combine two or more HDF5 files in a
manner similar to mounting file systems in Unix. That is, the
group structure and meta data from one file appear as though
they exist in the first file. One opens File-A, and then
<em>mounts</em> File-B at some point in File-A, the <em>mount
point</em>, so that traversing into the mount point actually
causes one to enter the root object of File-B. File-A and
File-B are each complete HDF5 files and can be accessed
individually without mounting them.
<p>We need a couple additional pieces of machinery to make this
work. First, an haddr_t type (a file address) doesn't contain
any info about which HDF5 file's address space the address
belongs to. But since haddr_t is an opaque type except at
layers 2 and below, it should be quite easy to add a pointer to
the HDF5 file. This would also remove the H5F_t argument from
most of the low-level functions since it would be part of the
OID.
<p>The other thing we need is a table of mount points and some
functions that understand them. We would add the following
table to each H5F_t struct:
<p><code><pre>
struct H5F_mount_t {
H5F_t *parent; /* Parent HDF5 file if any */
struct {
H5F_t *f; /* File which is mounted */
haddr_t where; /* Address of mount point */
} *mount; /* Array sorted by mount point */
intn nmounts; /* Number of mounted files */
intn alloc; /* Size of mount table */
}
</pre></code>
<p>The <code>H5Fmount</code> function takes the ID of an open
file or group, the name of a to-be-mounted file, the name of the mount
point, and a file access property list (like <code>H5Fopen</code>).
It opens the new file and adds a record to the parent's mount
table. The <code>H5Funmount</code> function takes the parent
file or group ID and the name of the mount point and disassociates
the mounted file from the mount point. It does not close the
mounted file. The <code>H5Fclose</code>
function closes/unmounts files recursively.
<p>The <code>H5G_iname</code> function which translates a name to
a file address (<code>haddr_t</code>) looks at the mount table
at each step in the translation and switches files where
appropriate. All name-to-address translations occur through
this function.
<h3>How Long?</h3>
<p>I'm expecting to be able to implement the two new flavors of
single linear address space in about two days. It took two hours
to implement the malloc/free file driver at level zero and I
don't expect this to be much more work.
<p>I'm expecting three days to implement the external raw data for
discontiguous arrays. Adding the file index to the B-tree is
quite trivial; adding the external file list message shouldn't
be too hard since the object header message class from wich this
message derives is fully implemented; and changing
<code>H5F_istore_read</code> should be trivial. Most of the
time will be spent designing a way to cache Unix file
descriptors efficiently since the total number open files
allowed per process could be much smaller than the total number
of HDF5 files and external raw data files.
<p>I'm expecting four days to implement being able to mount one
HDF5 file on another. I was originally planning a lot more, but
making <code>haddr_t</code> opaque turned out to be much easier
than I planned (I did it last Fri). Most of the work will
probably be removing the redundant H5F_t arguments for lots of
functions.
<h3>Conclusion</h3>
<p>The external raw data could be implemented as a single linear
address space, but doing so would require one to allocate large
enough file addresses throughout the file (>32bits) before the
file was created. It would make mixing an HDF5 file family with
external raw data, or external HDF5 wrapper around an HDF4 file
a more difficult process. So I consider the implementation of
external raw data files as a single HDF5 linear address space a
kludge.
<p>The ability to mount one HDF5 file on another might not be a
very important feature especially since each HDF5 file must be a
complete file by itself. It's not possible to stripe an array
over multiple HDF5 files because the B-tree wouldn't be complete
in any one file, so the only choice is to stripe the array
across multiple raw data files and store the B-tree in the HDF5
file. On the other hand, it might be useful if one file
contains some public data which can be mounted by other files
(e.g., a mesh topology shared among collaborators and mounted by
files that contain other fields defined on the mesh). Of course
the applications can open the two files separately, but it might
be more portable if we support it in the library.
<p>So we're looking at about two weeks to implement all three
versions. I didn't get a chance to do any of them in AIO
although we had long-term plans for the first two with a
possibility of the third. They'll be much easier to implement in
HDF5 than AIO since I've been keeping these in mind from the
start.
<hr>
<address><a href="mailto:matzke@llnl.gov">Robb Matzke</a></address>
<!-- Created: Sat Nov 8 18:08:52 EST 1997 -->
<!-- hhmts start -->
Last modified: Tue Sep 8 14:43:32 EDT 1998
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,205 @@
<html>
<head>
<title>Memory Management and Free Lists</title>
</head>
<body>
<h1>Memory Management and Free Lists</h1>
<pre>
At Release 1.2.2, free list management code was introduced to the HDF5
library. This included one user-level function, H5garbage_collect, which
garbage collects on all the free-lists. H5garbage_collect is the only user-
accessible (i.e., application developer-accessible) element of this
functionality.
The free-lists generally reduce the amount of dynamic memory used to around
75% of the pre-optimized amount as well as speed up the time in the library
code by ~5% The free-lists also help linearize the amount of memory used with
increasing numbers of datasets or re-writes on the data, so the amount of
memory used for the 1500/45 free-list case is only 66% of the memory used for
the unoptimized case.
Overall, the introduction of free list management is a win: the library is
slightly faster and uses much less system resources than before. Most of the
emphasis has been focused on the main "thouroughfares" through the code;
less attention was paid to the "back streets" which are used much less
frequently and offer less potential for abuse.
Adding a free-list for a data structure in the HDF5 library code is easy:
Old code:
---------
int foo(void)
{
H5W_t *w;
for(i=0; i&lt;x; i++) {
w=H5MM_malloc(sizeof(H5W_t));
&lt;use w&gt;
H5MM_xfree(w);
}
}
New code:
---------
H5FL_DEFINE(H5W_t);
int foo(void)
{
H5W_t *w;
for(i=0; i&lt;x; i++) {
w=H5FL_ALLOC(H5W_t,0);
&lt;use w&gt;
H5FL_FREE(H5W_t,w);
}
}
There are three kinds of free-lists:
-- for "regular" objects,
-- arrays of fixed size object (both fixed length and unknown length), and
-- "blocks" of bytes.
"Regular" free-lists use the H5FL_&lt;*&gt; macros in H5FLprivate.h and are
designed for single, fixed-size data structures like typedef'ed structs,
etc.
Arrays of objects use the H5FL_ARR_&lt;*&gt; macros and are designed for arrays
(both fixed in length and varying lengths) of fixed length data structures
(like typedef'ed types).
"Block" free-lists use the H5FL_BLK_&lt;*&gt; macros and are designed to hold
varying sized buffers of bytes, with no structure.
H5S.c contains examples for "regular" and fixed-sized arrays;
H5B.c contains examples for variable-sized arrays and "blocks".
A free-list doesn't have to be used for every data structure allocated and
freed, just for those which are prone to abuse when multiple operations are
being performed. It is important to use the macros for declaring and
manipulating the free-lists however; they allow the free'd objects on the
lists to be garbage collected by the library at the library's termination
or at the user's request.
One public API function has been added: H5garbage_collect, which garbage
collects on all the free-lists of all the different types. It's not required
to be called and is only necessary in situations when the application
performs actions which cause the library to allocate many objects and then
the application eventually releases those objects and wants to reduce the
memory used by the library from the peak usage required. The library
automatically garbage collects all the free lists when the application ends.
Questions should be sent to the HDF Help Desk at hdfhelp@ncsa.uiuc.edu.
===========================================
BENCHMARK INFORMATION
===========================================
New version with free lists:
Datasets=500, Data Rewrites=15:
Peak Heap Usage: 18210820 bytes
Time in library code: 2.260 seconds
# of malloc calls: 22864
Datasets=1000, Data Rewrites=15:
Peak Heap Usage: 31932420 bytes
Time in library code: 5.090 seconds
# of malloc calls: 43045
Datasets=1500, Data Rewrites=15:
Peak Heap Usage: 41566212 bytes
Time in library code: 8.623 seconds
# of malloc calls: 60623
Datasets=500, Data Rewrites=30:
Peak Heap Usage: 19456004 bytes
Time in library code: 4.274 seconds
# of malloc calls: 23353
Datasets=1000, Data Rewrites=30:
Peak Heap Usage: 33988612 bytes
Time in library code: 9.955 seconds
# of malloc calls: 43855
Datasets=1500, Data Rewrites=30:
Peak Heap Usage: 43950084 bytes
Time in library code: 17.413 seconds
# of malloc calls: 61554
Datasets=500, Data Rewrites=45:
Peak Heap Usage: 20717572 bytes
Time in library code: 6.326 seconds
# of malloc calls: 23848
Datasets=1000, Data Rewrites=45:
Peak Heap Usage: 35807236 bytes
Time in library code: 15.146 seconds
# of malloc calls: 44572
Datasets=1500, Data Rewrites=45:
Peak Heap Usage: 46022660 bytes
Time in library code: 27.140 seconds
# of malloc calls: 62370
Older version with no free lists:
Datasets=500, Data Rewrites=15:
Peak Heap Usage: 25370628 bytes
Time in library code: 2.329 seconds
# of malloc calls: 194991
Datasets=1000, Data Rewrites=15:
Peak Heap Usage: 39550980 bytes
Time in library code: 5.251 seconds
# of malloc calls: 417971
Datasets=1500, Data Rewrites=15:
Peak Heap Usage: 68870148 bytes
Time in library code: 8.913 seconds
# of malloc calls: 676564
Datasets=500, Data Rewrites=30:
Peak Heap Usage: 31670276 bytes
Time in library code: 4.435 seconds
# of malloc calls: 370320
Datasets=1000, Data Rewrites=30:
Peak Heap Usage: 44646404 bytes
Time in library code: 10.325 seconds
# of malloc calls: 797125
Datasets=1500, Data Rewrites=30:
Peak Heap Usage: 68870148 bytes
Time in library code: 18.057 seconds
# of malloc calls: 1295336
Datasets=500, Data Rewrites=45:
Peak Heap Usage: 33906692 bytes
Time in library code: 6.577 seconds
# of malloc calls: 545656
Datasets=1000, Data Rewrites=45:
Peak Heap Usage: 56778756 bytes
Time in library code: 15.720 seconds
# of malloc calls: 1176285
Datasets=1500, Data Rewrites=45:
Peak Heap Usage: 68870148 bytes
Time in library code: 28.138 seconds
# of malloc calls: 1914097
===========================================
Last Modified: 3 May 2000
HDF Help Desk: hdfhelp@ncsa.uiuc.edu
</pre>
</body>
</html>

View File

@@ -0,0 +1,271 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Backward/Forward Compatability</title>
</head>
<body>
<h1>Backward/Forward Compatability</h1>
<p>The HDF5 development must proceed in such a manner as to
satisfy the following conditions:
<ol type=A>
<li>HDF5 applications can produce data that HDF5
applications can read and write and HDF4 applications can produce
data that HDF4 applications can read and write. The situation
that demands this condition is obvious.</li>
<li>HDF5 applications are able to produce data that HDF4 applications
can read and HDF4 applications can subsequently modify the
file subject to certain constraints depending on the
implementation. This condition is for the temporary
situation where a consumer has neither been relinked with a new
HDF4 API built on top of the HDF5 API nor recompiled with the
HDF5 API.</li>
<li>HDF5 applications can read existing HDF4 files and subsequently
modify the file subject to certain constraints depending on
the implementation. This is condition is for the temporary
situation in which the producer has neither been relinked with a
new HDF4 API built on top of the HDF5 API nor recompiled with
the HDF5 API, or the permanent situation of HDF5 consumers
reading archived HDF4 files.</li>
</ul>
<p>There's at least one invarient: new object features introduced
in the HDF5 file format (like 2-d arrays of structs) might be
impossible to "translate" to a format that an old HDF4
application can understand either because the HDF4 file format
or the HDF4 API has no mechanism to describe the object.
<p>What follows is one possible implementation based on how
Condition B was solved in the AIO/PDB world. It also attempts
to satisfy these goals:
<ol type=1>
<li>The main HDF5 library contains as little extra baggage as
possible by either relying on external programs to take care
of compatability issues or by incorporating the logic of such
programs as optional modules in the HDF5 library. Conditions B
and C are separate programs/modules.</li>
<li>No extra baggage not only means the library proper is small,
but also means it can be implemented (rather than migrated
from HDF4 source) from the ground up with minimal regard for
HDF4 thus keeping the logic straight forward.</li>
<li>Compatability issues are handled behind the scenes when
necessary (and possible) but can be carried out explicitly
during things like data migration.</li>
</ol>
<hr>
<h2>Wrappers</h2>
<p>The proposed implementation uses <i>wrappers</i> to handle
compatability issues. A Format-X file is <i>wrapped</i> in a
Format-Y file by creating a Format-Y skeleton that replicates
the Format-X meta data. The Format-Y skeleton points to the raw
data stored in Format-X without moving the raw data. The
restriction is that raw data storage methods in Format-Y is a
superset of raw data storage methods in Format-X (otherwise the
raw data must be copied to Format-Y). We're assuming that meta
data is small wrt the entire file.
<p>The wrapper can be a separate file that has pointers into the
first file or it can be contained within the first file. If
contained in a single file, the file can appear as a Format-Y
file or simultaneously a Format-Y and Format-X file.
<p>The Format-X meta-data can be thought of as the original
wrapper around raw data and Format-Y is a second wrapper around
the same data. The wrappers are independend of one another;
modifying the meta-data in one wrapper causes the other to
become out of date. Modification of raw data doesn't invalidate
either view as long as the meta data that describes its storage
isn't modifed. For instance, an array element can change values
if storage is already allocated for the element, but if storage
isn't allocated then the meta data describing the storage must
change, invalidating all wrappers but one.
<p>It's perfectly legal to modify the meta data of one wrapper
without modifying the meta data in the other wrapper(s). The
illegal part is accessing the raw data through a wrapper which
is out of date.
<p>If raw data is wrapped by more than one internal wrapper
(<i>internal</i> means that the wrapper is in the same file as
the raw data) then access to that file must assume that
unreferenced parts of that file contain meta data for another
wrapper and cannot be reclaimed as free memory.
<hr>
<h2>Implementation of Condition B</h2>
<p>Since this is a temporary situation which can't be
automatically detected by the HDF5 library, we must rely
on the application to notify the HDF5 library whether or not it
must satisfy Condition B. (Even if we don't rely on the
application, at some point someone is going to remove the
Condition B constraint from the library.) So the module that
handles Condition B is conditionally compiled and then enabled
on a per-file basis.
<p>If the application desires to produce an HDF4 file (determined
by arguments to <code>H5Fopen</code>), and the Condition B
module is compiled into the library, then <code>H5Fclose</code>
calls the module to traverse the HDF5 wrapper and generate an
additional internal or external HDF4 wrapper (wrapper specifics
are described below). If Condition B is implemented as a module
then it can benefit from the metadata already cached by the main
library.
<p>An internal HDF4 wrapper would be used if the HDF5 file is
writable and the user doesn't mind that the HDF5 file is
modified. An external wrapper would be used if the file isn't
writable or if the user wants the data file to be primarily HDF5
but a few applications need an HDF4 view of the data.
<p>Modifying through the HDF5 library an HDF5 file that has
internal HDF4 wrapper should invalidate the HDF4 wrapper (and
optionally regenerate it when <code>H5Fclose</code> is
called). The HDF5 library must understand how wrappers work, but
not necessarily anything about the HDF4 file format.
<p>Modifying through the HDF5 library an HDF5 file that has an
external HDF4 wrapper will cause the HDF4 wrapper to become out
of date (but possibly regenerated during <code>H5Fclose</code>).
<b>Note: Perhaps the next release of the HDF4 library should
insure that the HDF4 wrapper file has a more recent modification
time than the raw data file (the HDF5 file) to which it
points(?)</b>
<p>Modifying through the HDF4 library an HDF5 file that has an
internal or external HDF4 wrapper will cause the HDF5 wrapper to
become out of date. However, there is now way for the old HDF4
library to notify the HDF5 wrapper that it's out of date.
Therefore the HDF5 library must be able to detect when the HDF5
wrapper is out of date and be able to fix it. If the HDF4
wrapper is complete then the easy way is to ignore the original
HDF5 wrapper and generate a new one from the HDF4 wrapper. The
other approach is to compare the HDF4 and HDF5 wrappers and
assume that if they differ HDF4 is the right one, if HDF4 omits
data then it was because HDF4 is a partial wrapper (rather than
assume HDF4 deleted the data), and if HDF4 has new data then
copy the new meta data to the HDF5 wrapper. On the other hand,
perhaps we don't need to allow these situations (modifying an
HDF5 file with the old HDF4 library and then accessing it with
the HDF5 library is either disallowed or causes HDF5 objects
that can't be described by HDF4 to be lost).
<p>To convert an HDF5 file to an HDF4 file on demand, one simply
opens the file with the HDF4 flag and closes it. This is also
how AIO implemented backward compatability with PDB in its file
format.
<hr>
<h2>Implementation of Condition C</h2>
<p>This condition must be satisfied for all time because there
will always be archived HDF4 files. If a pure HDF4 file (that
is, one without HDF5 meta data) is opened with an HDF5 library,
the <code>H5Fopen</code> builds an internal or external HDF5
wrapper and then accesses the raw data through that wrapper. If
the HDF5 library modifies the file then the HDF4 wrapper becomes
out of date. However, since the HDF5 library hasn't been
released, we can at least implement it to disable and/or reclaim
the HDF4 wrapper.
<p>If an external and temporary HDF5 wrapper is desired, the
wrapper is created through the cache like all other HDF5 files.
The data appears on disk only if a particular cached datum is
preempted. Instead of calling <code>H5Fclose</code> on the HDF5
wrapper file we call <code>H5Fabort</code> which immediately
releases all file resources without updating the file, and then
we unlink the file from Unix.
<hr>
<h2>What do wrappers look like?</h2>
<p>External wrappers are quite obvious: they contain only things
from the format specs for the wrapper and nothing from the
format specs of the format which they wrap.
<p>An internal HDF4 wrapper is added to an HDF5 file in such a way
that the file appears to be both an HDF4 file and an HDF5
file. HDF4 requires an HDF4 file header at file offset zero. If
a user block is present then we just move the user block down a
bit (and truncate it) and insert the minimum HDF4 signature.
The HDF4 <code>dd</code> list and any other data it needs are
appended to the end of the file and the HDF5 signature uses the
logical file length field to determine the beginning of the
trailing part of the wrapper.
<p>
<center>
<table border width="60%">
<tr>
<td>HDF4 minimal file header. Its main job is to point to
the <code>dd</code> list at the end of the file.</td>
</tr>
<tr>
<td>User-defined block which is truncated by the size of the
HDF4 file header so that the HDF5 boot block file address
doesn't change.</td>
</tr>
<tr>
<td>The HDF5 boot block and data, unmodified by adding the
HDF4 wrapper.</td>
</tr>
<tr>
<td>The main part of the HDF4 wrapper. The <code>dd</code>
list will have entries for all parts of the file so
hdpack(?) doesn't (re)move anything.</td>
</tr>
</table>
</center>
<p>When such a file is opened by the HDF5 library for
modification it shifts the user block back down to address zero
and fills with zeros, then truncates the file at the end of the
HDF5 data or adds the trailing HDF4 wrapper to the free
list. This prevents HDF4 applications from reading the file with
an out of date wrapper.
<p>If there is no user block then we have a problem. The HDF5
boot block must be moved to make room for the HDF4 file header.
But moving just the boot block causes problems because all file
addresses stored in the file are relative to the boot block
address. The only option is to shift the entire file contents
by 512 bytes to open up a user block (too bad we don't have
hooks into the Unix i-node stuff so we could shift the entire
file contents by the size of a file system page without ever
performing I/O on the file :-)
<p>Is it possible to place an HDF5 wrapper in an HDF4 file? I
don't know enough about the HDF4 format, but I would suspect it
might be possible to open a hole at file address 512 (and
possibly before) by moving some things to the end of the file
to make room for the HDF5 signature. The remainder of the HDF5
wrapper goes at the end of the file and entries are added to the
HDF4 <code>dd</code> list to mark the location(s) of the HDF5
wrapper.
<hr>
<h2>Other Thoughts</h2>
<p>Conversion programs that copy an entire HDF4 file to a separate,
self-contained HDF5 file and vice versa might be useful.
<hr>
<address><a href="mailto:matzke@llnl.gov">Robb Matzke</a></address>
<!-- Created: Fri Oct 3 11:52:31 EST 1997 -->
<!-- hhmts start -->
Last modified: Wed Oct 8 12:34:42 EST 1997
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,84 @@
<html>
<body>
<h1>Heap Management in HDF5</h1>
<pre>
Heap functions are in the H5H package.
off_t
H5H_new (hdf5_file_t *f, size_t size_hint, size_t realloc_hint);
Creates a new heap in the specified file which can efficiently
store at least SIZE_HINT bytes. The heap can store more than
that, but doing so may cause the heap to become less efficient
(for instance, a heap implemented as a B-tree might become
discontigous). The REALLOC_HINT is the minimum number of bytes
by which the heap will grow when it must be resized. The hints
may be zero in which case reasonable (but probably not
optimal) values will be chosen.
The return value is the address of the new heap relative to
the beginning of the file boot block.
off_t
H5H_insert (hdf5_file_t *f, off_t addr, size_t size, const void *buf);
Copies SIZE bytes of data from BUF into the heap whose address
is ADDR in file F. BUF must be the _entire_ heap object. The
return value is the byte offset of the new data in the heap.
void *
H5H_read (hdf5_file_t *f, off_t addr, off_t offset, size_t size, void *buf);
Copies SIZE bytes of data from the heap whose address is ADDR
in file F into BUF and then returns the address of BUF. If
BUF is the null pointer then a new buffer will be malloc'd by
this function and its address is returned.
Returns buffer address or null.
const void *
H5H_peek (hdf5_file_t *f, off_t addr, off_t offset)
A more efficient version of H5H_read that returns a pointer
directly into the cache; the data is not copied from the cache
to a buffer. The pointer is valid until the next call to an
H5AC function directly or indirectly.
Returns a pointer or null. Do not free the pointer.
void *
H5H_write (hdf5_file_t *f, off_t addr, off_t offset, size_t size,
const void *buf);
Modifies (part of) an object in the heap at address ADDR of
file F by copying SIZE bytes from the beginning of BUF to the
file. OFFSET is the address withing the heap where the output
is to occur.
This function can fail if the combination of OFFSET and SIZE
would write over a boundary between two heap objects.
herr_t
H5H_remove (hdf5_file_t *f, off_t addr, off_t offset, size_t size);
Removes an object or part of an object which begins at byte
OFFSET within a heap whose address is ADDR in file F. SIZE
bytes are returned to the free list. Removing the middle of
an object has the side effect that one object is now split
into two objects.
Returns success or failure.
===========================================
Last Modified: 8 July 1998 (technical content)
Last Modified: 28 April 2000 (included in HDF5 Technical Notes)
HDF Help Desk: hdfhelp@ncsa.uiuc.edu
</pre>
</body>
</html>

View File

@@ -0,0 +1,114 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>The Raw Data I/O Pipeline</title>
</head>
<body>
<h1>The Raw Data I/O Pipeline</h1>
<p>The HDF5 raw data pipeline is a complicated beast that handles
all aspects of raw data storage and transfer of that data
between the file and the application. Data can be stored
contiguously (internal or external), in variable size external
segments, or regularly chunked; it can be sparse, extendible,
and/or compressible. Data transfers must be able to convert from
one data space to another, convert from one number type to
another, and perform partial I/O operations. Furthermore,
applications will expect their common usage of the pipeline to
perform well.
<p>To accomplish these goals, the pipeline has been designed in a
modular way so no single subroutine is overly complicated and so
functionality can be inserted easily at the appropriate
locations in the pipeline. A general pipeline was developed and
then certain paths through the pipeline were optimized for
performance.
<p>We describe only the file-to-memory side of the pipeline since
the memory-to-file side is a mirror image. We also assume that a
proper hyperslab of a simple data space is being read from the
file into a proper hyperslab of a simple data space in memory,
and that the data type is a compound type which may require
various number conversions on its members.
<img alt="Figure 1" src="pipe1.gif">
<p>The diagrams should be read from the top down. The Line A
in the figure above shows that <code>H5Dread()</code> copies
data from a hyperslab of a file dataset to a hyperslab of an
application buffer by calling <code>H5D_read()</code>. And
<code>H5D_read()</code> calls, in a loop,
<code>H5S_simp_fgath()</code>, <code>H5T_conv_struct()</code>,
and <code>H5S_simp_mscat()</code>. A temporary buffer, TCONV, is
loaded with data points from the file, then data type conversion
is performed on the temporary buffer, and finally data points
are scattered out to application memory. Thus, data type
conversion is an in-place operation and data space conversion
consists of two steps. An additional temporary buffer, BKG, is
large enough to hold <em>N</em> instances of the destination
data type where <em>N</em> is the same number of data points
that can be held by the TCONV buffer (which is large enough to
hold either source or destination data points).
<p>The application sets an upper limit for the size of the TCONV
buffer and optionally supplies a buffer. If no buffer is
supplied then one will be created by calling
<code>malloc()</code> when the pipeline is executed (when
necessary) and freed when the pipeline exits. The size of the
BKG buffer depends on the size of the TCONV buffer and if the
application supplies a BKG buffer it should be at least as large
as the TCONV buffer. The default size for these buffers is one
megabyte but the buffer might not be used to full capacity if
the buffer size is not an integer multiple of the source or
destination data point size (whichever is larger, but only
destination for the BKG buffer).
<p>Occassionally the destination data points will be partially
initialized and the <code>H5Dread()</code> operation should not
clobber those values. For instance, the destination type might
be a struct with members <code>a</code> and <code>b</code> where
<code>a</code> is already initialized and we're reading
<code>b</code> from the file. An extra line, G, is added to the
pipeline to provide the type conversion functions with the
existing data.
<img alt="Figure 2" src="pipe2.gif">
<p>It will most likely be quite common that no data type
conversion is necessary. In such cases a temporary buffer for
data type conversion is not needed and data space conversion
can happen in a single step. In fact, when the source and
destination data are both contiguous (they aren't in the
picture) the loop degenerates to a single iteration.
<img alt="Figure 3" src="pipe3.gif">
<p>So far we've looked only at internal contiguous storage, but by
replacing Line B in Figures 1 and 2 and Line A in Figure 3 with
Figure 4 the pipeline is able to handle regularly chunked
objects. Line B of Figure 4 is executed once for each chunk
which contains data to be read and the chunk address is found by
looking at a multi-dimensional key in a chunk B-tree which has
one entry per chunk.
<img alt="Figure 4" src="pipe4.gif">
<p>If a single chunk is requested and the destination buffer is
the same size/shape as the chunk, then the CHUNK buffer is
bypassed and the destination buffer is used instead as shown in
Figure 5.
<img alt="Figure 5" src="pipe5.gif">
<hr>
<address><a href="mailto:matzke@llnl.gov">Robb Matzke</a></address>
<!-- Created: Tue Mar 17 11:13:35 EST 1998 -->
<!-- hhmts start -->
Last modified: Wed Mar 18 10:38:30 EST 1998
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,128 @@
<html>
<body>
<h1>Information for HDF5 Maintainers</h1>
<pre>
* You can run make from any directory. However, running in a
subdirectory only knows how to build things in that directory and
below. However, all makefiles know when their target depends on
something outside the local directory tree:
$ cd test
$ make
make: *** No rule to make target ../src/libhdf5.a
* All Makefiles understand the following targets:
all -- build locally.
install -- install libs, headers, progs.
uninstall -- remove installed files.
mostlyclean -- remove temp files (eg, *.o but not *.a).
clean -- mostlyclean plus libs and progs.
distclean -- all non-distributed files.
maintainer-clean -- all derived files but H5config.h.in and configure.
* Most Makefiles also understand:
TAGS -- build a tags table
dep, depend -- recalculate source dependencies
lib -- build just the libraries w/o programs
* If you have personal preferences for which make, compiler, compiler
flags, preprocessor flags, etc., that you use and you don't want to
set environment variables, then use a site configuration file.
When configure starts, it looks in the config directory for files
whose name is some combination of the CPU name, vendor, and
operating system in this order:
CPU-VENDOR-OS
VENDOR-OS
CPU-VENDOR
OS
VENDOR
CPU
The first file which is found is sourced and can therefore affect
the behavior of the rest of configure. See config/BlankForm for the
template.
* If you use GNU make along with gcc the Makefile will contain targets
that automatically maintain a list of source interdependencies; you
seldom have to say `make clean'. I say `seldom' because if you
change how one `*.h' file includes other `*.h' files you'll have
to force an update.
To force an update of all dependency information remove the
`.depend' file from each directory and type `make'. For
instance:
$ cd $HDF5_HOME
$ find . -name .depend -exec rm {} \;
$ make
If you're not using GNU make and gcc then dependencies come from
".distdep" files in each directory. Those files are generated on
GNU systems and inserted into the Makefile's by running
config.status (which happens near the end of configure).
* If you use GNU make along with gcc then the Perl script `trace' is
run just before dependencies are calculated to update any H5TRACE()
calls that might appear in the file. Otherwise, after changing the
type of a function (return type or argument types) one should run
`trace' manually on those source files (e.g., ../bin/trace *.c).
* Object files stay in the directory and are added to the library as a
final step instead of placing the file in the library immediately
and removing it from the directory. The reason is three-fold:
1. Most versions of make don't allow `$(LIB)($(SRC:.c=.o))'
which makes it necessary to have two lists of files, one
that ends with `.c' and the other that has the library
name wrapped around each `.o' file.
2. Some versions of make/ar have problems with modification
times of archive members.
3. Adding object files immediately causes problems on SMP
machines where make is doing more than one thing at a
time.
* When using GNU make on an SMP you can cause it to compile more than
one thing at a time. At the top of the source tree invoke make as
$ make -j -l6
which causes make to fork as many children as possible as long as
the load average doesn't go above 6. In subdirectories one can say
$ make -j2
which limits the number of children to two (this doesn't work at the
top level because the `-j2' is not passed to recursive makes).
* To create a release tarball go to the top-level directory and run
./bin/release. You can optionally supply one or more of the words
`tar', `gzip', `bzip2' or `compress' on the command line. The
result will be a (compressed) tar file(s) in the `releases'
directory. The README file is updated to contain the release date
and version number.
* To create a tarball of all the files which are part of HDF5 go to
the top-level directory and type:
tar cvf foo.tar `grep '^\.' MANIFEST |unexpand |cut -f1`
===========================================
Last Modified: 15 October 1999 (technical content)
Last Modified: 28 April 2000 (included in HDF5 Technical Notes)
HDF Help Desk: hdfhelp@ncsa.uiuc.edu
</pre>
</body>
</html>

View File

@@ -0,0 +1,510 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Memory Management in HDF5</title>
</head>
<body>
<h1>Memory Management in HDF5</h1>
<!-- ---------------------------------------------------------------- -->
<h2>Is a Memory Manager Necessary?</h2>
<p>Some form of memory management may be necessary in HDF5 when
the various deletion operators are implemented so that the
file memory is not permanently orphaned. However, since an
HDF5 file was designed with persistent data in mind, the
importance of a memory manager is questionable.
<p>On the other hand, when certain meta data containers (file glue)
grow, they may need to be relocated in order to keep the
container contiguous.
<blockquote>
<b>Example:</b> An object header consists of up to two
chunks of contiguous memory. The first chunk is a fixed
size at a fixed location when the header link count is
greater than one. Thus, inserting additional items into an
object header may require the second chunk to expand. When
this occurs, the second chunk may need to move to another
location in the file, freeing the file memory which that
chunk originally occupied.
</blockquote>
<p>The relocation of meta data containers could potentially
orphan a significant amount of file memory if the application
has made poor estimates for preallocation sizes.
<!-- ---------------------------------------------------------------- -->
<h2>Levels of Memory Management</h2>
<p>Memory management by the library can be independent of memory
management support by the file format. The file format can
support no memory management, some memory management, or full
memory management. Similarly with the library.
<h3>Support in the Library</h3>
<dl>
<dt><b>No Support: I</b>
<dd>When memory is deallocated it simply becomes unreferenced
(orphaned) in the file. Memory allocation requests are
satisfied by extending the file.
<dd>A separate off-line utility can be used to detect the
unreferenced bytes of a file and "bubble" them up to the end
of the file and then truncate the file.
<dt><b>Some Support: II</b>
<dd>The library could support partial memory management all
the time, or full memory management some of the time.
Orphaning free blocks instead of adding them to a free list
should not affect the file integrity, nor should fulfilling
new requests by extending the file instead of using the free
list.
<dt><b>Full Support: III</b>
<dd>The library supports space-efficient memory management by
always fulfilling allocation requests from the free list when
possible, and by coalescing adjacent free blocks into a
single larger free block.
</dl>
<h3>Support in the File Format</h3>
<dl>
<dt><b>No Support: A</b>
<dd>The file format does not support memory management; any
unreferenced block in the file is assumed to be free. If
the library supports full memory management then it will
have to traverse the entire file to determine which blocks
are unreferenced.
<dt><b>Some Support: B</b>
<dd>Assuming that unreferenced blocks are free can be
dangerous in a situation where the file is not consistent.
For instance, if a directory tree becomes detached from the
main directory hierarchy, then the detached directory and
everything that is referenced only through the detached
directory become unreferenced. File repair utilities will
be unable to determine which unreferenced blocks need to be
linked back into the file hierarchy.
<dd>Therefore, it might be useful to keep an unsorted,
doubly-linked list of free blocks in the file. The library
can add and remove blocks from the list in constant time,
and can generate its own internal free-block data structure
in time proportional to the number of free blocks instead of
the size of the file. Additionally, a library can use a
subset of the free blocks, an alternative which is not
feasible if the file format doesn't support any form of
memory management.
<dt><b>Full Support: C</b>
<dd>The file format can mirror library data structures for
space-efficient memory management. The free blocks are
linked in unsorted, doubly-linked lists with one list per
free block size. The heads of the lists are pointed to by a
B-tree whose nodes are sorted by free block size. At the
same time, all free blocks are the leaf nodes of another
B-tree sorted by starting and ending address. When the
trees are used in combination we can deallocate and allocate
memory in O(log <em>N</em>) time where <em>N</em> is the
number of free blocks.
</dl>
<h3>Combinations of Library and File Format Support</h3>
<p>We now evaluate each combination of library support with file
support:
<dl>
<dt><b>I-A</b>
<dd>If neither the library nor the file support memory
management, then each allocation request will come from the
end of the file and each deallocation request is a no-op
that simply leaves the free block unreferenced.
<ul>
<li>Advantages
<ul>
<li>No file overhead for allocation or deallocation.
<li>No library overhead for allocation or
deallocation.
<li>No file traversal required at time of open.
<li>No data needs to be written back to the file when
it's closed.
<li>Trivial to implement (already implemented).
</ul>
<li>Disadvantages
<ul>
<li>Inefficient use of file space.
<li>A file repair utility must reclaim lost file space.
<li>Difficulties for file repair utilities. (Is an
unreferenced block a free block or orphaned data?)
</ul>
</ul>
<dt><b>II-A</b>
<dd>In order for the library to support memory management, it
will be required to build the internal free block
representation by traversing the entire file looking for
unreferenced blocks.
<ul>
<li>Advantages
<ul>
<li>No file overhead for allocation or deallocation.
<li>Variable amount of library overhead for allocation
and deallocation depending on how much work the
library wants to do.
<li>No data needs to be written back to the file when
it's closed.
<li>Might use file space efficiently.
</ul>
<li>Disadvantages
<ul>
<li>Might use file space inefficiently.
<li>File traversal required at time of open.
<li>A file repair utility must reclaim lost file space.
<li>Difficulties for file repair utilities.
<li>Sharing of the free list between processes falls
outside the HDF5 file format documentation.
</ul>
</ul>
<dt><b>III-A</b>
<dd>In order for the library to support full memory
management, it will be required to build the internal free
block representation by traversing the entire file looking
for unreferenced blocks.
<ul>
<li>Advantages
<ul>
<li>No file overhead for allocation or deallocation.
<li>Efficient use of file space.
<li>No data needs to be written back to the file when
it's closed.
</ul>
<li>Disadvantages
<ul>
<li>Moderate amount of library overhead for allocation
and deallocation.
<li>File traversal required at time of open.
<li>A file repair utility must reclaim lost file space.
<li>Difficulties for file repair utilities.
<li>Sharing of the free list between processes falls
outside the HDF5 file format documentation.
</ul>
</ul>
<dt><b>I-B</b>
<dd>If the library doesn't support memory management but the
file format supports some level of management, then a file
repair utility will have to be run occasionally to reclaim
unreferenced blocks.
<ul>
<li>Advantages
<ul>
<li>No file overhead for allocation or deallocation.
<li>No library overhead for allocation or
deallocation.
<li>No file traversal required at time of open.
<li>No data needs to be written back to the file when
it's closed.
</ul>
<li>Disadvantages
<ul>
<li>A file repair utility must reclaim lost file space.
<li>Difficulties for file repair utilities.
</ul>
</ul>
<dt><b>II-B</b>
<dd>Both the library and the file format support some level
of memory management.
<ul>
<li>Advantages
<ul>
<li>Constant file overhead per allocation or
deallocation.
<li>Variable library overhead per allocation or
deallocation depending on how much work the library
wants to do.
<li>Traversal at file open time is on the order of the
free list size instead of the file size.
<li>The library has the option of reading only part of
the free list.
<li>No data needs to be written at file close time if
it has been amortized into the cost of allocation
and deallocation.
<li>File repair utilties don't have to be run to
reclaim memory.
<li>File repair utilities can detect whether an
unreferenced block is a free block or orphaned data.
<li>Sharing of the free list between processes might
be easier.
<li>Possible efficient use of file space.
</ul>
<li>Disadvantages
<ul>
<li>Possible inefficient use of file space.
</ul>
</ul>
<dt><b>III-B</b>
<dd>The library provides space-efficient memory management but
the file format only supports an unsorted list of free
blocks.
<ul>
<li>Advantages
<ul>
<li>Constant time file overhead per allocation or
deallocation.
<li>No data needs to be written at file close time if
it has been amortized into the cost of allocation
and deallocation.
<li>File repair utilities don't have to be run to
reclaim memory.
<li>File repair utilities can detect whether an
unreferenced block is a free block or orphaned data.
<li>Sharing of the free list between processes might
be easier.
<li>Efficient use of file space.
</ul>
<li>Disadvantages
<ul>
<li>O(log <em>N</em>) library overhead per allocation or
deallocation where <em>N</em> is the total number of
free blocks.
<li>O(<em>N</em>) time to open a file since the entire
free list must be read to construct the in-core
trees used by the library.
<li>Library is more complicated.
</ul>
</ul>
<dt><b>I-C</b>
<dd>This has the same advantages and disadvantages as I-C with
the added disadvantage that the file format is much more
complicated.
<dt><b>II-C</b>
<dd>If the library only provides partial memory management but
the file requires full memory management, then this method
degenerates to the same as II-A with the added disadvantage
that the file format is much more complicated.
<dt><b>III-C</b>
<dd>The library and file format both provide complete data
structures for space-efficient memory management.
<ul>
<li>Advantages
<ul>
<li>Files can be opened in constant time since the
free list is read on demand and amortised into the
allocation and deallocation requests.
<li>No data needs to be written back to the file when
it's closed.
<li>File repair utilities don't have to be run to
reclaim memory.
<li>File repair utilities can detect whether an
unreferenced block is a free block or orphaned data.
<li>Sharing the free list between processes is easy.
<li>Efficient use of file space.
</ul>
<li>Disadvantages
<ul>
<li>O(log <em>N</em>) file allocation and deallocation
cost where <em>N</em> is the total number of free
blocks.
<li>O(log <em>N</em>) library allocation and
deallocation cost.
<li>Much more complicated file format.
<li>More complicated library.
</ul>
</ul>
</dl>
<!-- ---------------------------------------------------------------- -->
<h2>The Algorithm for II-B</h2>
<p>The file contains an unsorted, doubly-linked list of free
blocks. The address of the head of the list appears in the
boot block. Each free block contains the following fields:
<center>
<table border cellpadding=4 width="60%">
<tr align=center>
<th width="25%">byte</th>
<th width="25%">byte</th>
<th width="25%">byte</th>
<th width="25%">byte</th>
<tr align=center>
<th colspan=4>Free Block Signature</th>
<tr align=center>
<th colspan=4>Total Free Block Size</th>
<tr align=center>
<th colspan=4>Address of Left Sibling</th>
<tr align=center>
<th colspan=4>Address of Right Sibling</th>
<tr align=center>
<th colspan=4><br><br>Remainder of Free Block<br><br><br></th>
</table>
</center>
<p>The library reads as much of the free list as convenient when
convenient and pushes those entries onto stacks. This can
occur when a file is opened or any time during the life of the
file. There is one stack for each free block size and the
stacks are sorted by size in a balanced tree in memory.
<p>Deallocation involves finding the correct stack or creating
a new one (an O(log <em>K</em>) operation where <em>K</em> is
the number of stacks), pushing the free block info onto the
stack (a constant-time operation), and inserting the free
block into the file free block list (a constant-time operation
which doesn't necessarily involve any I/O since the free blocks
can be cached like other objects). No attempt is made to
coalesce adjacent free blocks into larger blocks.
<p>Allocation involves finding the correct stack (an O(log
<em>K</em>) operation), removing the top item from the stack
(a constant-time operation), and removing the block from the
file free block list (a constant-time operation). If there is
no free block of the requested size or larger, then the file
is extended.
<p>To provide sharability of the free list between processes,
the last step of an allocation will check for the free block
signature and if it doesn't find one will repeat the process.
Alternatively, a process can temporarily remove free blocks
from the file and hold them in it's own private pool.
<p>To summarize...
<dl>
<dt>File opening
<dd>O(<em>N</em>) amortized over the time the file is open,
where <em>N</em> is the number of free blocks. The library
can still function without reading any of the file free
block list.
<dt>Deallocation
<dd>O(log <em>K</em>) where <em>K</em> is the number of unique
sizes of free blocks. File access is constant.
<dt>Allocation
<dd>O(log <em>K</em>). File access is constant.
<dt>File closing
<dd>O(1) even if the library temporarily removes free
blocks from the file to hold them in a private pool since
the pool can still be a linked list on disk.
</dl>
<!-- ---------------------------------------------------------------- -->
<h2>The Algorithm for III-C</h2>
<p>The HDF5 file format supports a general B-tree mechanism
for storing data with keys. If we use a B-tree to represent
all parts of the file that are free and the B-tree is indexed
so that a free file chunk can be found if we know the starting
or ending address, then we can efficiently determine whether a
free chunk begins or ends at the specified address. Call this
the <em>Address B-Tree</em>.
<p>If a second B-tree points to a set of stacks where the
members of a particular stack are all free chunks of the same
size, and the tree is indexed by chunk size, then we can
efficiently find the best-fit chunk size for a memory request.
Call this the <em>Size B-Tree</em>.
<p>All free blocks of a particular size can be linked together
with an unsorted, doubly-linked, circular list and the left
and right sibling addresses can be stored within the free
chunk, allowing us to remove or insert items from the list in
constant time.
<p>Deallocation of a block fo file memory consists of:
<ol type="I">
<li>Add the new free block whose address is <em>ADDR</em> to the
address B-tree.
<ol type="A">
<li>If the address B-tree contains an entry for a free
block that ends at <em>ADDR</em>-1 then remove that
block from the B-tree and from the linked list (if the
block was the first on the list then the size B-tree
must be updated). Adjust the size and address of the
block being freed to include the block just removed from
the free list. The time required to search for and
possibly remove the left block is O(log <em>N</em>)
where <em>N</em> is the number of free blocks.
<li>If the address B-tree contains an entry for the free
block that begins at <em>ADDR</em>+<em>LENGTH</em> then
remove that block from the B-tree and from the linked
list (if the block was the first on the list then the
size B-tree must be updated). Adjust the size of the
block being freed to include the block just removed from
the free list. The time required to search for and
possibly remove the right block is O(log <em>N</em>).
<li>Add the new (adjusted) block to the address B-tree.
The time for this operation is O(log <em>N</em>).
</ol>
<li>Add the new block to the size B-tree and linked list.
<ol type="A">
<li>If the size B-tree has an entry for this particular
size, then add the chunk to the tail of the list. This
is an O(log <em>K</em>) operation where <em>K</em> is
the number of unique free block sizes.
<li>Otherwise make a new entry in the B-tree for chunks of
this size. This is also O(log <em>K</em>).
</ol>
</ol>
<p>Allocation is similar to deallocation.
<p>To summarize...
<dl>
<dt>File opening
<dd>O(1)
<dt>Deallocation
<dd>O(log <em>N</em>) where <em>N</em> is the total number of
free blocks. File access time is O(log <em>N</em>).
<dt>Allocation
<dd>O(log <em>N</em>). File access time is O(log <em>N</em>).
<dt>File closing
<dd>O(1).
</dl>
<hr>
<address><a href="mailto:matzke@llnl.gov">Robb Matzke</a></address>
<!-- Created: Thu Jul 24 15:16:40 PDT 1997 -->
<!-- hhmts start -->
Last modified: Thu Jul 31 14:41:01 EST
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,66 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Relocating a File Data Structure</title>
</head>
<body>
<h1>Relocating a File Data Structure</h1>
<p>Since file data structures can be cached in memory by the H5AC
package it becomes problematic to move such a data structure in
the file. One cannot just copy a portion of the file from one
location to another because:
<ol>
<li>the file might not contain the latest information, and</li>
<li>the H5AC package might not realize that the object's
address has changed and attempt to write the object to disk
at the old address.</li>
</ol>
<p>Here's a correct method to move data from one location to
another. The example code assumes that one is moving a B-link
tree node from <code>old_addr</code> to <code>new_addr</code>.
<ol>
<li>Make sure the disk is up-to-date with respect to the
cache. There is no need to remove the item from the cache,
hence the final argument to <code>H5AC_flush</code> is
<code>FALSE</code>.
<br><br>
<code>
H5AC_flush (f, H5AC_BT, old_addr, FALSE);<br>
</code>
<br>
</li>
<li>Read the data from the old address and write it to the new
address.
<br><br>
<code>
H5F_block_read (f, old_addr, size, buf);<br>
H5F_block_write (f, new_addr, size, buf);<br>
</code>
<br>
</li>
<li>Notify the cache that the address of the object changed.
<br><br>
<code>
H5AC_rename (f, H5AC_BT, old_addr, new_addr);<br>
</code>
<br>
</li>
</ol>
<hr>
<address><a href="mailto:robb@maya.nuance.com">Robb Matzke</a></address>
<!-- Created: Mon Jul 14 15:09:06 EST 1997 -->
<!-- hhmts start -->
Last modified: Mon Jul 14 15:38:29 EST
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,300 @@
<HTML>
<HEAD><TITLE>
HDF5 Naming Scheme
</TITLE> </HEAD>
<BODY bgcolor="#ffffff">
<H1>
<FONT color="#c80028"
<I> <B> <CENTER> HDF5 Naming Scheme for </CENTER> </B> </I> </H1>
</FONT>
<P>
<UL>
<LI> <A HREF = "#01"><I> FILES </I> </A>
<LI> <A HREF = "#02"><I> PACKAGES </I> </A>
<LI> <A HREF = "#03"><I> PUBLIC vs PRIVATE </I> </A>
<LI> <A HREF = "#04"><I> INTEGRAL TYPES </I> </A>
<LI> <A HREF = "#05"><I> OTHER TYPES </I> </A>
<LI> <A HREF = "#06"><I> GLOBAL VARIABLES </I> </A>
<LI> <A HREF = "#07"><I> MACROS, PREPROCESSOR CONSTANTS, AND ENUM MEMEBERs </I> </A>
</UL>
<P>
<center>
Authors: <A HREF = "mailto:koziol@ncsa.uiuc.edu">
<I>Quincey Koziol</I> </A> and
<A HREF = "mailto:matzke@llnl.gov">
<I> Robb Matzke </I> </A>
</center>
<UL>
<FONT color="#c80028"
<LI> <A NAME="01"> <B> <I> FILES </I> </B> </A>
</FONT>
<UL>
<LI> Source files are named according to the package they contain (see
below). All files will begin with `H5' so we can stuff our
object files into someone else's library and not worry about file
name conflicts.
<P>For Example:
<i><b>
<dd> H5.c -- "Generic" library functions
<br>
<dd> H5B.c -- B-link tree functions
</i></b>
<p>
<LI> If a package is in more than one file, then another name is tacked
on. It's all lower case with no underscores or hyphens.
<P>For Example:
<i><b>
<dd> H5F.c -- the file for this package
<br>
<dd> H5Fstdio.c -- stdio functions (just an example)
<br>
<dd> H5Ffcntl.c -- fcntl functions (just an example)
</i></b>
<p>
<LI> Each package file has a header file of API stuff (unless there is
no API component to the package)
<P>For Example:
<i><b>
<dd> H5F.h -- things an application would see. </i> </b>
<P>
and a header file of private stuff
<i><b>
<p>
<dd> H5Fprivate.h -- things an application wouldn't see. The
private header includes the public header.
</i></b>
<p>
and a header for private prototypes
<i><b>
<p>
<dd> H5Fproto.h -- prototypes for internal functions.
</i></b>
<P>
By splitting the prototypes into separate include files we don't
have to recompile everything when just one function prototype
changes.
<LI> The main API header file is `hdf5.h' and it includes each of the
public header files but none of the private header files. Or the
application can include just the public header files it needs.
<LI> There is no main private or prototype header file because it
prevents make from being efficient. Instead, each source file
includes only the private header and prototype files it needs
(first all the private headers, then all the private prototypes).
<LI> Header files should include everything they need and nothing more.
</UL>
<P>
<FONT color="#c80028"
<LI> <A NAME="02"> <B> <I> PACKAGES </I> </B> </A>
</FONT>
<P>
Names exported beyond function scope begin with `H5' followed by zero,
one, or two upper-case letters that describe the class of object.
This prefix is the package name. The implementation of packages
doesn't necessarily have to map 1:1 to the source files.
<P>
<i><b>
<dd> H5 -- library functions
<br>
<dd> H5A -- atoms
<br>
<dd> H5AC -- cache
<br>
<dd> H5B -- B-link trees
<br>
<dd> H5D -- datasets
<br>
<dd> H5E -- error handling
<br>
<dd> H5F -- files
<br>
<dd> H5G -- groups
<br>
<dd> H5M -- meta data
<br>
<dd> H5MM -- core memory management
<br>
<dd> H5MF -- file memory management
<br>
<dd> H5O -- object headers
<br>
<dd> H5P -- Property Lists
<br>
<dd> H5S -- dataspaces
<br>
<dd> H5R -- relationships
<br>
<dd> H5T -- datatype
</i></b>
<p>
Each package implements a single main class of object (e.g., the H5B
package implements B-link trees). The main data type of a package is
the package name followed by `_t'.
<p>
<i><b>
<dd> H5F_t -- HDF5 file type
<br>
<dd> H5B_t -- B-link tree data type
</i></b>
<p>
Not all packages implement a data type (H5, H5MF) and some
packages provide access to a preexisting data type (H5MM, H5S).
<p>
<FONT color="#c80028"
<LI> <A NAME="03"> <B> <I> PUBLIC vs PRIVATE </I> </B> </A>
</FONT>
<p>
If the symbol is for internal use only, then the package name is
followed by an underscore and the rest of the name. Otherwise, the
symbol is part of the API and there is no underscore between the
package name and the rest of the name.
<p>
<i><b>
<dd> H5Fopen -- an API function.
<br>
<dd> H5B_find -- an internal function.
</i></b>
<p>
For functions, this is important because the API functions never pass
pointers around (they use atoms instead for hiding the implementation)
and they perform stringent checks on their arguments. Internal
unctions, on the other hand, check arguments with assert().
<p>
Data types like H5B_t carry no information about whether the type is
public or private since it doesn't matter.
<p>
<FONT color="#c80028"
<LI> <A NAME="04"> <B> <I> INTEGRAL TYPES </I> </B> </A>
</FONT>
<p>
Integral fixed-point type names are an optional `u' followed by `int'
followed by the size in bits (8, 16,
32, or 64). There is no trailing `_t' because these are common
enough and follow their own naming convention.
<p>
<pre><H4>
<dd> hbool_t -- boolean values (BTRUE, BFALSE, BFAIL)
<br>
<dd> int8 -- signed 8-bit integers
<br>
<dd> uint8 -- unsigned 8-bit integers
<br>
<dd> int16 -- signed 16-bit integers
<br>
<dd> uint16 -- unsigned 16-bit integers
<br>
<dd> int32 -- signed 32-bit integers
<br>
<dd> uint32 -- unsigned 32-bit integers
<br>
<dd> int64 -- signed 64-bit integers
<br>
<dd> uint64 -- unsigned 64-bit integers
<br>
<dd> intn -- "native" integers
<br>
<dd> uintn -- "native" unsigned integers
</pre></H4>
<p>
<FONT color="#c80028"
<LI> <A NAME="05"> <B> <I> OTHER TYPES </I> </B> </A>
</FONT>
<p>
Other data types are always followed by `_t'.
<p>
<pre><H4>
<dd> H5B_key_t-- additional data type used by H5B package.
</pre></H4>
<p>
However, if the name is so common that it's used almost everywhere,
then we make an alias for it by removing the package name and leading
underscore and replacing it with an `h' (the main datatype for a
package already has a short enough name, so we don't have aliases for
them).
<P>
<pre><H4>
<dd> typedef H5E_err_t herr_t;
</pre> </H4>
<p>
<FONT color="#c80028"
<LI> <A NAME="06"> <B> <I> GLOBAL VARIABLES </I> </B> </A>
</FONT>
<p>
Global variables include the package name and end with `_g'.
<p>
<pre><H4>
<dd> H5AC_methods_g -- global variable in the H5AC package.
</pre> </H4>
<p>
<FONT color="#c80028"
<LI> <A NAME="07">
<I> <B>
MACROS, PREPROCESSOR CONSTANTS, AND ENUM MEMBERS
</I> </B> </A>
</FONT>
<p>
Same rules as other symbols except the name is all upper case. There
are a few exceptions: <br>
<ul>
<li> Constants and macros defined on a system that is deficient:
<p><pre><H4>
<dd> MIN(x,y), MAX(x,y) and their relatives
</pre></H4>
<li> Platform constants :
<P>
No naming scheme; determined by OS and compiler.<br>
These appear only in one header file anyway.
<p>
<li> Feature test constants (?)<br>
Always start with `HDF5_HAVE_' like HDF5_HAVE_STDARG_H for a
header file, or HDF5_HAVE_DEV_T for a data type, or
HDF5_HAVE_DIV for a function.
</UL>
<p>
</UL>
<p>
<H6>
<center>
This file /hdf3/web/hdf/internal/HDF_standard/HDF5.coding_standard.html is
maintained by Elena Pourmal <A HREF = "mailto:epourmal@ncsa.uiuc.edu">
<I>epourmal@ncsa.uiuc.edu</I> </A>.
</center>
<p>
<center>
Last modified August 5, 1997
</center>
</H6>
</BODY>
<HTML>

View File

@@ -0,0 +1,72 @@
<html>
<body>
<h1>Object Headers</h1>
<pre>
haddr_t
H5O_new (hdf5_file_t *f, intn nrefs, size_t size_hint)
Creates a new empty object header and returns its address.
The SIZE_HINT is the initial size of the data portion of the
object header and NREFS is the number of symbol table entries
that reference this object header (normally one).
If SIZE_HINT is too small, then at least some default amount
of space is allocated for the object header.
intn /*num remaining links */
H5O_link (hdf5_file_t *f, /*file containing header */
haddr_t addr, /*header file address */
intn adjust) /*link adjustment amount */
size_t
H5O_sizeof (hdf5_file_t *f, /*file containing header */
haddr_t addr, /*header file address */
H5O_class_t *type, /*message type or H5O_ANY */
intn sequence) /*sequence number, usually zero */
Returns the size of a particular instance of a message in an
object header. When an object header has more than one
instance of a particular message type, then SEQUENCE indicates
which instance to return.
void *
H5O_read (hdf5_file_t *f, /*file containing header */
haddr_t addr, /*header file address */
H5G_entry_t *ent, /*optional symbol table entry */
H5O_class_t *type, /*message type or H5O_ANY */
intn sequence, /*sequence number, usually zero */
size_t size, /*size of output message */
void *mesg) /*output buffer */
Reads a message from the object header into memory.
const void *
H5O_peek (hdf5_file_t *f, /*file containing header */
haddr_t addr, /*header file address */
H5G_entry_t *ent, /*optional symbol table entry */
H5O_class_t *type, /*type of message or H5O_ANY */
intn sequence) /*sequence number, usually zero */
haddr_t /*new heap address */
H5O_modify (hdf5_file_t *f, /*file containing header */
haddr_t addr, /*header file address */
H5G_entry_t *ent, /*optional symbol table entry */
hbool_t *ent_modified, /*entry modification flag */
H5O_class_t *type, /*message type */
intn overwrite, /*sequence number or -1 */
void *mesg) /*the message */
===========================================
Last Modified: 8 July 1998 (technical content)
Last Modified: 28 April 2000 (included in HDF5 Technical Notes)
HDF Help Desk: hdfhelp@ncsa.uiuc.edu
</pre>
</body>
</html>

View File

@@ -0,0 +1,274 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Raw Data Storage in HDF5</title>
</head>
<body>
<h1>Raw Data Storage in HDF5</h1>
<p>This document describes the various ways that raw data is
stored in an HDF5 file and the object header messages which
contain the parameters for the storage.
<p>Raw data storage has three components: the mapping from some
logical multi-dimensional element space to the linear address
space of a file, compression of the raw data on disk, and
striping of raw data across multiple files. These components
are orthogonal.
<p>Some goals of the storage mechanism are to be able to
efficently store data which is:
<dl>
<dt>Small
<dd>Small pieces of raw data can be treated as meta data and
stored in the object header. This will be achieved by storing
the raw data in the object header with message 0x0006.
Compression and striping are not supported in this case.
<dt>Complete Large
<dd>The library should be able to store large arrays
contiguously in the file provided the user knows the final
array size a priori. The array can then be read/written in a
single I/O request. This is accomplished by describing the
storage with object header message 0x0005. Compression and
striping are not supported in this case.
<dt>Sparse Large
<dd>A large sparse raw data array should be stored in a manner
that is space-efficient but one in which any element can still
be accessed in a reasonable amount of time. Implementation
details are below.
<dt>Dynamic Size
<dd>One often doesn't have prior knowledge of the size of an
array. It would be nice to allow arrays to grow dynamically in
any dimension. It might also be nice to allow the array to
grow in the negative dimension directions if convenient to
implement. Implementation details are below.
<dt>Subslab Access
<dd>Some multi-dimensional arrays are almost always accessed by
subslabs. For instance, a 2-d array of pixels might always be
accessed as smaller 1k-by-1k 2-d arrays always aligned on 1k
index values. We should be able to store the array in such a
way that striding though the entire array is not necessary.
Subslab access might also be useful with compression
algorithms where each storage slab can be compressed
independently of the others. Implementation details are below.
<dt>Compressed
<dd>Various compression algorithms can be applied to the entire
array. We're not planning to support separate algorithms (or a
single algorithm with separate parameters) for each chunk
although it would be possible to implement that in a manner
similar to the way striping across files is
implemented.
<dt>Striped Across Files
<dd>The array access functions should support arrays stored
discontiguously across a set of files.
</dl>
<h1>Implementation of Indexed Storage</h1>
<p>The Sparse Large, Dynamic Size, and Subslab Access methods
share so much code that they can be described with a single
message. The new Indexed Storage Message (<code>0x0008</code>)
will replace the old Chunked Object (<code>0x0009</code>) and
Sparse Object (<code>0x000A</code>) Messages.
<p>
<center>
<table border cellpadding=4 width="60%">
<caption align=bottom>
<b>The Format of the Indexed Storage Message</b>
</caption>
<tr align=center>
<th width="25%">byte</th>
<th width="25%">byte</th>
<th width="25%">byte</th>
<th width="25%">byte</th>
</tr>
<tr align=center>
<td colspan=4><br>Address of B-tree<br><br></td>
</tr>
<tr align=center>
<td>Number of Dimensions</td>
<td>Reserved</td>
<td>Reserved</td>
<td>Reserved</td>
</tr>
<tr align=center>
<td colspan=4>Reserved (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>Alignment for Dimension 0 (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>Alignment for Dimension 1 (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>...</td>
</tr>
<tr align=center>
<td colspan=4>Alignment for Dimension N (4 bytes)</td>
</tr>
</table>
</center>
<p>The alignment fields indicate the alignment in logical space to
use when allocating new storage areas on disk. For instance,
writing every other element of a 100-element one-dimensional
array (using one HDF5 I/O partial write operation per element)
that has unit storage alignment would result in 50
single-element, discontiguous storage segments. However, using
an alignment of 25 would result in only four discontiguous
segments. The size of the message varies with the number of
dimensions.
<p>A B-tree is used to point to the discontiguous portions of
storage which has been allocated for the object. All keys of a
particular B-tree are the same size and are a function of the
number of dimensions. It is therefore not possible to change the
dimensionality of an indexed storage array after its B-tree is
created.
<p>
<center>
<table border cellpadding=4 width="60%">
<caption align=bottom>
<b>The Format of a B-Tree Key</b>
</caption>
<tr align=center>
<th width="25%">byte</th>
<th width="25%">byte</th>
<th width="25%">byte</th>
<th width="25%">byte</th>
</tr>
<tr align=center>
<td colspan=4>External File Number or Zero (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>Chunk Offset in Dimension 0 (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>Chunk Offset in Dimension 1 (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>...</td>
</tr>
<tr align=center>
<td colspan=4>Chunk Offset in Dimension N (4 bytes)</td>
</tr>
</table>
</center>
<p>The keys within a B-tree obey an ordering based on the chunk
offsets. If the offsets in dimension-0 are equal, then
dimension-1 is used, etc. The External File Number field
contains a 1-origin offset into the External File List message
which contains the name of the external file in which that chunk
is stored.
<h1>Implementation of Striping</h1>
<p>The indexed storage will support arbitrary striping at the
chunk level; each chunk can be stored in any file. This is
accomplished by using the External File Number field of an
indexed storage B-tree key as a 1-origin offset into an External
File List Message (0x0009) which takes the form:
<p>
<center>
<table border cellpadding=4 width="60%">
<caption align=bottom>
<b>The Format of the External File List Message</b>
</caption>
<tr align=center>
<th width="25%">byte</th>
<th width="25%">byte</th>
<th width="25%">byte</th>
<th width="25%">byte</th>
</tr>
<tr align=center>
<td colspan=4><br>Name Heap Address<br><br></td>
</tr>
<tr align=center>
<td colspan=4>Number of Slots Allocated (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>Number of File Names (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>Byte Offset of Name 1 in Heap (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>Byte Offset of Name 2 in Heap (4 bytes)</td>
</tr>
<tr align=center>
<td colspan=4>...</td>
</tr>
<tr align=center>
<td colspan=4><br>Unused Slot(s)<br><br></td>
</tr>
</table>
</center>
<p>Each indexed storage array that has all or part of its data
stored in external files will contain a single external file
list message. The size of the messages is determined when the
message is created, but it may be possible to enlarge the
message on demand by moving it. At this time, it's not possible
for multiple arrays to share a single external file list
message.
<dl>
<dt><code>
H5O_efl_t *H5O_efl_new (H5G_entry_t *object, intn
nslots_hint, intn heap_size_hint)
</code>
<dd>Adds a new, empty external file list message to an object
header and returns a pointer to that message. The message
acts as a cache for file descriptors of external files that
are open.
<p><dt><code>
intn H5O_efl_index (H5O_efl_t *efl, const char *filename)
</code>
<dd>Gets the external file index number for a particular file name.
If the name isn't in the external file list then it's added to
the H5O_efl_t struct and immediately written to the object
header to which the external file list message belongs. Name
comparison is textual. Each name should be relative to the
directory which contains the HDF5 file.
<p><dt><code>
H5F_low_t *H5O_efl_open (H5O_efl_t *efl, intn index, uintn mode)
</code>
<dd>Gets a low-level file descriptor for an external file. The
external file list caches file descriptors because we might
have many more external files than there are file descriptors
available to this process. The caller should not close this file.
<p><dt><code>
herr_t H5O_efl_release (H5O_efl_t *efl)
</code>
<dd>Releases an external file list, closes all files
associated with that list, and if the list has been modified
since the call to <code>H5O_efl_new</code> flushes the message
to disk.
</dl>
<hr>
<address><a href="mailto:robb@arborea.spizella.com">Robb Matzke</a></address>
<!-- Created: Fri Oct 3 09:52:32 EST 1997 -->
<!-- hhmts start -->
Last modified: Tue Nov 25 12:36:50 EST 1997
<!-- hhmts end -->
</body>
</html>

View File

@@ -0,0 +1,329 @@
<html>
<body>
<h1>Symbol Table Caching Issues</h1>
<pre>
A number of issues involving caching of object header messages in
symbol table entries must be resolved.
What is the motivation for these changes?
If we make objects completely independent of object name it allows
us to refer to one object by multiple names (a concept called hard
links in Unix file systems), which in turn provides an easy way to
share data between datasets.
Every object in an HDF5 file has a unique, constant object header
address which serves as a handle (or OID) for the object. The
object header contains messages which describe the object.
HDF5 allows some of the object header messages to be cached in
symbol table entries so that the object header doesn't have to be
read from disk. For instance, an entry for a directory caches the
directory disk addresses required to access that directory, so the
object header for that directory is seldom read.
If an object has multiple names (that is, a link count greater than
one), then it has multiple symbol table entries which point to it.
All symbol table entries must agree on header messages. The
current mechanism is to turn off the caching of header messages in
symbol table entries when the header link count is more than one,
and to allow caching once the link count returns to one.
However, in the current implementation, a package is allowed to
copy a symbol table entry and use it as a private cache for the
object header. This doesn't work for a number of reasons (all but
one require a `delete symbol entry' operation).
1. If two packages hold copies of the same symbol table entry,
they don't notify each other of changes to the symbol table
entry. Eventually, one package reads a cached message and
gets the wrong value because the other package changed the
message in the object header.
2. If one package holds a copy of the symbol table entry and
some other part of HDF5 removes the object and replaces it
with some other object, then the original package will
continue to access the non-existent object using the new
object header.
3. If one package holds a copy of the symbol table entry and
some other part of HDF5 (re)moves the directory which
contains the object, then the package will be unable to
update the symbol table entry with the new cached
data. Packages that refer to the object by the new name will
use old cached data.
The basic problem is that there may be multiple copies of the object
symbol table entry floating around in the code when there should
really be at most one per hard link.
Level 0: A copy may exist on disk as part of a symbol table node, which
is a small 1d array of symbol table entries.
Level 1: A copy may be cached in memory as part of a symbol table node
in the H5Gnode.c file by the H5AC layer.
Level 2a: Another package may be holding a copy so it can perform
fast lookup of any header messages that might be cached in
the symbol table entry. It can't point directly to the
cached symbol table node because that node can dissappear
at any time.
Level 2b: Packages may hold more than one copy of a symbol table
entry. For instance, if H5D_open() is called twice for
the same name, then two copies of the symbol table entry
for the dataset exist in the H5D package.
How can level 2a and 2b be combined?
If package data structures contained pointers to symbol table
entries instead of copies of symbol table entries and if H5G
allocated one symbol table entry per hard link, then it's trivial
for Level 2a and 2b to benefit from one another's actions since
they share the same cache.
How does this work conceptually?
Level 2a and 2b must notify Level 1 of their intent to use (or stop
using) a symbol table entry to access an object header. The
notification of the intent to access an object header is called
`opening' the object and releasing the access is `closing' the
object.
Opening an object requires an object name which is used to locate
the symbol table entry to use for caching of object header
messages. The return value is a handle for the object. Figure 1
shows the state after Dataset1 opens Object with a name that maps
through Entry1. The open request created a copy of Entry1 called
Shadow1 which exists even if SymNode1 is preempted from the H5AC
layer.
______
Object / \
SymNode1 +--------+ |
+--------+ _____\ | Header | |
| | / / +--------+ |
+--------+ +---------+ \______/
| Entry1 | | Shadow1 | /____
+--------+ +---------+ \ \
: : \
+--------+ +----------+
| Dataset1 |
+----------+
FIGURE 1
The SymNode1 can appear and disappear from the H5AC layer at any
time without affecting the Object Header data cached in the Shadow.
The rules are:
* If the SymNode1 is present and is about to disappear and the
Shadow1 dirty bit is set, then Shadow1 is copied over Entry1, the
Entry1 dirty bit is set, and the Shadow1 dirty bit is cleared.
* If something requests a copy of Entry1 (for a read-only peek
request), and Shadow1 exists, then a copy (not pointer) of Shadow1
is returned instead.
* Entry1 cannot be deleted while Shadow1 exists.
* Entry1 cannot change directly if Shadow1 exists since this means
that some other package has opened the object and may be modifying
it. I haven't decided if it's useful to ever change Entry1
directly (except of course within the H5G layer itself).
* Shadow1 is created when Dataset1 `opens' the object through
Entry1. Dataset1 is given a pointer to Shadow1 and Shadow1's
reference count is incremented.
* When Dataset1 `closes' the Object the Shadow1 reference count is
decremented. When the reference count reaches zero, if the
Shadow1 dirty bit is set, then Shadow1's contents are copied to
Entry1, and the Entry1 dirty bit is set. Shadow1 is then deleted
if its reference count is zero. This may require reading SymNode1
back into the H5AC layer.
What happens when another Dataset opens the Object through Entry1?
If the current state is represented by the top part of Figure 2,
then Dataset2 will be given a pointer to Shadow1 and the Shadow1
reference count will be incremented to two. The Object header link
count remains at one so Object Header messages continue to be cached
by Shadow1. Dataset1 and Dataset2 benefit from one another
actions. The resulting state is represented by Figure 2.
_____
SymNode1 Object / \
+--------+ _____\ +--------+ |
| | / / | Header | |
+--------+ +---------+ +--------+ |
| Entry1 | | Shadow1 | /____ \_____/
+--------+ +---------+ \ \
: : _ \
+--------+ |\ +----------+
\ | Dataset1 |
\________ +----------+
\ \
+----------+ |
| Dataset2 | |- New Dataset
+----------+ |
/
FIGURE 2
What happens when the link count for Object increases while Dataset
has the Object open?
SymNode2
+--------+
SymNode1 Object | |
+--------+ ____\ +--------+ /______ +--------+
| | / / | header | \ `| Entry2 |
+--------+ +---------+ +--------+ +--------+
| Entry1 | | Shadow1 | /____ : :
+--------+ +---------+ \ \ +--------+
: : \
+--------+ +----------+ \________________/
| Dataset1 | |
+----------+ New Link
FIGURE 3
The current state is represented by the left part of Figure 3. To
create a new link the Object Header had to be located by traversing
through Entry1/Shadow1. On the way through, the Entry1/Shadow1
cache is invalidated and the Object Header link count is
incremented. Entry2 is then added to SymNode2.
Since the Object Header link count is greater than one, Object
header data will not be cached in Entry1/Shadow1.
If the initial state had been all of Figure 3 and a third link is
being added and Object is open by Entry1 and Entry2, then creation
of the third link will invalidate the cache in Entry1 or Entry2. It
doesn't matter which since both caches are already invalidated
anyway.
What happens if another Dataset opens the same object by another name?
If the current state is represented by Figure 3, then a Shadow2 is
created and associated with Entry2. However, since the Object
Header link count is more than one, nothing gets cached in Shadow2
(or Shadow1).
What happens if the link count decreases?
If the current state is represented by all of Figure 3 then it isn't
possible to delete Entry1 because the object is currently open
through that entry. Therefore, the link count must have
decreased because Entry2 was removed.
As Dataset1 reads/writes messages in the Object header they will
begin to be cached in Shadow1 again because the Object header link
count is one.
What happens if the object is removed while it's open?
That operation is not allowed.
What happens if the directory containing the object is deleted?
That operation is not allowed since deleting the directory requires
that the directory be empty. The directory cannot be emptied
because the open object cannot be removed from the directory.
What happens if the object is moved?
Moving an object is a process consisting of creating a new
hard-link with the new name and then deleting the old name.
This will fail if the object is open.
What happens if the directory containing the entry is moved?
The entry and the shadow still exist and are associated with one
another.
What if a file is flushed or closed when objects are open?
Flushing a symbol table with open objects writes correct information
to the file since Shadow is copied to Entry before the table is
flushed.
Closing a file with open objects will create a valid file but will
return failure.
How is the Shadow associated with the Entry?
A symbol table is composed of one or more symbol nodes. A node is a
small 1-d array of symbol table entries. The entries can move
around within a node and from node-to-node as entries are added or
removed from the symbol table and nodes can move around within a
symbol table, being created and destroyed as necessary.
Since a symbol table has an object header with a unique and constant
file offset, and since H5G contains code to efficiently locate a
symbol table entry given it's name, we use these two values as a key
within a shadow to associate the shadow with the symbol table
entry.
struct H5G_shadow_t {
haddr_t stab_addr; /*symbol table header address*/
char *name; /*entry name wrt symbol table*/
hbool_t dirty; /*out-of-date wrt stab entry?*/
H5G_entry_t ent; /*my copy of stab entry */
H5G_entry_t *main; /*the level 1 entry or null */
H5G_shadow_t *next, *prev; /*other shadows for this stab*/
};
The set of shadows will be organized in a hash table of linked
lists. Each linked list will contain the shadows associated with a
particular symbol table header address and the list will be sorted
lexicographically.
Also, each Entry will have a pointer to the corresponding Shadow or
null if there is no shadow.
When a symbol table node is loaded into the main cache, we look up
the linked list of shadows in the shadow hash table based on the
address of the symbol table object header. We then traverse that
list matching shadows with symbol table entries.
We assume that opening/closing objects will be a relatively
infrequent event compared with loading/flushing symbol table
nodes. Therefore, if we keep the linked list of shadows sorted it
costs O(N) to open and close objects where N is the number of open
objects in that symbol table (instead of O(1)) but it costs only
O(N) to load a symbol table node (instead of O(N^2)).
What about the root symbol entry?
Level 1 storage for the root symbol entry is always available since
it's stored in the hdf5_file_t struct instead of a symbol table
node. However, the contents of that entry can move from the file
handle to a symbol table node by H5G_mkroot(). Therefore, if the
root object is opened, we keep a shadow entry for it whose
`stab_addr' field is zero and whose `name' is null.
For this reason, the root object should always be read through the
H5G interface.
One more key invariant: The H5O_STAB message in a symbol table header
never changes. This allows symbol table entries to cache the H5O_STAB
message for the symbol table to which it points without worrying about
whether the cache will ever be invalidated.
===========================================
Last Modified: 8 July 1998 (technical content)
Last Modified: 28 April 2000 (included in HDF5 Technical Notes)
HDF Help Desk: hdfhelp@ncsa.uiuc.edu
</pre>
</body>
</html>

View File

@@ -0,0 +1,137 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Version Numbers</title>
</head>
<body>
<h1>HDF5 Release Version Numbers</h1>
<h2>1. Introduction</h2>
<p>The HDF5 version number is a set of three integer values
written as either <code>hdf5-1.2.3</code> or <code>hdf5 version
1.2 release 3</code>.
<p>The <code>5</code> is part of the library name and will only
change if the entire file format and library are redesigned
similar in scope to the changes between HDF4 and HDF5.
<p>The <code>1</code> is the <em>major version number</em> and
changes when there is an extensive change to the file format or
library API. Such a change will likely require files to be
translated and applications to be modified. This number is not
expected to change frequently.
<p>The <code>2</code> is the <em>minor version number</em> and is
incremented by each public release that presents new features.
Even numbers are reserved for stable public versions of the
library while odd numbers are reserved for developement
versions. See the diagram below for examples.
<p>The <code>3</code> is the <em>release number</em>. For public
versions of the library, the release number is incremented each
time a bug is fixed and the fix is made available to the public.
For development versions, the release number is incremented more
often (perhaps almost daily).
<h2>2. Abbreviated Versions</h2>
<p>It's often convenient to drop the release number when referring
to a version of the library, like saying version 1.2 of HDF5.
The release number can be any value in this case.
<h2>3. Special Versions</h2>
<p>Version 1.0.0 was released for alpha testing the first week of
March, 1998. The developement version number was incremented to
1.0.1 and remained constant until the the last week of April,
when the release number started to increase and development
versions were made available to people outside the core HDF5
development team.
<p>Version 1.0.23 was released mid-July as a second alpha
version.
<p>Version 1.1.0 will be the first official beta release but the
1.1 branch will also serve as a development branch since we're
not concerned about providing bug fixes separate from normal
development for the beta version.
<p>After the beta release we rolled back the version number so the
first release is version 1.0 and development will continue on
version 1.1. We felt that an initial version of 1.0 was more
important than continuing to increment the pre-release version
numbers.
<h2>4. Public versus Development</h2>
<p>The motivation for separate public and development versions is
that the public version will receive only bug fixes while the
development version will receive new features. This also allows
us to release bug fixes expediently without waiting for the
development version to reach a stable state.
<p>Eventually, the development version will near completion and a
new development branch will fork while the original one enters a
feature freeze state. When the original development branch is
ready for release the minor version number will be incremented
to an even value.
<p>
<center>
<img alt="Version Example" src="version.gif">
<br><b>Fig 1: Version Example</b>
</center>
<h2>5. Version Support from the Library</h2>
<p>The library provides a set of macros and functions to query and
check version numbers.
<dl>
<dt><code>H5_VERS_MAJOR</code>
<dt><code>H5_VERS_MINOR</code>
<dt><code>H5_VERS_RELEASE</code>
<dd>These preprocessor constants are defined in the public
include file and determine the version of the include files.
<br><br>
<dt><code>herr_t H5get_libversion (unsigned *<em>majnum</em>, unsigned
*<em>minnum</em>, unsigned *<em>relnum</em>)</code>
<dd>This function returns through its arguments the version
numbers for the library to which the application is linked.
<br><br>
<dt><code>void H5check(void)</code>
<dd>This is a macro that verifies that the version number of the
HDF5 include file used to compile the application matches the
version number of the library to which the application is
linked. This check occurs automatically when the first HDF5
file is created or opened and is important because a mismatch
between the include files and the library is likely to result
in corrupted data and/or segmentation faults. If a mismatch
is detected the library issues an error message on the
standard error stream and aborts with a core dump.
<br><br>
<dt><code>herr_t H5check_version (unsigned <em>majnum</em>,
unsigned <em>minnum</em>, unsigned <em>relnum</em>)</code>
<dd>This function is called by the <code>H5check()</code> macro
with the include file version constants. The function
compares its arguments to the result returned by
<code>H5get_libversion()</code> and if a mismatch is detected prints
an error message on the standard error stream and aborts.
</dl>
<hr>
<address><a href="mailto:hdfhelp@ncsa.uiuc.edu">HDF Help Desk</a></address>
<br>
<!-- Created: Wed Apr 22 11:24:40 EDT 1998 -->
<!-- hhmts start -->
Last modified: Fri Oct 30 10:32:50 EST 1998
<!-- hhmts end -->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

@@ -0,0 +1,136 @@
%TGIF 3.0-p5
state(1,33,100,0,0,0,8,1,9,1,1,0,0,0,0,1,1,'Helvetica',0,17,0,0,0,10,0,0,1,1,0,16,0,0,1,1,1,0,1408,1088,0,0,2880).
%
% @(#)$Header$
% %W%
%
unit("1 pixel/pixel").
page(1,"",1).
box('black',64,64,128,256,0,1,1,22,0,0,0,0,0,'1',[
]).
box('black',80,96,112,224,26,1,1,23,0,0,0,0,0,'1',[
]).
poly('black',2,[
128,160,912,160],1,2,1,24,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',5,[
160,160,144,224,160,272,176,224,160,160],1,2,1,25,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',5,[
848,160,832,224,848,272,864,224,848,160],1,2,1,34,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
box('black',464,192,496,256,26,1,1,39,0,0,0,0,0,'1',[
]).
poly('black',2,[
160,224,464,224],1,2,1,40,0,26,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',2,[
496,224,848,224],1,2,1,41,0,26,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',5,[
192,224,176,288,192,336,208,288,192,224],1,2,1,42,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',5,[
432,224,416,288,432,336,448,288,432,224],1,2,1,43,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',2,[
192,288,432,288],1,2,1,44,0,26,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
box('black',464,352,496,416,26,1,1,45,0,0,0,0,0,'1',[
]).
poly('black',5,[
528,224,512,288,528,336,544,288,528,224],1,2,1,46,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',5,[
816,224,800,288,816,336,832,288,816,224],1,2,1,47,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',2,[
528,288,816,288],1,2,1,48,0,26,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',5,[
464,256,456,304,464,328,488,304,488,256],1,2,1,62,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',2,[
480,352,488,304],2,2,1,85,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
box('black',912,64,976,256,0,1,1,87,0,0,0,0,0,'1',[
]).
box('black',928,96,960,224,26,1,1,88,0,0,0,0,0,'1',[
]).
text('black',96,48,'Helvetica',0,17,1,1,0,1,21,15,89,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"File"]).
text('black',944,48,'Helvetica',0,17,1,1,0,1,64,15,93,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Application"]).
text('black',480,144,'Helvetica',0,17,1,1,0,1,65,15,99,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5D_read()"]).
text('black',480,128,'Helvetica',0,17,1,1,0,1,58,15,108,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5Dread()"]).
text('black',304,208,'Helvetica',0,17,1,1,0,1,86,15,115,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_arr_read()"]).
text('black',304,192,'Helvetica',0,17,1,1,0,1,99,15,119,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5S_simp_fgath()"]).
text('black',296,288,'Helvetica',0,17,1,1,0,1,101,15,125,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_block_read()"]).
text('black',296,304,'Helvetica',0,17,1,1,0,1,90,15,132,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_low_read()"]).
text('black',296,320,'Helvetica',0,17,1,1,0,1,98,15,136,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_sec2_read()"]).
text('black',296,336,'Helvetica',0,17,1,1,0,1,33,15,140,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"read()"]).
text('black',664,208,'Helvetica',0,17,1,1,0,1,106,15,146,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5V_stride_copy()"]).
text('black',664,176,'Helvetica',0,17,1,1,0,1,104,15,150,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5S_simp_mscat()"]).
text('black',664,272,'Helvetica',0,17,1,1,0,1,54,15,154,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"memcpy()"]).
text('black',384,392,'Helvetica',0,17,1,1,0,1,105,15,170,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5T_conv_struct()"]).
poly('black',4,[
392,384,400,352,440,368,456,336],1,1,1,172,1,0,0,0,8,3,0,0,0,'1','8','3',
"6",[
]).
text('black',480,176,'Helvetica',0,17,1,1,0,1,44,15,176,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"TCONV"]).
text('black',480,416,'Helvetica',0,17,1,1,0,1,25,15,182,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"BKG"]).
box('black',48,32,992,512,0,1,1,186,0,0,0,0,0,'1',[
]).
poly('black',5,[
72,392,56,456,72,504,88,456,72,392],1,2,1,188,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
text('black',96,448,'Helvetica',0,17,1,0,0,1,46,15,189,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"== Loop"]).
poly('black',3,[
48,384,152,384,152,512],0,1,1,191,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
text('black',480,40,'Helvetica',0,24,1,1,0,1,380,29,197,0,24,5,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Fig 1: Internal Contiguous Storage"]).
text('black',136,144,'Helvetica',0,17,1,1,0,1,9,15,201,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"A"]).
text('black',160,208,'Helvetica',0,17,1,1,0,1,8,15,207,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"B"]).
text('black',192,272,'Helvetica',0,17,1,1,0,1,9,15,211,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"C"]).
text('black',504,208,'Helvetica',0,17,1,1,0,1,8,15,215,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"E"]).
text('black',528,272,'Helvetica',0,17,1,1,0,1,8,15,223,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"F"]).
text('black',464,304,'Helvetica',0,17,1,1,0,1,9,15,231,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"D"]).
text('black',664,192,'Helvetica',0,17,1,1,0,1,107,15,324,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5V_hyper_copy()"]).

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,168 @@
%TGIF 3.0-p5
state(1,33,100,0,0,0,8,1,9,1,1,1,1,0,0,1,1,'Helvetica',0,17,0,0,0,10,0,0,1,1,0,16,0,0,1,1,1,0,1408,1088,0,0,2880).
%
% @(#)$Header$
% %W%
%
unit("1 pixel/pixel").
page(1,"",1).
box('black',64,64,128,256,0,1,1,22,0,0,0,0,0,'1',[
]).
box('black',80,96,112,224,26,1,1,23,0,0,0,0,0,'1',[
]).
poly('black',2,[
128,160,912,160],1,2,1,24,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',5,[
160,160,144,224,160,272,176,224,160,160],1,2,1,25,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',5,[
848,160,832,224,848,272,864,224,848,160],1,2,1,34,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
box('black',464,192,496,256,26,1,1,39,0,0,0,0,0,'1',[
]).
poly('black',2,[
160,224,464,224],1,2,1,40,0,26,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',2,[
496,224,848,224],1,2,1,41,0,26,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',5,[
192,224,176,288,192,336,208,288,192,224],1,2,1,42,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',5,[
432,224,416,288,432,336,448,288,432,224],1,2,1,43,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',2,[
192,288,432,288],1,2,1,44,0,26,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
box('black',464,352,496,416,26,1,1,45,0,0,0,0,0,'1',[
]).
poly('black',5,[
528,224,512,288,528,336,544,288,528,224],1,2,1,46,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',5,[
816,224,800,288,816,336,832,288,816,224],1,2,1,47,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',2,[
528,288,816,288],1,2,1,48,0,26,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',5,[
848,240,848,352,832,384,800,384,496,384],1,2,1,55,1,0,0,0,10,4,0,0,0,'2','10','4',
"70",[
]).
poly('black',5,[
528,384,512,448,528,496,544,448,528,384],1,2,1,57,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',5,[
800,384,784,448,800,496,816,448,800,384],1,2,1,58,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',2,[
800,448,528,448],1,2,1,61,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',5,[
464,256,456,304,464,328,488,304,488,256],1,2,1,62,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',2,[
480,352,488,304],0,2,1,85,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
box('black',912,64,976,256,0,1,1,87,0,0,0,0,0,'1',[
]).
box('black',928,96,960,224,26,1,1,88,0,0,0,0,0,'1',[
]).
text('black',96,48,'Helvetica',0,17,1,1,0,1,21,15,89,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"File"]).
text('black',944,48,'Helvetica',0,17,1,1,0,1,64,15,93,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Application"]).
text('black',480,144,'Helvetica',0,17,1,1,0,1,65,15,99,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5D_read()"]).
text('black',480,128,'Helvetica',0,17,1,1,0,1,58,15,108,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5Dread()"]).
text('black',304,208,'Helvetica',0,17,1,1,0,1,86,15,115,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_arr_read()"]).
text('black',304,192,'Helvetica',0,17,1,1,0,1,99,15,119,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5S_simp_fgath()"]).
text('black',296,288,'Helvetica',0,17,1,1,0,1,101,15,125,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_block_read()"]).
text('black',296,304,'Helvetica',0,17,1,1,0,1,90,15,132,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_low_read()"]).
text('black',296,320,'Helvetica',0,17,1,1,0,1,98,15,136,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_sec2_read()"]).
text('black',296,336,'Helvetica',0,17,1,1,0,1,33,15,140,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"read()"]).
text('black',664,208,'Helvetica',0,17,1,1,0,1,106,15,146,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5V_stride_copy()"]).
text('black',664,176,'Helvetica',0,17,1,1,0,1,104,15,150,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5S_simp_mscat()"]).
text('black',664,272,'Helvetica',0,17,1,1,0,1,54,15,154,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"memcpy()"]).
text('black',672,368,'Helvetica',0,17,1,1,0,1,106,15,158,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5V_stride_copy()"]).
text('black',672,336,'Helvetica',0,17,1,1,0,1,105,15,162,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5S_simp_mgath()"]).
text('black',672,432,'Helvetica',0,17,1,1,0,1,54,15,166,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"memcpy()"]).
text('black',384,392,'Helvetica',0,17,1,1,0,1,105,15,170,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5T_conv_struct()"]).
poly('black',4,[
392,384,400,352,440,368,456,336],1,1,1,172,1,0,0,0,8,3,0,0,0,'1','8','3',
"6",[
]).
text('black',480,176,'Helvetica',0,17,1,1,0,1,44,15,176,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"TCONV"]).
text('black',480,416,'Helvetica',0,17,1,1,0,1,25,15,182,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"BKG"]).
box('black',48,32,992,512,0,1,1,186,0,0,0,0,0,'1',[
]).
poly('black',5,[
72,392,56,456,72,504,88,456,72,392],1,2,1,188,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
text('black',96,448,'Helvetica',0,17,1,0,0,1,46,15,189,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"== Loop"]).
poly('black',3,[
48,384,152,384,152,512],0,1,1,191,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
text('black',480,40,'Helvetica',0,24,1,1,0,1,404,29,197,0,24,5,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Fig 2: Partially Initialized Destination"]).
text('black',136,144,'Helvetica',0,17,1,1,0,1,9,15,201,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"A"]).
text('black',160,208,'Helvetica',0,17,1,1,0,1,8,15,207,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"B"]).
text('black',192,272,'Helvetica',0,17,1,1,0,1,9,15,211,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"C"]).
text('black',504,208,'Helvetica',0,17,1,1,0,1,8,15,215,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"E"]).
text('black',528,272,'Helvetica',0,17,1,1,0,1,8,15,223,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"F"]).
text('black',856,288,'Helvetica',0,17,1,1,0,1,9,15,225,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"G"]).
text('black',800,432,'Helvetica',0,17,1,1,0,1,9,15,229,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H"]).
text('black',464,304,'Helvetica',0,17,1,1,0,1,9,15,231,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"D"]).
poly('black',4,[
848,240,848,224,864,224,904,224],0,2,1,318,1,0,0,0,10,4,0,0,0,'2','10','4',
"6",[
]).
text('black',664,192,'Helvetica',0,17,1,1,0,1,107,15,326,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5V_hyper_copy()"]).
text('black',672,352,'Helvetica',0,17,1,1,0,1,107,15,334,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5V_hyper_copy()"]).

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,70 @@
%TGIF 3.0-p5
state(1,33,100,0,0,0,8,1,9,1,1,0,0,0,0,1,1,'Helvetica',0,17,0,0,0,10,0,0,1,1,0,16,0,0,1,1,1,0,1408,1088,0,0,2880).
%
% @(#)$Header$
% %W%
%
unit("1 pixel/pixel").
page(1,"",1).
box('black',64,64,128,256,0,1,1,22,0,0,0,0,0,'1',[
]).
box('black',80,96,112,224,26,1,1,23,0,0,0,0,0,'1',[
]).
poly('black',2,[
128,160,912,160],1,2,1,24,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
box('black',912,64,976,256,0,1,1,87,0,0,0,0,0,'1',[
]).
box('black',928,96,960,224,26,1,1,88,0,0,0,0,0,'1',[
]).
text('black',96,48,'Helvetica',0,17,1,1,0,1,21,15,89,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"File"]).
text('black',944,48,'Helvetica',0,17,1,1,0,1,64,15,93,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Application"]).
text('black',480,104,'Helvetica',0,17,1,1,0,1,65,15,99,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5D_read()"]).
text('black',480,88,'Helvetica',0,17,1,1,0,1,58,15,108,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5Dread()"]).
box('black',48,32,992,512,0,1,1,186,0,0,0,0,0,'1',[
]).
poly('black',5,[
72,392,56,456,72,504,88,456,72,392],1,2,1,188,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
text('black',96,448,'Helvetica',0,17,1,0,0,1,46,15,189,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"== Loop"]).
poly('black',3,[
48,384,152,384,152,512],0,1,1,191,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
text('black',480,40,'Helvetica',0,24,1,1,0,1,295,29,197,0,24,5,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Fig 3: No Type Conversion"]).
text('black',136,144,'Helvetica',0,17,1,1,0,1,9,15,201,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"A"]).
poly('black',5,[
152,160,136,224,152,272,168,224,152,160],1,2,1,273,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
text('black',480,120,'Helvetica',0,17,1,1,0,1,96,15,277,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5S_simp_read()"]).
text('black',480,136,'Helvetica',0,17,1,1,0,1,86,15,281,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_arr_read()"]).
poly('black',5,[
880,160,864,224,880,272,896,224,880,160],1,2,1,283,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',2,[
152,224,880,224],1,2,1,286,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
text('black',480,232,'Helvetica',0,17,1,1,0,1,101,15,291,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_block_read()"]).
text('black',480,248,'Helvetica',0,17,1,1,0,1,90,15,293,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_low_read()"]).
text('black',480,264,'Helvetica',0,17,1,1,0,1,98,15,309,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_sec2_read()"]).
text('black',480,280,'Helvetica',0,17,1,1,0,1,33,15,311,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"read()"]).
text('black',176,208,'Helvetica',0,17,1,1,0,1,8,15,418,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"B"]).

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,92 @@
%TGIF 3.0-p5
state(1,33,100,0,0,0,8,1,9,1,1,1,2,1,0,1,0,'Helvetica',0,17,0,0,0,10,0,0,1,1,0,16,0,0,1,1,1,0,1408,1088,0,0,2880).
%
% @(#)$Header$
% %W%
%
unit("1 pixel/pixel").
page(1,"",1).
box('black',64,64,128,256,0,1,1,22,0,0,0,0,0,'1',[
]).
box('black',80,96,112,224,26,1,1,23,0,0,0,0,0,'1',[
]).
poly('black',2,[
128,160,912,160],1,2,1,24,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
box('black',912,96,944,224,26,1,1,88,0,0,0,0,0,'1',[
]).
text('black',96,48,'Helvetica',0,17,1,1,0,1,21,15,89,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"File"]).
text('black',928,72,'Helvetica',0,17,1,1,0,1,32,15,93,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Buffer"]).
box('black',48,32,992,512,0,1,1,186,0,0,0,0,0,'1',[
]).
poly('black',5,[
72,392,56,456,72,504,88,456,72,392],1,2,1,188,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
text('black',96,448,'Helvetica',0,17,1,0,0,1,46,15,189,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"== Loop"]).
poly('black',3,[
48,384,152,384,152,512],0,1,1,191,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
text('black',480,40,'Helvetica',0,24,1,1,0,1,372,29,197,0,24,5,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Fig 4: Regularly Chunked Storage"]).
text('black',136,144,'Helvetica',0,17,1,1,0,1,9,15,201,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"A"]).
text('black',480,104,'Helvetica',0,17,1,1,0,1,86,15,281,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_arr_read()"]).
text('black',480,120,'Helvetica',0,17,1,1,0,1,102,15,349,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_istore_read()"]).
text('black',480,136,'Helvetica',0,17,1,1,0,1,167,15,351,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_istore_copy_hyperslab()"]).
poly('black',5,[
160,160,144,224,160,272,176,224,160,160],1,2,1,362,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
poly('black',5,[
880,160,864,224,880,272,896,224,880,160],1,2,1,363,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
box('black',448,192,512,256,26,1,1,364,0,0,0,0,0,'1',[
]).
text('black',480,176,'Helvetica',0,17,1,1,0,1,43,15,367,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"CHUNK"]).
poly('black',2,[
160,224,448,224],1,2,1,372,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
poly('black',2,[
512,224,880,224],1,2,1,373,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
text('black',288,224,'Helvetica',0,17,1,1,0,1,101,15,385,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_block_read()"]).
text('black',288,240,'Helvetica',0,17,1,1,0,1,90,15,387,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_low_read()"]).
text('black',288,256,'Helvetica',0,17,1,1,0,1,98,15,391,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_sec2_read()"]).
text('black',288,272,'Helvetica',0,17,1,1,0,1,33,15,395,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"read()"]).
poly('black',5,[
456,256,448,296,480,320,512,296,504,256],1,2,1,401,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
text('black',184,208,'Helvetica',0,17,1,1,0,1,8,15,422,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"B"]).
text('black',520,208,'Helvetica',0,17,1,1,0,1,9,15,434,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"D"]).
text('black',440,272,'Helvetica',0,17,1,1,0,1,9,15,440,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"C"]).
text('black',480,320,'Helvetica',0,17,1,1,0,1,107,15,444,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5Z_uncompress()"]).
text('black',672,224,'Helvetica',0,17,1,1,0,1,107,15,454,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5V_hyper_copy()"]).
text('black',672,240,'Helvetica',0,17,1,1,0,1,106,15,464,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5V_stride_copy()"]).
text('black',672,256,'Helvetica',0,17,1,1,0,1,54,15,466,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"memcpy()"]).
text('black',168,488,'Helvetica',0,17,1,0,0,1,282,15,471,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"NOTE: H5Z_uncompress() is not implemented yet."]).

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -0,0 +1,52 @@
%TGIF 3.0-p5
state(1,33,100,0,0,0,8,1,9,1,1,1,2,1,0,1,0,'Helvetica',0,17,0,0,0,10,0,0,1,1,0,16,0,0,1,1,1,0,1408,1088,0,0,2880).
%
% @(#)$Header$
% %W%
%
unit("1 pixel/pixel").
page(1,"",1).
box('black',64,64,128,256,0,1,1,22,0,0,0,0,0,'1',[
]).
box('black',80,96,112,224,26,1,1,23,0,0,0,0,0,'1',[
]).
poly('black',2,[
128,160,912,160],1,2,1,24,0,0,0,0,10,4,0,0,0,'2','10','4',
"0",[
]).
box('black',912,96,944,224,26,1,1,88,0,0,0,0,0,'1',[
]).
text('black',96,48,'Helvetica',0,17,1,1,0,1,21,15,89,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"File"]).
text('black',928,72,'Helvetica',0,17,1,1,0,1,32,15,93,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Buffer"]).
box('black',48,32,992,512,0,1,1,186,0,0,0,0,0,'1',[
]).
text('black',480,40,'Helvetica',0,24,1,1,0,1,333,29,197,0,24,5,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Fig 5: Reading a Single Chunk"]).
text('black',136,144,'Helvetica',0,17,1,1,0,1,9,15,201,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"A"]).
text('black',480,112,'Helvetica',0,17,1,1,0,1,86,15,281,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_arr_read()"]).
text('black',480,128,'Helvetica',0,17,1,1,0,1,102,15,349,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_istore_read()"]).
text('black',480,144,'Helvetica',0,17,1,1,0,1,167,15,351,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_istore_copy_hyperslab()"]).
text('black',480,160,'Helvetica',0,17,1,1,0,1,101,15,385,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_block_read()"]).
text('black',480,176,'Helvetica',0,17,1,1,0,1,90,15,387,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_low_read()"]).
text('black',480,192,'Helvetica',0,17,1,1,0,1,98,15,391,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5F_sec2_read()"]).
text('black',480,208,'Helvetica',0,17,1,1,0,1,33,15,395,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"read()"]).
text('black',864,240,'Helvetica',0,17,1,1,0,1,107,15,444,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"H5Z_uncompress()"]).
text('black',56,488,'Helvetica',0,17,1,0,0,1,282,15,471,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"NOTE: H5Z_uncompress() is not implemented yet."]).
poly('black',5,[
912,176,864,176,840,208,872,232,912,216],1,2,1,490,2,0,0,0,10,4,0,0,0,'2','10','4',
"",[
]).
text('black',896,184,'Helvetica',0,17,1,0,0,1,8,15,491,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"B"]).

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,96 @@
%TGIF 3.0-p5
state(0,33,100,0,0,0,8,1,9,1,1,0,2,1,0,1,0,'Courier',0,17,0,0,0,10,0,0,1,1,0,16,0,0,1,1,1,0,1088,1408,0,0,2880).
%
% @(#)$Header$
% %W%
%
unit("1 pixel/pixel").
page(1,"",1).
poly('black',2,[
128,128,128,448],0,3,1,0,0,0,0,0,12,5,0,0,0,'3','12','5',
"0",[
]).
poly('black',2,[
128,128,128,64],0,3,1,1,0,0,2,0,12,5,0,0,0,'3','12','5',
"0",[
]).
poly('black',2,[
128,448,128,512],0,3,1,4,0,0,2,0,12,5,0,0,0,'3','12','5',
"0",[
]).
text('black',144,112,'Courier',0,17,1,0,0,1,42,14,22,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.3.30"]).
text('black',144,144,'Courier',0,17,1,0,0,1,42,14,30,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.3.31"]).
text('black',144,176,'Courier',0,17,1,0,0,1,42,14,32,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.3.32"]).
poly('black',2,[
256,208,256,448],0,3,1,34,0,0,0,0,12,5,0,0,0,'3','12','5',
"0",[
]).
poly('black',2,[
256,448,256,512],0,3,1,36,0,0,2,0,12,5,0,0,0,'3','12','5',
"0",[
]).
poly('black',2,[
128,192,256,208],1,1,1,37,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
text('black',144,224,'Courier',0,17,1,0,0,1,42,14,41,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.3.33"]).
text('black',144,256,'Courier',0,17,1,0,0,1,42,14,43,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.3.34"]).
text('black',272,224,'Courier',0,17,1,0,0,1,35,14,45,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.5.0"]).
text('black',272,256,'Courier',0,17,1,0,0,1,35,14,47,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.5.1"]).
text('black',272,288,'Courier',0,17,1,0,0,1,35,14,49,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.5.2"]).
text('black',272,320,'Courier',0,17,1,0,0,1,35,14,51,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.5.3"]).
text('black',144,288,'Courier',0,17,1,0,0,1,42,14,53,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.3.35"]).
text('black',144,320,'Courier',0,17,1,0,0,1,35,14,57,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.4.0"]).
text('black',144,368,'Courier',0,17,1,0,0,1,35,14,59,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.4.1"]).
text('black',272,192,'Helvetica',0,17,1,0,0,1,144,15,67,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"New development branch"]).
text('black',144,64,'Helvetica',0,17,1,0,0,1,163,15,69,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Original development branch"]).
text('black',16,208,'Helvetica',0,17,2,0,0,1,87,30,71,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Feature Freeze",
"at this point."]).
text('black',16,320,'Helvetica',0,17,2,0,0,1,84,30,73,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Public Release",
"at this point."]).
poly('black',2,[
104,208,128,208],1,1,1,77,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
104,320,128,320],1,1,1,78,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
poly('black',2,[
256,336,128,352],1,1,1,79,0,0,0,0,8,3,0,0,0,'1','8','3',
"0",[
]).
text('black',320,368,'Helvetica',0,17,3,0,0,1,137,45,82,0,12,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"Merge a bug fix from the",
"development branch to",
"the release branch."]).
box('black',312,368,464,416,0,1,1,87,0,0,0,0,0,'1',[
]).
poly('black',4,[
312,392,240,384,296,344,232,344],1,1,1,90,1,0,0,0,8,3,0,0,0,'1','8','3',
"6",[
]).
box('black',8,208,104,240,0,1,1,95,0,0,0,0,0,'1',[
]).
box('black',8,320,104,352,0,1,1,98,0,0,0,0,0,'1',[
]).
text('black',144,408,'Courier',0,17,1,0,0,1,35,14,102,0,11,3,0,0,0,0,0,2,0,0,0,0,"",0,0,0,[
"1.4.2"]).
box('black',0,40,480,528,0,1,1,104,0,0,0,0,0,'1',[
]).