2893 lines
105 KiB
HTML
2893 lines
105 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
|
<html>
|
|
<head>
|
|
<title>Datatype Interface (H5T)</title>
|
|
</head>
|
|
|
|
<body bgcolor="#FFFFFF">
|
|
|
|
|
|
<hr>
|
|
<center>
|
|
<table border=0 width=98%>
|
|
<tr><td valign=top align=left>
|
|
<a href="index.html">HDF5 documents and links</a> <br>
|
|
<a href="H5.intro.html">Introduction to HDF5</a> <br>
|
|
<a href="RM_H5Front.html">HDF5 Reference Manual</a> <br>
|
|
<!--
|
|
<a href="Glossary.html">Glossary</a><br>
|
|
-->
|
|
</td>
|
|
<td valign=top align=right>
|
|
And in this document, the
|
|
<a href="H5.user.html"><strong>HDF5 User's Guide:</strong></a>
|
|
<br>
|
|
<a href="Files.html">Files</a>
|
|
<a href="Datasets.html">Datasets</a>
|
|
Datatypes
|
|
<a href="Dataspaces.html">Dataspaces</a>
|
|
<a href="Groups.html">Groups</a>
|
|
<br>
|
|
<a href="References.html">References</a>
|
|
<a href="Attributes.html">Attributes</a>
|
|
<a href="Properties.html">Property Lists</a>
|
|
<a href="Errors.html">Error Handling</a>
|
|
<br>
|
|
<a href="Filters.html">Filters</a>
|
|
<a href="Caching.html">Caching</a>
|
|
<a href="Chunking.html">Chunking</a>
|
|
<a href="MountingFiles.html">Mounting Files</a>
|
|
<br>
|
|
<a href="Performance.html">Performance</a>
|
|
<a href="Debugging.html">Debugging</a>
|
|
<a href="Environment.html">Environment</a>
|
|
<a href="ddl.html">DDL</a>
|
|
<br>
|
|
<a href="Ragged.html">Ragged Arrays</a>
|
|
</td></tr>
|
|
</table>
|
|
</center>
|
|
<hr>
|
|
|
|
|
|
<h1>The Datatype Interface (H5T)</h1>
|
|
|
|
<h2>1. Introduction</h2>
|
|
|
|
<p>The datatype interface provides a mechanism to describe the
|
|
storage format of individual data points of a data set and is
|
|
hopefully designed in such a way as to allow new features to be
|
|
easily added without disrupting applications that use the
|
|
datatype interface. A dataset (the H5D interface) is composed of a
|
|
collection or raw data points of homogeneous type organized
|
|
according to the data space (the H5S interface).
|
|
|
|
<p>A datatype is a collection of datatype properties, all of
|
|
which can be stored on disk, and which when taken as a whole,
|
|
provide complete information for data conversion to or from that
|
|
datatype. The interface provides functions to set and query
|
|
properties of a datatype.
|
|
|
|
<p>A <em>data point</em> is an instance of a <em>datatype</em>,
|
|
which is an instance of a <em>type class</em>. We have defined
|
|
a set of type classes and properties which can be extended at a
|
|
later time. The atomic type classes are those which describe
|
|
types which cannot be decomposed at the datatype interface
|
|
level; all other classes are compound.
|
|
|
|
<h2>2. General Datatype Operations</h2>
|
|
|
|
<p>The functions defined in this section operate on datatypes as
|
|
a whole. New datatypes can be created from scratch or copied
|
|
from existing datatypes. When a datatype is no longer needed
|
|
its resources should be released by calling <code>H5Tclose()</code>.
|
|
|
|
<p> Datatypes come in two flavors: named datatypes and transient
|
|
datatypes. A named datatype is stored in a file while the
|
|
transient flavor is independent of any file. Named datatypes
|
|
are always read-only, but transient types come in three
|
|
varieties: modifiable, read-only, and immutable. The difference
|
|
between read-only and immutable types is that immutable types
|
|
cannot be closed except when the entire library is closed (the
|
|
predefined types like <code>H5T_NATIVE_INT</code> are immutable
|
|
transient types).
|
|
|
|
<dl>
|
|
<dt><code>hid_t H5Tcreate (H5T_class_t <em>class</em>, size_t
|
|
<em>size</em>)</code>
|
|
<dd> Datatypes can be created by calling this
|
|
function, where <em>class</em> is a datatype class
|
|
identifier. However, the only class currently allowed is
|
|
<code>H5T_COMPOUND</code> to create a new empty compound
|
|
datatype where <em>size</em> is the total size in bytes of an
|
|
instance of this datatype. Other datatypes are created with
|
|
<code>H5Tcopy()</code>. All functions that return datatype
|
|
identifiers return a negative value for failure.
|
|
|
|
<br><br>
|
|
<dt><code>hid_t H5Topen (hid_t <em>location</em>, const char
|
|
*<em>name</em>)</code>
|
|
<dd>A named datatype can be opened by calling this function,
|
|
which returns a datatype identifier. The identifier should
|
|
eventually be released by calling <code>H5Tclose()</code> to
|
|
release resources. The named datatype returned by this
|
|
function is read-only or a negative value is returned for
|
|
failure. The <em>location</em> is either a file or group
|
|
identifier.
|
|
|
|
<br><br>
|
|
<dt><code>herr_t H5Tcommit (hid_t <em>location</em>, const char
|
|
*<em>name</em>, hid_t <em>type</em>)</code>
|
|
<dd>A transient datatype (not immutable) can be committed to a
|
|
file and turned into a named datatype by calling this
|
|
function. The <em>location</em> is either a file or group
|
|
identifier and when combined with <em>name</em> refers to a new
|
|
named datatype.
|
|
|
|
<br><br>
|
|
<dt><code>htri_t H5Tcommitted (hid_t <em>type</em>)</code>
|
|
<dd>A type can be queried to determine if it is a named type or
|
|
a transient type. If this function returns a positive value
|
|
then the type is named (that is, it has been committed perhaps
|
|
by some other application). Datasets which return committed
|
|
datatypes with <code>H5Dget_type()</code> are able to share
|
|
the datatype with other datasets in the same file.
|
|
|
|
<br><br>
|
|
<dt><code>hid_t H5Tcopy (hid_t <em>type</em>)</code>
|
|
<dd>This function returns a modifiable transient datatype
|
|
which is a copy of <em>type</em> or a negative value for
|
|
failure. If <em>type</em> is a dataset identifier then the type
|
|
returned is a modifiable transient copy of the datatype of
|
|
the specified dataset.
|
|
|
|
<br><br>
|
|
<dt><code>herr_t H5Tclose (hid_t <em>type</em>)</code>
|
|
<dd>Releases resources associated with a datatype. The
|
|
datatype identifier should not be subsequently used since the
|
|
results would be unpredictable. It is illegal to close an
|
|
immutable transient datatype.
|
|
|
|
<br><br>
|
|
<dt><code>htri_t H5Tequal (hid_t <em>type1</em>, hid_t
|
|
<em>type2</em>)</code>
|
|
<dd>Determines if two types are equal. If <em>type1</em> and
|
|
<em>type2</em> are the same then this function returns
|
|
<code>TRUE</code>, otherwise it returns <code>FALSE</code> (an
|
|
error results in a negative return value).
|
|
|
|
<br><br>
|
|
<dt><code>herr_t H5Tlock (hid_t <em>type</em>)</code>
|
|
<dd>A transient datatype can be locked, making it immutable
|
|
(read-only and not closable). The library does this to all
|
|
predefined types to prevent the application from inadvertently
|
|
modifying or deleting (closing) them, but the application is
|
|
also allowed to do this for its own datatypes. Immutable
|
|
datatypes are closed when the library closes (either by
|
|
<code>H5close()</code> or by normal program termination).
|
|
</dl>
|
|
|
|
<h2>3. Properties of Atomic Types</h2>
|
|
|
|
<p>An atomic type is a type which cannot be decomposed into
|
|
smaller units at the API level. All atomic types have a common
|
|
set of properties which are augmented by properties specific to
|
|
a particular type class. Some of these properties also apply to
|
|
compound datatypes, but we discuss them only as they apply to
|
|
atomic datatypes here. The properties and the functions that
|
|
query and set their values are:
|
|
|
|
<dl>
|
|
<dt><code>H5T_class_t H5Tget_class (hid_t <em>type</em>)</code>
|
|
<dd>This property holds one of the class names:
|
|
<code>H5T_INTEGER, H5T_FLOAT, H5T_TIME, H5T_STRING, or
|
|
H5T_BITFIELD</code>. This property is read-only and is set
|
|
when the datatype is created or copied (see
|
|
<code>H5Tcreate()</code>, <code>H5Tcopy()</code>). If this
|
|
function fails it returns <code>H5T_NO_CLASS</code> which has
|
|
a negative value (all other class constants are non-negative).
|
|
|
|
<br><br>
|
|
<dt><code>size_t H5Tget_size (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_size (hid_t <em>type</em>, size_t
|
|
<em>size</em>)</code>
|
|
<dd>This property is total size of the datum in bytes, including
|
|
padding which may appear on either side of the actual value.
|
|
If this property is reset to a smaller value which would cause
|
|
the significant part of the data to extend beyond the edge of
|
|
the datatype then the <code>offset</code> property is
|
|
decremented a bit at a time. If the offset reaches zero and
|
|
the significant part of the data still extends beyond the edge
|
|
of the datatype then the <code>precision</code> property is
|
|
decremented a bit at a time. Decreasing the size of a
|
|
datatype may fail if the <code>H5T_FLOAT</code> bit fields would
|
|
extend beyond the significant part of the type. Adjusting the
|
|
size of an <code>H5T_STRING</code> automatically adjusts the
|
|
precision as well. On error, <code>H5Tget_size()</code>
|
|
returns zero which is never a valid size.
|
|
|
|
<br><br>
|
|
<dt><code>H5T_order_t H5Tget_order (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_order (hid_t <em>type</em>, H5T_order_t
|
|
<em>order</em>)</code>
|
|
<dd>All atomic datatypes have a byte order which describes how
|
|
the bytes of the datatype are layed out in memory. If the
|
|
lowest memory address contains the least significant byte of
|
|
the datum then it is said to be <em>little-endian</em> or
|
|
<code>H5T_ORDER_LE</code>. If the bytes are in the oposite
|
|
order then they are said to be <em>big-endian</em> or
|
|
<code>H5T_ORDER_BE</code>. Some datatypes have the same byte
|
|
order on all machines and are <code>H5T_ORDER_NONE</code>
|
|
(like character strings). If <code>H5Tget_order()</code>
|
|
fails then it returns <code>H5T_ORDER_ERROR</code> which is a
|
|
negative value (all successful return values are
|
|
non-negative).
|
|
|
|
<br><br>
|
|
<dt><code>size_t H5Tget_precision (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_precision (hid_t <em>type</em>, size_t
|
|
<em>precision</em>)</code>
|
|
<dd>Some datatypes occupy more bytes than what is needed to
|
|
store the value. For instance, a <code>short</code> on a Cray
|
|
is 32 significant bits in an eight-byte field. The
|
|
<code>precision</code> property identifies the number of
|
|
significant bits of a datatype and the <code>offset</code>
|
|
property (defined below) identifies its location. The
|
|
<code>size</code> property defined above represents the entire
|
|
size (in bytes) of the datatype. If the precision is
|
|
decreased then padding bits are inserted on the MSB side of
|
|
the significant bits (this will fail for
|
|
<code>H5T_FLOAT</code> types if it results in the sign,
|
|
mantissa, or exponent bit field extending beyond the edge of
|
|
the significant bit field). On the other hand, if the
|
|
precision is increased so that it "hangs over" the edge of the
|
|
total size then the <code>offset</code> property is
|
|
decremented a bit at a time. If the <code>offset</code>
|
|
reaches zero and the significant bits still hang over the
|
|
edge, then the total size is increased a byte at a time. The
|
|
precision of an <code>H5T_STRING</code> is read-only and is
|
|
always eight times the value returned by
|
|
<code>H5Tget_size()</code>. <code>H5Tget_precision()</code>
|
|
returns zero on failure since zero is never a valid precision.
|
|
|
|
<br><br>
|
|
<dt><code>size_t H5Tget_offset (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_offset (hid_t <em>type</em>, size_t
|
|
<em>offset</em>)</code>
|
|
<dd>While the <code>precision</code> property defines the number
|
|
of significant bits, the <code>offset</code> property defines
|
|
the location of those bits within the entire datum. The bits
|
|
of the entire data are numbered beginning at zero at the least
|
|
significant bit of the least significant byte (the byte at the
|
|
lowest memory address for a little-endian type or the byte at
|
|
the highest address for a big-endian type). The
|
|
<code>offset</code> property defines the bit location of the
|
|
least signficant bit of a bit field whose length is
|
|
<code>precision</code>. If the offset is increased so the
|
|
significant bits "hang over" the edge of the datum, then the
|
|
<code>size</code> property is automatically incremented. The
|
|
offset is a read-only property of an <code>H5T_STRING</code>
|
|
and is always zero. <code>H5Tget_offset()</code> returns zero
|
|
on failure which is also a valid offset, but is guaranteed to
|
|
succeed if a call to <code>H5Tget_precision()</code> succeeds
|
|
with the same arguments.
|
|
|
|
<br><br>
|
|
<dt><code>herr_t H5Tget_pad (hid_t <em>type</em>, H5T_pad_t
|
|
*<em>lsb</em>, H5T_pad_t *<em>msb</em>)</code>
|
|
<dt><code>herr_t H5Tset_pad (hid_t <em>type</em>, H5T_pad_t
|
|
<em>lsb</em>, H5T_pad_t <em>msb</em>)</code>
|
|
<dd>The bits of a datum which are not significant as defined by
|
|
the <code>precision</code> and <code>offset</code> properties
|
|
are called <em>padding</em>. Padding falls into two
|
|
categories: padding in the low-numbered bits is <em>lsb</em>
|
|
padding and padding in the high-numbered bits is <em>msb</em>
|
|
padding (bits are numbered according to the description for
|
|
the <code>offset</code> property). Padding bits can always be
|
|
set to zero (<code>H5T_PAD_ZERO</code>) or always set to one
|
|
(<code>H5T_PAD_ONE</code>). The current pad types are returned
|
|
through arguments of <code>H5Tget_pad()</code> either of which
|
|
may be null pointers.
|
|
</dl>
|
|
|
|
<h3>3.1. Properties of Integer Atomic Types</h3>
|
|
|
|
<p>Integer atomic types (<code>class=H5T_INTEGER</code>)
|
|
describe integer number formats. Such types include the
|
|
following information which describes the type completely and
|
|
allows conversion between various integer atomic types.
|
|
|
|
<dl>
|
|
<dt><code>H5T_sign_t H5Tget_sign (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_sign (hid_t <em>type</em>, H5T_sign_t
|
|
<em>sign</em>)</code>
|
|
<dd>Integer data can be signed two's complement
|
|
(<code>H5T_SGN_2</code>) or unsigned
|
|
(<code>H5T_SGN_NONE</code>). Whether data is signed or not
|
|
becomes important when converting between two integer
|
|
datatypes of differing sizes as it determines how values are
|
|
truncated and sign extended.
|
|
</dl>
|
|
|
|
<h3>3.2. Properties of Floating-point Atomic Types</h3>
|
|
|
|
<p>The library supports floating-point atomic types
|
|
(<code>class=H5T_FLOAT</code>) as long as the bits of the
|
|
exponent are contiguous and stored as a biased positive number,
|
|
the bits of the mantissa are contiguous and stored as a positive
|
|
magnitude, and a sign bit exists which is set for negative
|
|
values. Properties specific to floating-point types are:
|
|
|
|
<dl>
|
|
<dt><code>herr_t H5Tget_fields (hid_t <em>type</em>, size_t
|
|
*<em>spos</em>, size_t *<em>epos</em>, size_t
|
|
*<em>esize</em>, size_t *<em>mpos</em>, size_t
|
|
*<em>msize</em>)</code>
|
|
<dt><code>herr_t H5Tset_fields (hid_t <em>type</em>, size_t
|
|
<em>spos</em>, size_t <em>epos</em>, size_t <em>esize</em>,
|
|
size_t <em>mpos</em>, size_t <em>msize</em>)</code>
|
|
<dd>A floating-point datum has bit fields which are the exponent
|
|
and mantissa as well as a mantissa sign bit. These properties
|
|
define the location (bit position of least significant bit of
|
|
the field) and size (in bits) of each field. The bit
|
|
positions are numbered beginning at zero at the beginning of
|
|
the significant part of the datum (see the descriptions of the
|
|
<code>precision</code> and <code>offset</code>
|
|
properties). The sign bit is always of length one and none of
|
|
the fields are allowed to overlap. When expanding a
|
|
floating-point type one should set the precision first; when
|
|
decreasing the size one should set the field positions and
|
|
sizes first.
|
|
|
|
<br><br>
|
|
<dt><code>size_t H5Tget_ebias (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_ebias (hid_t <em>type</em>, size_t
|
|
<em>ebias</em>)</code>
|
|
<dd>The exponent is stored as a non-negative value which is
|
|
<code>ebias</code> larger than the true exponent.
|
|
<code>H5Tget_ebias()</code> returns zero on failure which is
|
|
also a valid exponent bias, but the function is guaranteed to
|
|
succeed if <code>H5Tget_precision()</code> succeeds when
|
|
called with the same arguments.
|
|
|
|
<br><br>
|
|
<dt><code>H5T_norm_t H5Tget_norm (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_norm (hid_t <em>type</em>, H5T_norm_t
|
|
<em>norm</em>)</code>
|
|
<dd>This property determines the normalization method of the
|
|
mantissa.
|
|
<ul>
|
|
<li>If the value is <code>H5T_NORM_MSBSET</code> then the
|
|
mantissa is shifted left (if non-zero) until the first bit
|
|
after the radix point is set and the exponent is adjusted
|
|
accordingly. All bits of the mantissa after the radix
|
|
point are stored.
|
|
|
|
<li>If its value is <code>H5T_NORM_IMPLIED</code> then the
|
|
mantissa is shifted left (if non-zero) until the first bit
|
|
after the radix point is set and the exponent is adjusted
|
|
accordingly. The first bit after the radix point is not stored
|
|
since it's always set.
|
|
|
|
<li>If its value is <code>H5T_NORM_NONE</code> then the fractional
|
|
part of the mantissa is stored without normalizing it.
|
|
</ul>
|
|
|
|
<br><br>
|
|
<dt><code>H5T_pad_t H5Tget_inpad (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_inpad (hid_t <em>type</em>, H5T_pad_t
|
|
<em>inpad</em>)</code>
|
|
<dd>If any internal bits (that is, bits between the sign bit,
|
|
the mantissa field, and the exponent field but within the
|
|
precision field) are unused, then they will be filled
|
|
according to the value of this property. The <em>inpad</em>
|
|
argument can be <code>H5T_PAD_ZERO</code> if the internal
|
|
padding should always be set to zero, or <code>H5T_PAD_ONE</code>
|
|
if it should always be set to one.
|
|
<code>H5Tget_inpad()</code> returns <code>H5T_PAD_ERROR</code>
|
|
on failure which is a negative value (successful return is
|
|
always non-negative).
|
|
</dl>
|
|
|
|
<h3>3.3. Properties of Date and Time Atomic Types</h3>
|
|
|
|
<p>Dates and times (<code>class=H5T_TIME</code>) are stored as
|
|
character strings in one of the ISO-8601 formats like
|
|
"<em>1997-12-05 16:25:30</em>"; as character strings using the
|
|
Unix asctime(3) format like "<em>Thu Dec 05 16:25:30 1997</em>";
|
|
as an integer value by juxtaposition of the year, month, and
|
|
day-of-month, hour, minute and second in decimal like
|
|
<em>19971205162530</em>; as an integer value in Unix time(2)
|
|
format; or other variations.
|
|
|
|
<h3>3.4. Properties of Character String Atomic Types</h3>
|
|
|
|
<p>Fixed-length character string types are used to store textual
|
|
information. The <code>offset</code> property of a string is
|
|
always zero and the <code>precision</code> property is eight
|
|
times as large as the value returned by
|
|
<code>H5Tget_size()</code> (since precision is measured in bits
|
|
while size is measured in bytes). Both properties are
|
|
read-only.
|
|
|
|
<dl>
|
|
<dt><code>H5T_cset_t H5Tget_cset (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_cset (hid_t <em>type</em>, H5T_cset_t
|
|
<em>cset</em>)</code>
|
|
<dd>HDF5 is able to distinguish between character sets of
|
|
different nationalities and to convert between them to the
|
|
extent possible. The only character set currently supported
|
|
is <code>H5T_CSET_ASCII</code>.
|
|
|
|
<br><br>
|
|
<dt><code>H5T_str_t H5Tget_strpad (hid_t <em>type</em>)</code>
|
|
<dt><code>herr_t H5Tset_strpad (hid_t <em>type</em>, H5T_str_t
|
|
<em>strpad</em>)</code>
|
|
<dd>The method used to store character strings differs with the
|
|
programming language: C usually null terminates strings while
|
|
Fortran left-justifies and space-pads strings. This property
|
|
defines the storage mechanism and can be
|
|
|
|
<p>
|
|
<dl>
|
|
<dt><code>H5T_STR_NULLTERM</code>
|
|
<dd>A C-style string which is guaranteed to be null
|
|
terminated. When converting from a longer string the
|
|
value will be truncated and then a null character
|
|
appended.
|
|
|
|
<br><br>
|
|
<dt><code>H5T_STR_NULLPAD</code>
|
|
<dd>A C-style string which is padded with null characters
|
|
but not necessarily null terminated. Conversion from a
|
|
long string to a shorter <code>H5T_STR_NULLPAD</code>
|
|
string will truncate but not null terminate. Conversion
|
|
from a short value to a longer value will append null
|
|
characters as with <code>H5T_STR_NULLTERM</code>.
|
|
|
|
<br><br>
|
|
<dt><code>H5T_STR_SPACEPAD</code>
|
|
<dd>A Fortran-style string which is padded with space
|
|
characters. This is the same as
|
|
<code>H5T_STR_NULLPAD</code> except the padding character
|
|
is a space instead of a null.
|
|
</dl>
|
|
|
|
<p><code>H5Tget_strpad()</code> returns
|
|
<code>H5T_STR_ERROR</code> on failure, a negative value (all
|
|
successful return values are non-negative).
|
|
</dl>
|
|
|
|
<h3>3.5. Properties of Bit Field Atomic Types</h3>
|
|
|
|
<p>Converting a bit field (<code>class=H5T_BITFIELD</code>) from
|
|
one type to another simply copies the significant bits. If the
|
|
destination is smaller than the source then bits are truncated.
|
|
Otherwise new bits are filled according to the <code>msb</code>
|
|
padding type.
|
|
|
|
<h3>3.6. Character and String Datatype Issues</h3>
|
|
|
|
The <code>H5T_NATIVE_CHAR</code> and <code>H5T_NATIVE_UCHAR</code>
|
|
datatypes are actually numeric data (1-byte integers). If the
|
|
application wishes to store character data, then an HDF5
|
|
<em>string</em> datatype should be derived from
|
|
<code>H5T_C_S1</code> instead.
|
|
|
|
<h4>Motivation</h4>
|
|
|
|
HDF5 defines at least three classes of datatypes:
|
|
integer data, floating point data, and character data.
|
|
However, the C language defines only integer and
|
|
floating point datatypes; character data in C is
|
|
overloaded on the 8- or 16-bit integer types and
|
|
character strings are overloaded on arrays of those
|
|
integer types which, by convention, are terminated with
|
|
a zero element.
|
|
|
|
In C, the variable <code>unsigned char s[256]</code> is
|
|
either an array of numeric data, a single character string
|
|
with at most 255 characters, or an array of 256 characters,
|
|
depending entirely on usage. For uniformity with the
|
|
other <code>H5T_NATIVE_</code> types, HDF5 uses the
|
|
numeric interpretation of <code>H5T_NATIVE_CHAR</code>
|
|
and <code>H5T_NATIVE_UCHAR</code>.
|
|
|
|
|
|
<h4>Usage</h4>
|
|
|
|
To store <code>unsigned char s[256]</code> data as an
|
|
array of integer values, use the HDF5 datatype
|
|
<code>H5T_NATIVE_UCHAR</code> and a data space that
|
|
describes the 256-element array. Some other application
|
|
that reads the data will then be able to read, say, a
|
|
256-element array of 2-byte integers and HDF5 will
|
|
perform the numeric translation.
|
|
|
|
To store <code>unsigned char s[256]</code> data as a
|
|
character string, derive a fixed length string datatype
|
|
from <code>H5T_C_S1</code> by increasing its size to
|
|
256 characters. Some other application that reads the
|
|
data will be able to read, say, a space padded string
|
|
of 16-bit characters and HDF5 will perform the character
|
|
and padding translations.
|
|
|
|
<pre>
|
|
hid_t s256 = H5Tcopy(H5T_C_S1);
|
|
H5Tset_size(s256, 256);
|
|
</pre>
|
|
|
|
To store <code>unsigned char s[256]</code> data as
|
|
an array of 256 ASCII characters, use an
|
|
HDF5 data space to describe the array and derive a
|
|
one-character string type from <code>H5T_C_S1</code>.
|
|
Some other application will be able to read a subset
|
|
of the array as 16-bit characters and HDF5 will
|
|
perform the character translations.
|
|
The <code>H5T_STR_NULLPAD</code> is necessary because
|
|
if <code>H5T_STR_NULLTERM</code> were used
|
|
(the default) then the single character of storage
|
|
would be for the null terminator and no useful data
|
|
would actually be stored (unless the length were
|
|
incremented to more than one character).
|
|
|
|
<pre>
|
|
hid_t s1 = H5Tcopy(H5T_C_S1);
|
|
H5Tset_strpad(s1, H5T_STR_NULLPAD);
|
|
</pre>
|
|
|
|
<h4>Summary</h4>
|
|
|
|
The C language uses the term <code>char</code> to
|
|
represent one-byte numeric data and does not make
|
|
character strings a first-class datatype.
|
|
HDF5 makes a distinction between integer and
|
|
character data and maps the C <code>signed char</code>
|
|
(<code>H5T_NATIVE_CHAR</code>) and
|
|
<code>unsigned char</code> (<code>H5T_NATIVE_UCHAR</code>)
|
|
datatypes to the HDF5 integer type class.
|
|
|
|
<h2>4. Properties of Opaque Types</h2>
|
|
|
|
<p>Opaque types (<code>class=H5T_OPAQUE</code>) provide the
|
|
application with a mechanism for describing data which cannot be
|
|
otherwise described by HDF5. The only properties associated with
|
|
opaque types are a size in bytes and an ASCII tag which is
|
|
manipulated with <code>H5Tset_tag()</code> and
|
|
<code>H5Tget_tag()</code> functions. The library contains no
|
|
predefined conversion functions but the application is free to
|
|
register conversions between any two opaque types or between an
|
|
opaque type and some other type.
|
|
|
|
<h2>5. Properties of Compound Types</h2>
|
|
|
|
<p>A compound datatype is similar to a <code>struct</code> in C
|
|
or a common block in Fortran: it is a collection of one or more
|
|
atomic types or small arrays of such types. Each
|
|
<em>member</em> of a compound type has a name which is unique
|
|
within that type, and a byte offset that determines the first
|
|
byte (smallest byte address) of that member in a compound datum.
|
|
A compound datatype has the following properties:
|
|
|
|
<dl>
|
|
<dt><code>H5T_class_t H5Tget_class (hid_t <em>type</em>)</code>
|
|
<dd>All compound datatypes belong to the type class
|
|
<code>H5T_COMPOUND</code>. This property is read-only and is
|
|
defined when a datatype is created or copied (see
|
|
<code>H5Tcreate()</code> or <code>H5Tcopy()</code>).
|
|
|
|
<br><br>
|
|
<dt><code>size_t H5Tget_size (hid_t <em>type</em>)</code>
|
|
<dd>Compound datatypes have a total size in bytes which is
|
|
returned by this function. All members of a compound
|
|
datatype must exist within this size. A value of zero is returned
|
|
for failure; all successful return values are positive.
|
|
|
|
<br><br>
|
|
<dt><code>int H5Tget_nmembers (hid_t <em>type</em>)</code>
|
|
<dd>A compound datatype consists of zero or more members
|
|
(defined in any order) with unique names and which occupy
|
|
non-overlapping regions within the datum. In the functions
|
|
that follow, individual members are referenced by an index
|
|
number between zero and <em>N</em>-1, inclusive, where
|
|
<em>N</em> is the value returned by this function.
|
|
<code>H5Tget_nmembers()</code> returns -1 on failure.
|
|
|
|
<br><br>
|
|
<dt><code>char *H5Tget_member_name (hid_t <em>type</em>, int
|
|
<em>membno</em>)</code>
|
|
<dd>Each member has a name which is unique among its siblings in
|
|
a compound datatype. This function returns a pointer to a
|
|
null-terminated copy of the name allocated with
|
|
<code>malloc()</code> or the null pointer on failure. The
|
|
caller is responsible for freeing the memory returned by this
|
|
function.
|
|
|
|
<br><br>
|
|
<dt><code>size_t H5Tget_member_offset (hid_t <em>type</em>, int
|
|
<em>membno</em>)</code>
|
|
<dd>The byte offset of member number <em>membno</em> with
|
|
respect to the beginning of the containing compound datum is
|
|
returned by this function. A zero is returned on failure
|
|
which is also a valid offset, but this function is guaranteed
|
|
to succeed if a call to <code>H5Tget_member_dims()</code>
|
|
succeeds when called with the same <em>type</em> and
|
|
<em>membno</em> arguments.
|
|
|
|
<br><br>
|
|
<dt><code>int H5Tget_member_dims (hid_t <em>type</em>, int
|
|
<em>membno</em>, int <em>dims</em>[4], int
|
|
<em>perm</em>[4])</code>
|
|
<dd>Each member can be a small array of up to four dimensions,
|
|
making it convenient to describe things like transposition
|
|
matrices. The dimensionality of the member is returned (or
|
|
negative for failure) and the size in each dimension is
|
|
returned through the <em>dims</em> argument. The
|
|
<em>perm</em> argument describes how the array's elements are
|
|
mapped to the linear address space of memory with respect to
|
|
some reference order (the reference order is specified in
|
|
natural language documentation which describes the compound
|
|
datatype). The application which "invented" the type will
|
|
often use the identity permutation and other applications will
|
|
use a permutation that causes the elements to be rearranged to
|
|
the desired order. Only the first few elements of
|
|
<em>dims</em> and <em>perm</em> are initialized according to
|
|
the dimensionality of the member. Scalar members have
|
|
dimensionality zero.
|
|
|
|
<b>The only permutations supported at this
|
|
time are the identity permutation and the transpose
|
|
permutation (in the 4d case, {0,1,2,3} and {3,2,1,0}).</b>
|
|
|
|
<br><br>
|
|
<dt><code>hid_t H5Tget_member_type (hid_t <em>type</em>, int
|
|
<em>membno</em>)</code>
|
|
<dd>Each member has its own datatype, a copy of which is
|
|
returned by this function. The returned datatype identifier
|
|
should be released by eventually calling
|
|
<code>H5Tclose()</code> on that type.
|
|
</dl>
|
|
|
|
<p>Properties of members of a compound datatype are
|
|
defined when the member is added to the compound type (see
|
|
<code>H5Tinsert()</code>) and cannot be subsequently modified.
|
|
This makes it imposible to define recursive data structures.
|
|
|
|
<a name="DTypes-PredefinedAtomic">
|
|
<h2>6. Predefined Atomic Datatypes</h2>
|
|
</a>
|
|
|
|
<p>The library predefines a modest number of datatypes having
|
|
names like <code>H5T_<em>arch</em>_<em>base</em></code> where
|
|
<em>arch</em> is an architecture name and <em>base</em> is a
|
|
programming type name. New types can be derived from the
|
|
predifined types by copying the predefined type (see
|
|
<code>H5Tcopy()</code>) and then modifying the result.
|
|
|
|
<p>
|
|
<center>
|
|
<table align=center width="80%">
|
|
<tr>
|
|
<th align=left width="20%">Architecture Name</th>
|
|
<th align=left width="80%">Description</th>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>IEEE</code></td>
|
|
<td>This architecture defines standard floating point
|
|
types in various byte orders.</td>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>STD</code></td>
|
|
<td>This is an architecture that contains semi-standard
|
|
datatypes like signed two's complement integers,
|
|
unsigned integers, and bitfields in various byte
|
|
orders.</td>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>UNIX</code></td>
|
|
<td>Types which are specific to Unix operating systems are
|
|
defined in this architecture. The only type currently
|
|
defined is the Unix date and time types
|
|
(<code>time_t</code>).</td>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>C<br>FORTRAN</code></td>
|
|
<td>Types which are specific to the C or Fortran
|
|
programming languages are defined in these
|
|
architectures. For instance, <code>H5T_C_STRING</code>
|
|
defines a base string type with null termination which
|
|
can be used to derive string types of other
|
|
lengths.</td>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>NATIVE</code></td>
|
|
<td>This architecture contains C-like datatypes for the
|
|
machine on which the library was compiled. The types
|
|
were actually defined by running the
|
|
<code>H5detect</code> program when the library was
|
|
compiled. In order to be portable, applications should
|
|
almost always use this architecture to describe things
|
|
in memory.</td>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>CRAY</code></td>
|
|
<td>Cray architectures. These are word-addressable,
|
|
big-endian systems with non-IEEE floating point.</td>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>INTEL</code></td>
|
|
<td>All Intel and compatible CPU's including 80286, 80386,
|
|
80486, Pentium, Pentium-Pro, and Pentium-II. These are
|
|
little-endian systems with IEEE floating-point.</td>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>MIPS</code></td>
|
|
<td>All MIPS CPU's commonly used in SGI systems. These
|
|
are big-endian systems with IEEE floating-point.</td>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>ALPHA</code></td>
|
|
<td>All DEC Alpha CPU's, little-endian systems with IEEE
|
|
floating-point.</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>The base name of most types consists of a letter, a precision
|
|
in bits, and an indication of the byte order. The letters are:
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="40%">
|
|
<tr>
|
|
<td align=center width="30%">B</td>
|
|
<td width="70%">Bitfield</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>D</td>
|
|
<td>Date and time</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>F</td>
|
|
<td>Floating point</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>I</td>
|
|
<td>Signed integer</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>R</td>
|
|
<td>References</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>S</td>
|
|
<td>Character string</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>U</td>
|
|
<td>Unsigned integer</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>The byte order is a two-letter sequence:
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="40%">
|
|
<tr>
|
|
<td align=center width="30%">BE</td>
|
|
<td width="70%">Big endian</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>LE</td>
|
|
<td>Little endian</td>
|
|
</tr>
|
|
<tr>
|
|
<td align=center>VX</td>
|
|
<td>Vax order</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>
|
|
<center>
|
|
<table align=center width="80%">
|
|
<tr>
|
|
<th align=left><br><br>Example</th>
|
|
<th align=left><br><br>Description</th>
|
|
</tr>
|
|
|
|
<tr valign=top>
|
|
<td><code>H5T_IEEE_F64LE</code></td>
|
|
<td>Eight-byte, little-endian, IEEE floating-point</td>
|
|
</tr>
|
|
<tr valign=top>
|
|
<td><code>H5T_IEEE_F32BE</code></td>
|
|
<td>Four-byte, big-endian, IEEE floating point</td>
|
|
</tr>
|
|
<tr valign=top>
|
|
<td><code>H5T_STD_I32LE</code></td>
|
|
<td>Four-byte, little-endian, signed two's complement integer</td>
|
|
</tr>
|
|
<tr valign=top>
|
|
<td><code>H5T_STD_U16BE</code></td>
|
|
<td>Two-byte, big-endian, unsigned integer</td>
|
|
</tr>
|
|
<tr valign=top>
|
|
<td><code>H5T_UNIX_D32LE</code></td>
|
|
<td>Four-byte, little-endian, time_t</td>
|
|
</tr>
|
|
<tr valign=top>
|
|
<td><code>H5T_C_S1</code></td>
|
|
<td>One-byte, null-terminated string of eight-bit characters</td>
|
|
</tr>
|
|
<tr valign=top>
|
|
<td><code>H5T_INTEL_B64</code></td>
|
|
<td>Eight-byte bit field on an Intel CPU</td>
|
|
</tr>
|
|
<tr valign=top>
|
|
<td><code>H5T_CRAY_F64</code></td>
|
|
<td>Eight-byte Cray floating point</td>
|
|
</tr>
|
|
<tr valign=top>
|
|
<td><code>H5T_STD_ROBJ</code></td>
|
|
<td>Reference to an entire object in a file</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>The <code>NATIVE</code> architecture has base names which don't
|
|
follow the same rules as the others. Instead, native type names
|
|
are similar to the C type names. Here are some examples:
|
|
|
|
<p>
|
|
<center>
|
|
<table align=center width="80%">
|
|
<tr>
|
|
<th align=left><br><br>Example</th>
|
|
<th align=left><br><br>Corresponding C Type</th>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_CHAR</code></td>
|
|
<td><code>char</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_SCHAR</code></td>
|
|
<td><code>signed char</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_UCHAR</code></td>
|
|
<td><code>unsigned char</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_SHORT</code></td>
|
|
<td><code>short</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_USHORT</code></td>
|
|
<td><code>unsigned short</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_INT</code></td>
|
|
<td><code>int</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_UINT</code></td>
|
|
<td><code>unsigned</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_LONG</code></td>
|
|
<td><code>long</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_ULONG</code></td>
|
|
<td><code>unsigned long</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_LLONG</code></td>
|
|
<td><code>long long</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_ULLONG</code></td>
|
|
<td><code>unsigned long long</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_FLOAT</code></td>
|
|
<td><code>float</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_DOUBLE</code></td>
|
|
<td><code>double</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_LDOUBLE</code></td>
|
|
<td><code>long double</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_HSIZE</code></td>
|
|
<td><code>hsize_t</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_HSSIZE</code></td>
|
|
<td><code>hssize_t</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_HERR</code></td>
|
|
<td><code>herr_t</code></td>
|
|
</tr>
|
|
<tr>
|
|
<td><code>H5T_NATIVE_HBOOL</code></td>
|
|
<td><code>hbool_t</code></td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: A 128-bit
|
|
integer</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>To create a 128-bit, little-endian signed integer
|
|
type one could use the following (increasing the
|
|
precision of a type automatically increases the total
|
|
size):
|
|
|
|
<p><code><pre>
|
|
hid_t new_type = H5Tcopy (H5T_NATIVE_INT);
|
|
H5Tset_precision (new_type, 128);
|
|
H5Tset_order (new_type, H5T_ORDER_LE);
|
|
</pre></code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: An 80-character
|
|
string</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>To create an 80-byte null terminated string type one
|
|
might do this (the offset of a character string is
|
|
always zero and the precision is adjusted
|
|
automatically to match the size):
|
|
|
|
<p><code><pre>
|
|
hid_t str80 = H5Tcopy (H5T_C_S1);
|
|
H5Tset_size (str80, 80);
|
|
</pre></code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
|
|
|
|
<h2>7. Defining Compound Datatypes</h2>
|
|
|
|
<p>Unlike atomic datatypes which are derived from other atomic
|
|
datatypes, compound datatypes are created from scratch. First,
|
|
one creates an empty compound datatype and specifies it's total
|
|
size. Then members are added to the compound datatype in any
|
|
order.
|
|
|
|
<p>Usually a C struct will be defined to hold a data point in
|
|
memory, and the offsets of the members in memory will be the
|
|
offsets of the struct members from the beginning of an instance
|
|
of the struct.
|
|
|
|
<dl>
|
|
<dt><code>HOFFSET(s,m)</code>
|
|
<dd>This macro computes the offset of member <em>m</em> within
|
|
a struct <em>s</em>.
|
|
<dt><code>offsetof(s,m)</code>
|
|
<dd>This macro defined in <code>stddef.h</code> does
|
|
exactly the same thing as the <code>HOFFSET()</code> macro.
|
|
</dl>
|
|
|
|
<p>Each member must have a descriptive name which is the
|
|
key used to uniquely identify the member within the compound
|
|
datatype. A member name in an HDF5 datatype does not
|
|
necessarily have to be the same as the name of the member in the
|
|
C struct, although this is often the case. Nor does one need to
|
|
define all members of the C struct in the HDF5 compound
|
|
datatype (or vice versa).
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: A simple struct</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>An HDF5 datatype is created to describe complex
|
|
numbers whose type is defined by the
|
|
<code>complex_t</code> struct.
|
|
|
|
<p><code><pre>
|
|
typedef struct {
|
|
double re; /*real part*/
|
|
double im; /*imaginary part*/
|
|
} complex_t;
|
|
|
|
hid_t complex_id = H5Tcreate (H5T_COMPOUND, sizeof tmp);
|
|
H5Tinsert (complex_id, "real", HOFFSET(complex_t,re),
|
|
H5T_NATIVE_DOUBLE);
|
|
H5Tinsert (complex_id, "imaginary", HOFFSET(complex_t,im),
|
|
H5T_NATIVE_DOUBLE);
|
|
</pre></code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>Member alignment is handled by the <code>HOFFSET</code>
|
|
macro. However, data stored on disk does not require alignment,
|
|
so unaligned versions of compound data structures can be created
|
|
to improve space efficiency on disk. These unaligned compound
|
|
datatypes can be created by computing offsets by hand to
|
|
eliminate inter-member padding, or the members can be packed by
|
|
calling <code>H5Tpack()</code> (which modifies a datatype
|
|
directly, so it is usually preceded by a call to
|
|
<code>H5Tcopy()</code>):
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: A packed struct</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>This example shows how to create a disk version of a
|
|
compound datatype in order to store data on disk in
|
|
as compact a form as possible. Packed compound
|
|
datatypes should generally not be used to describe memory
|
|
as they may violate alignment constraints for the
|
|
architecture being used. Note also that using a
|
|
packed datatype for disk storage may involve a higher
|
|
data conversion cost.
|
|
<p><code><pre>
|
|
hid_t complex_disk_id = H5Tcopy (complex_id);
|
|
H5Tpack (complex_disk_id);
|
|
</pre></code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: A flattened struct</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>Compound datatypes that have a compound datatype
|
|
member can be handled two ways. This example shows
|
|
that the compound datatype can be flattened,
|
|
resulting in a compound type with only atomic
|
|
members.
|
|
|
|
<p><code><pre>
|
|
typedef struct {
|
|
complex_t x;
|
|
complex_t y;
|
|
} surf_t;
|
|
|
|
hid_t surf_id = H5Tcreate (H5T_COMPOUND, sizeof tmp);
|
|
H5Tinsert (surf_id, "x-re", HOFFSET(surf_t,x.re),
|
|
H5T_NATIVE_DOUBLE);
|
|
H5Tinsert (surf_id, "x-im", HOFFSET(surf_t,x.im),
|
|
H5T_NATIVE_DOUBLE);
|
|
H5Tinsert (surf_id, "y-re", HOFFSET(surf_t,y.re),
|
|
H5T_NATIVE_DOUBLE);
|
|
H5Tinsert (surf_id, "y-im", HOFFSET(surf_t,y.im),
|
|
H5T_NATIVE_DOUBLE);
|
|
</code></pre>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: A nested struct</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>However, when the <code>complex_t</code> is used
|
|
often it becomes inconvenient to list its members over
|
|
and over again. So the alternative approach to
|
|
flattening is to define a compound datatype and then
|
|
use it as the type of the compound members, as is done
|
|
here (the typedefs are defined in the previous
|
|
examples).
|
|
|
|
<p><code><pre>
|
|
hid_t complex_id, surf_id; /*hdf5 datatypes*/
|
|
|
|
complex_id = H5Tcreate (H5T_COMPOUND, sizeof c);
|
|
H5Tinsert (complex_id, "re", HOFFSET(complex_t,re),
|
|
H5T_NATIVE_DOUBLE);
|
|
H5Tinsert (complex_id, "im", HOFFSET(complex_t,im),
|
|
H5T_NATIVE_DOUBLE);
|
|
|
|
surf_id = H5Tcreate (H5T_COMPOUND, sizeof s);
|
|
H5Tinsert (surf_id, "x", HOFFSET(surf_t,x), complex_id);
|
|
H5Tinsert (surf_id, "y", HOFFSET(surf_t,y), complex_id);
|
|
</code></pre>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
|
|
|
|
<a name="Datatypes_Enum"> </a>
|
|
<h2>8. Enumeration Datatypes</h2>
|
|
|
|
<h3>8.1. Introduction</h3>
|
|
|
|
<p>An HDF enumeration datatype is a 1:1 mapping between a set of
|
|
symbols and a set of integer values, and an order is imposed on
|
|
the symbols by their integer values. The symbols are passed
|
|
between the application and library as character strings and all
|
|
the values for a particular enumeration type are of the same
|
|
integer type, which is not necessarily a native type.
|
|
|
|
<h3>8.2. Creation</h3>
|
|
|
|
<p>Creation of an enumeration datatype resembles creation of a
|
|
compound datatype: first an empty enumeration type is created,
|
|
then members are added to the type, then the type is optionally
|
|
locked.
|
|
|
|
<dl>
|
|
<dt><code>hid_t H5Tcreate(H5T_class_t <em>type_class</em>,
|
|
size_t <em>size</em>)</code>
|
|
<dd>This function creates a new empty enumeration datatype based
|
|
on a native signed integer type. The first argument is the
|
|
constant <code>H5T_ENUM</code> and the second argument is the
|
|
size in bytes of the native integer on which the enumeration
|
|
type is based. If the architecture does not support a native
|
|
signed integer of the specified size then an error is
|
|
returned.
|
|
|
|
<pre>
|
|
/* Based on a native signed short */
|
|
hid_t hdf_en_colors = H5Tcreate(H5T_ENUM, sizeof(short));</pre>
|
|
|
|
|
|
<dt><code>hid_t H5Tenum_create(hid_t <em>base</em>)</code>
|
|
<dd>This function creates a new empty enumeration datatype based
|
|
on some integer datatype <em>base</em> and is a
|
|
generalization of the <code>H5Tcreate()</code> function. This
|
|
function is useful when creating an enumeration type based on
|
|
some non-native integer datatype, but it can be used for
|
|
native types as well.
|
|
|
|
<pre>
|
|
/* Based on a native unsigned short */
|
|
hid_t hdf_en_colors_1 = H5Tenum_create(H5T_NATIVE_USHORT);
|
|
|
|
/* Based on a MIPS 16-bit unsigned integer */
|
|
hid_t hdf_en_colors_2 = H5Tenum_create(H5T_MIPS_UINT16);
|
|
|
|
/* Based on a big-endian 16-bit unsigned integer */
|
|
hid_t hdf_en_colors_3 = H5Tenum_create(H5T_STD_U16BE);</pre>
|
|
|
|
|
|
<dt><code>herr_t H5Tenum_insert(hid_t <em>etype</em>, const char
|
|
*<em>symbol</em>, void *<em>value</em>)</code>
|
|
<dd>Members are inserted into the enumeration datatype
|
|
<em>etype</em> with this function. Each member has a symbolic
|
|
name <em>symbol</em> and some integer representation
|
|
<em>value</em>. The <em>value</em> argument must point to a value
|
|
of the same datatype as specified when the enumeration type
|
|
was created. The order of member insertion is not important
|
|
but all symbol names and values must be unique within a
|
|
particular enumeration type.
|
|
|
|
<pre>
|
|
short val;
|
|
H5Tenum_insert(hdf_en_colors, "RED", (val=0,&val));
|
|
H5Tenum_insert(hdf_en_colors, "GREEN", (val=1,&val));
|
|
H5Tenum_insert(hdf_en_colors, "BLUE", (val=2,&val));
|
|
H5Tenum_insert(hdf_en_colors, "WHITE", (val=3,&val));
|
|
H5Tenum_insert(hdf_en_colors, "BLACK", (val=4,&val));</pre>
|
|
|
|
|
|
<dt><code>herr_t H5Tlock(hid_t <em>etype</em>)</code>
|
|
<dd>This function locks a datatype so it cannot be modified or
|
|
freed unless the entire HDF5 library is closed. Its use is
|
|
completely optional but using it on an application datatype
|
|
makes that datatype act like a predefined datatype.
|
|
|
|
<pre>
|
|
H5Tlock(hdf_en_colors);</pre>
|
|
|
|
</dl>
|
|
|
|
<h3>8.3. Integer Operations</h3>
|
|
|
|
<p>Because an enumeration datatype is derived from an integer
|
|
datatype, any operation which can be performed on integer
|
|
datatypes can also be performed on enumeration datatypes. This
|
|
includes:
|
|
|
|
<p>
|
|
<center>
|
|
<table>
|
|
<tr>
|
|
<td><code>H5Topen()</code></td>
|
|
<td><code>H5Tcreate()</code></td>
|
|
<td><code>H5Tcopy()</code></td>
|
|
<td><code>H5Tclose()</code></td>
|
|
</tr><tr>
|
|
<td><code>H5Tequal()</code></td>
|
|
<td><code>H5Tlock()</code></td>
|
|
<td><code>H5Tcommit()</code></td>
|
|
<td><code>H5Tcommitted()</code></td>
|
|
</tr><tr>
|
|
<td><code>H5Tget_class()</code></td>
|
|
<td><code>H5Tget_size()</code></td>
|
|
<td><code>H5Tget_order()</code></td>
|
|
<td><code>H5Tget_pad()</code></td>
|
|
</tr><tr>
|
|
<td><code>H5Tget_precision()</code></td>
|
|
<td><code>H5Tget_offset()</code></td>
|
|
<td><code>H5Tget_sign()</code></td>
|
|
<td><code>H5Tset_size()</code></td>
|
|
</tr><tr>
|
|
<td><code>H5Tset_order()</code></td>
|
|
<td><code>H5Tset_precision()</code></td>
|
|
<td><code>H5Tset_offset()</code></td>
|
|
<td><code>H5Tset_pad()</code></td>
|
|
</tr><tr>
|
|
<td><code>H5Tset_sign()</code></td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>In addition, the new function <code>H5Tget_super()</code> will
|
|
be defined for all datatypes that are derived from existing
|
|
types (currently just enumeration types).
|
|
|
|
<dl>
|
|
<dt><code>hid_t H5Tget_super(hid_t <em>type</em>)</code>
|
|
<dd>Return the datatype from which <em>type</em> is
|
|
derived. When <em>type</em> is an enumeration datatype then
|
|
the returned value will be an integer datatype but not
|
|
necessarily a native type. One use of this function would be
|
|
to create a new enumeration type based on the same underlying
|
|
integer type and values but with possibly different symbols.
|
|
|
|
<pre>
|
|
hid_t itype = H5Tget_super(hdf_en_colors);
|
|
hid_t hdf_fr_colors = H5Tenum_create(itype);
|
|
H5Tclose(itype);
|
|
|
|
short val;
|
|
H5Tenum_insert(hdf_fr_colors, "ouge", (val=0,&val));
|
|
H5Tenum_insert(hdf_fr_colors, "vert", (val=1,&val));
|
|
H5Tenum_insert(hdf_fr_colors, "bleu", (val=2,&val));
|
|
H5Tenum_insert(hdf_fr_colors, "blanc", (val=3,&val));
|
|
H5Tenum_insert(hdf_fr_colors, "noir", (val=4,&val));
|
|
H5Tlock(hdf_fr_colors);</pre>
|
|
</dl>
|
|
|
|
<h3>8.4. Type Functions</h3>
|
|
|
|
<p>A small set of functions is available for querying properties
|
|
of an enumeration type. These functions are likely to be used
|
|
by browsers to display datatype information.
|
|
|
|
<dl>
|
|
<dt><code>int H5Tget_nmembers(hid_t <em>etype</em>)</code>
|
|
<dd>When given an enumeration datatype <em>etype</em> this
|
|
function returns the number of members defined for that
|
|
type. This function is already implemented for compound
|
|
datatypes.
|
|
|
|
<br><br>
|
|
<dt><code>char *H5Tget_member_name(hid_t <em>etype</em>, int
|
|
<em>membno</em>)</code>
|
|
<dd>Given an enumeration datatype <em>etype</em> this function
|
|
returns the symbol name for the member indexed by
|
|
<em>membno</em>. Members are numbered from zero to
|
|
<em>N</em>-1 where <em>N</em> is the return value from
|
|
<code>H5Tget_nmembers()</code>. The members are stored in no
|
|
particular order. This function is already implemented for
|
|
compound datatypes. If an error occurs then the null pointer
|
|
is returned. The return value should be freed by calling
|
|
<code>free()</code>.
|
|
|
|
<br><br>
|
|
<dt><code>herr_t H5Tget_member_value(hid_t <em>etype</em>, int
|
|
<em>membno</em>, void *<em>value</em>/*out*/)</code>
|
|
<dd>Given an enumeration datatype <em>etype</em> this function
|
|
returns the value associated with the member indexed by
|
|
<em>membno</em> (as described for
|
|
<code>H5Tget_member_name()</code>). The value returned
|
|
is in the domain of the underlying integer
|
|
datatype which is often a native integer type. The
|
|
application should ensure that the memory pointed to by
|
|
<em>value</em> is large enough to contain the result (the size
|
|
can be obtained by calling <code>H5Tget_size()</code> on
|
|
either the enumeration type or the underlying integer type
|
|
when the type is not known by the C compiler.
|
|
|
|
<pre>
|
|
int i, n = H5Tget_nmembers(hdf_en_colors);
|
|
for (i=0; i<n; i++) {
|
|
char *symbol = H5Tget_member_name(hdf_en_colors, i);
|
|
short val;
|
|
H5Tget_member_value(hdf_en_colors, i, &val);
|
|
printf("#%d %20s = %d\n", i, symbol, val);
|
|
free(symbol);
|
|
}</pre>
|
|
|
|
<p>
|
|
Output:
|
|
<pre>
|
|
#0 BLACK = 4
|
|
#1 BLUE = 2
|
|
#2 GREEN = 1
|
|
#3 RED = 0
|
|
#4 WHITE = 3</pre>
|
|
</dl>
|
|
|
|
<h3>8.5. Data Functions</h3>
|
|
|
|
<p>In addition to querying about the enumeration type properties,
|
|
an application may want to make queries about enumerated
|
|
data. These functions perform efficient mappings between symbol
|
|
names and values.
|
|
|
|
<dl>
|
|
<dt><code>herr_t H5Tenum_valueof(hid_t <em>etype</em>, const char
|
|
*<em>symbol</em>, void *<em>value</em>/*out*/)</code>
|
|
<dd>Given an enumeration datatype <em>etype</em> this function
|
|
returns through <em>value</em> the bit pattern associated with
|
|
the symbol name <em>symbol</em>. The <em>value</em> argument
|
|
should point to memory which is large enough to hold the result,
|
|
which is returned as the underlying integer datatype specified
|
|
when the enumeration type was created, often a native integer
|
|
type.
|
|
|
|
<br><br>
|
|
<dt><code>herr_t H5Tenum_nameof(hid_t <em>etype</em>, void
|
|
*<em>value</em>, char *<em>symbol</em>, size_t
|
|
<em>size</em>)</code>
|
|
<dd>This function translates a bit pattern pointed to by
|
|
<em>value</em> to a symbol name according to the mapping
|
|
defined in the enumeration datatype <em>etype</em> and stores
|
|
at most <em>size</em> characters of that name (counting the
|
|
null terminator) to the <em>symbol</em> buffer. If the name is
|
|
longer than the result buffer then the result is not null
|
|
terminated and the function returns failure. If <em>value</em>
|
|
points to a bit pattern which is not in the domain of the
|
|
enumeration type then the first byte of the <em>symbol</em>
|
|
buffer is set to zero and the function fails.
|
|
|
|
<pre>
|
|
short data[1000] = {4, 2, 0, 0, 5, 1, ...};
|
|
int i;
|
|
char symbol[32];
|
|
|
|
for (i=0; i<1000; i++) {
|
|
if (H5Tenum_nameof(hdf_en_colors, data+i, symbol,
|
|
sizeof symbol))<0) {
|
|
if (symbol[0]) {
|
|
strcpy(symbol+sizeof(symbol)-4, "...");
|
|
} else {
|
|
strcpy(symbol, "UNKNOWN");
|
|
}
|
|
}
|
|
printf("%d %s\n", data[i], symbol);
|
|
}
|
|
printf("}\n");</pre>
|
|
|
|
<p>
|
|
Output:
|
|
<pre>
|
|
4 BLACK
|
|
2 BLUE
|
|
0 RED
|
|
0 RED
|
|
5 UNKNOWN
|
|
1 GREEN
|
|
...</pre>
|
|
</dl>
|
|
|
|
<h3>8.6. Conversion</h3>
|
|
|
|
<p>Enumerated data can be converted from one type to another
|
|
provided the destination enumeration type contains all the
|
|
symbols of the source enumeration type. The conversion operates
|
|
by matching up the symbol names of the source and destination
|
|
enumeration types to build a mapping from source value to
|
|
destination value. For instance, if we are translating from an
|
|
enumeration type that defines a sequence of integers as the
|
|
values for the colors to a type that defines a different bit for
|
|
each color then the mapping might look like this:
|
|
|
|
<p><img src="EnumMap.gif" alt="Enumeration Mapping">
|
|
|
|
<p>That is, a source value of <code>2</code> which corresponds to
|
|
<code>BLUE</code> would be mapped to <code>0x0004</code>. The
|
|
following code snippet builds the second datatype, then
|
|
converts a raw data array from one datatype to another, and
|
|
then prints the result.
|
|
|
|
<pre>
|
|
/* Create a new enumeration type */
|
|
short val;
|
|
hid_t bits = H5Tcreate(H5T_ENUM, sizeof val);
|
|
H5Tenum_insert(bits, "RED", (val=0x0001,&val));
|
|
H5Tenum_insert(bits, "GREEN", (val=0x0002,&val));
|
|
H5Tenum_insert(bits, "BLUE", (val=0x0004,&val));
|
|
H5Tenum_insert(bits, "WHITE", (val=0x0008,&val));
|
|
H5Tenum_insert(bits, "BLACK", (val=0x0010,&val));
|
|
|
|
/* The data */
|
|
short data[6] = {1, 4, 2, 0, 3, 5};
|
|
|
|
/* Convert the data from one type to another */
|
|
H5Tconvert(hdf_en_colors, bits, 5, data, NULL, plist_id);
|
|
|
|
/* Print the data */
|
|
for (i=0; i<6; i++) {
|
|
printf("0x%04x\n", (unsigned)(data[i]));
|
|
}</pre>
|
|
|
|
<p>
|
|
Output:
|
|
<pre>
|
|
|
|
0x0002
|
|
0x0010
|
|
0x0004
|
|
0x0001
|
|
0x0008
|
|
0xffff</pre>
|
|
|
|
<p>If the source data stream contains values which are not in the
|
|
domain of the conversion map then an overflow exception is
|
|
raised within the library, causing the application defined
|
|
overflow handler to be invoked (see
|
|
<code>H5Tset_overflow()</code>). If no overflow handler is
|
|
defined then all bits of the destination value will be set.
|
|
|
|
<p>The HDF library will not provide conversions between enumerated
|
|
data and integers although the application is free to do so
|
|
(this is a policy we apply to all classes of HDF datatypes).
|
|
However, since enumeration types are derived from
|
|
integer types it is permissible to treat enumerated data as
|
|
integers and perform integer conversions in that context.
|
|
|
|
<h3>8.7. Symbol Order</h3>
|
|
|
|
<p>Symbol order is determined by the integer values associated
|
|
with each symbol. When the integer datatype is a native type,
|
|
testing the relative order of two symbols is an easy process:
|
|
simply compare the values of the symbols. If only the symbol
|
|
names are available then the values must first be determined by
|
|
calling <code>H5Tenum_valueof()</code>.
|
|
|
|
<pre>
|
|
short val1, val2;
|
|
H5Tenum_valueof(hdf_en_colors, "WHITE", &val1);
|
|
H5Tenum_valueof(hdf_en_colors, "BLACK", &val2);
|
|
if (val1 < val2) ...</pre>
|
|
|
|
<p>When the underlying integer datatype is not a native type then
|
|
the easiest way to compare symbols is to first create a similar
|
|
enumeration type that contains all the same symbols but has a
|
|
native integer type (HDF type conversion features can be used to
|
|
convert the non-native values to native values). Once we have a
|
|
native type we can compare symbol order as just described. If
|
|
<code>foreign</code> is some non-native enumeration type then a
|
|
native type can be created as follows:
|
|
|
|
<pre>
|
|
int n = H5Tget_nmembers(foreign);
|
|
hid_t itype = H5Tget_super(foreign);
|
|
void *val = malloc(n * MAX(H5Tget_size(itype), sizeof(int)));
|
|
char *name = malloc(n * sizeof(char*));
|
|
int i;
|
|
|
|
/* Get foreign type information */
|
|
for (i=0; i<n; i++) {
|
|
name[i] = H5Tget_member_name(foreign, i);
|
|
H5Tget_member_value(foreign, i,
|
|
(char*)val+i*H5Tget_size(foreign));
|
|
}
|
|
|
|
/* Convert integer values to new type */
|
|
H5Tconvert(itype, H5T_NATIVE_INT, n, val, NULL, plist_id);
|
|
|
|
/* Build a native type */
|
|
hid_t native = H5Tenum_create(H5T_NATIVE_INT);
|
|
for (i=0; i<n; i++) {
|
|
H5Tenum_insert(native, name[i], ((int*)val)[i]);
|
|
free(name[i]);
|
|
}
|
|
free(name);
|
|
free(val);</pre>
|
|
|
|
<p>It is also possible to convert enumerated data to a new type
|
|
that has a different order defined for the symbols. For
|
|
instance, we can define a new type, <code>reverse</code> that
|
|
defines the same five colors but in the reverse order.
|
|
|
|
<pre>
|
|
short val;
|
|
int i;
|
|
char sym[8];
|
|
short data[5] = {0, 1, 2, 3, 4};
|
|
|
|
hid_t reverse = H5Tenum_create(H5T_NATIVE_SHORT);
|
|
H5Tenum_insert(reverse, "BLACK", (val=0,&val));
|
|
H5Tenum_insert(reverse, "WHITE", (val=1,&val));
|
|
H5Tenum_insert(reverse, "BLUE", (val=2,&val));
|
|
H5Tenum_insert(reverse, "GREEN", (val=3,&val));
|
|
H5Tenum_insert(reverse, "RED", (val=4,&val));
|
|
|
|
/* Print data */
|
|
for (i=0; i<5; i++) {
|
|
H5Tenum_nameof(hdf_en_colors, data+i, sym, sizeof sym);
|
|
printf ("%d %s\n", data[i], sym);
|
|
}
|
|
|
|
puts("Converting...");
|
|
H5Tconvert(hdf_en_colors, reverse, 5, data, NULL, plist_id);
|
|
|
|
/* Print data */
|
|
for (i=0; i<5; i++) {
|
|
H5Tenum_nameof(reverse, data+i, sym, sizeof sym);
|
|
printf ("%d %s\n", data[i], sym);
|
|
}</pre>
|
|
|
|
<p>
|
|
Output:
|
|
<pre>
|
|
0 RED
|
|
1 GREEN
|
|
2 BLUE
|
|
3 WHITE
|
|
4 BLACK
|
|
Converting...
|
|
4 RED
|
|
3 GREEN
|
|
2 BLUE
|
|
1 WHITE
|
|
0 BLACK</pre>
|
|
|
|
<h3>8.8. Equality</h3>
|
|
|
|
<p>The order that members are inserted into an enumeration type is
|
|
unimportant; the important part is the associations between the
|
|
symbol names and the values. Thus, two enumeration datatypes
|
|
will be considered equal if and only if both types have the same
|
|
symbol/value associations and both have equal underlying integer
|
|
datatypes. Type equality is tested with the
|
|
<code>H5Tequal()</code> function.
|
|
|
|
<h3>8.9. Interacting with C's <code>enum</code> Type</h3>
|
|
|
|
<p>Although HDF enumeration datatypes are similar to C
|
|
<code>enum</code> datatypes, there are some important
|
|
differences:
|
|
|
|
<p>
|
|
<center>
|
|
<table border width="80%">
|
|
<tr>
|
|
<th>Difference</th>
|
|
<th>Motivation/Implications</th>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top>Symbols are unquoted in C but quoted in
|
|
HDF.</td>
|
|
<td valign=top>This allows the application to manipulate
|
|
symbol names in ways that are not possible with C.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top>The C compiler automatically replaces all
|
|
symbols with their integer values but HDF requires
|
|
explicit calls to do the same.</td>
|
|
<td valign=top>C resolves symbols at compile time while
|
|
HDF resolves symbols at run time.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top>The mapping from symbols to integers is
|
|
<em>N</em>:1 in C but 1:1 in HDF.</td>
|
|
<td valign=top>HDF can translate from value to name
|
|
uniquely and large <code>switch</code> statements are
|
|
not necessary to print values in human-readable
|
|
format.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top>A symbol must appear in only one C
|
|
<code>enum</code> type but may appear in multiple HDF
|
|
enumeration types.</td>
|
|
<td valign=top>The translation from symbol to value in HDF
|
|
requires the datatype to be specified while in C the
|
|
datatype is not necessary because it can be inferred
|
|
from the symbol.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top>The underlying integer value is always a
|
|
native integer in C but can be a foreign integer type in
|
|
HDF.</td>
|
|
<td valign=top>This allows HDF to describe data that might
|
|
reside on a foreign architecture, such as data stored in
|
|
a file.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top>The sign and size of the underlying integer
|
|
datatype is chosen automatically by the C compiler but
|
|
must be fully specified with HDF.</td>
|
|
<td valign=top>Since HDF doesn't require finalization of a
|
|
datatype, complete specification of the type must be
|
|
supplied before the type is used. Requiring that
|
|
information at the time of type creation was a design
|
|
decision to simplify the library.</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>The examples below use the following C datatypes:
|
|
|
|
<p>
|
|
<table width="90%" bgcolor="white">
|
|
<tr>
|
|
<td>
|
|
<code><pre>
|
|
/* English color names */
|
|
typedef enum {
|
|
RED,
|
|
GREEN,
|
|
BLUE,
|
|
WHITE,
|
|
BLACK
|
|
} c_en_colors;
|
|
|
|
/* Spanish color names, reverse order */
|
|
typedef enum {
|
|
NEGRO
|
|
BLANCO,
|
|
AZUL,
|
|
VERDE,
|
|
ROJO,
|
|
} c_sp_colors;
|
|
|
|
/* No enum definition for French names */
|
|
</pre></code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h4>Creating HDF Types from C Types</h4>
|
|
|
|
<p>An HDF enumeration datatype can be created from a C
|
|
<code>enum</code> type simply by passing pointers to the C
|
|
<code>enum</code> values to <code>H5Tenum_insert()</code>. For
|
|
instance, to create HDF types for the <code>c_en_colors</code>
|
|
type shown above:
|
|
|
|
<p>
|
|
<table width="90%" bgcolor="white">
|
|
<tr>
|
|
<td>
|
|
<code><pre>
|
|
|
|
c_en_colors val;
|
|
hid_t hdf_en_colors = H5Tcreate(H5T_ENUM, sizeof(c_en_colors));
|
|
H5Tenum_insert(hdf_en_colors, "RED", (val=RED, &val));
|
|
H5Tenum_insert(hdf_en_colors, "GREEN", (val=GREEN,&val));
|
|
H5Tenum_insert(hdf_en_colors, "BLUE", (val=BLUE, &val));
|
|
H5Tenum_insert(hdf_en_colors, "WHITE", (val=WHITE,&val));
|
|
H5Tenum_insert(hdf_en_colors, "BLACK", (val=BLACK,&val));</pre></code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h4>Name Changes between Applications</h4>
|
|
|
|
<p>Occassionally two applicatons wish to exchange data but they
|
|
use different names for the constants they exchange. For
|
|
instance, an English and a Spanish program may want to
|
|
communicate color names although they use different symbols in
|
|
the C <code>enum</code> definitions. The communication is still
|
|
possible although the applications must agree on common terms
|
|
for the colors. The following example shows the Spanish code to
|
|
read the values assuming that the applications have agreed that
|
|
the color information will be exchanged using Enlish color
|
|
names:
|
|
|
|
<p>
|
|
<table width="90%" bgcolor="white">
|
|
<tr>
|
|
<td>
|
|
<code><pre>
|
|
|
|
c_sp_colors val, data[1000];
|
|
hid_t hdf_sp_colors = H5Tcreate(H5T_ENUM, sizeof(c_sp_colors));
|
|
H5Tenum_insert(hdf_sp_colors, "RED", (val=ROJO, &val));
|
|
H5Tenum_insert(hdf_sp_colors, "GREEN", (val=VERDE, &val));
|
|
H5Tenum_insert(hdf_sp_colors, "BLUE", (val=AZUL, &val));
|
|
H5Tenum_insert(hdf_sp_colors, "WHITE", (val=BLANCO, &val));
|
|
H5Tenum_insert(hdf_sp_colors, "BLACK", (val=NEGRO, &val));
|
|
|
|
H5Dread(dataset, hdf_sp_colors, H5S_ALL, H5S_ALL, H5P_DEFAULT, data);</pre></code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
|
|
<h4>Symbol Ordering across Applications</h4>
|
|
|
|
<p>Since symbol ordering is completely determined by the integer values
|
|
assigned to each symbol in the <code>enum</code> definition,
|
|
ordering of <code>enum</code> symbols cannot be preserved across
|
|
files like with HDF enumeration types. HDF can convert from one
|
|
application's integer values to the other's so a symbol in one
|
|
application's C <code>enum</code> gets mapped to the same symbol
|
|
in the other application's C <code>enum</code>, but the relative
|
|
order of the symbols is not preserved.
|
|
|
|
<p>For example, an application may be defined to use the
|
|
definition of <code>c_en_colors</code> defined above where
|
|
<code>WHITE</code> is less than <code>BLACK</code>, but some
|
|
other application might define the colors in some other
|
|
order. If each application defines an HDF enumeration type based
|
|
on that application's C <code>enum</code> type then HDF will
|
|
modify the integer values as data is communicated from one
|
|
application to the other so that a <code>RED</code> value
|
|
in the first application is also a <code>RED</code> value in the
|
|
other application.
|
|
|
|
<p>A case of this reordering of symbol names was also shown in the
|
|
previous code snippet (as well as a change of language), where
|
|
HDF changed the integer values so 0 (<code>RED</code>) in the
|
|
input file became 4 (<code>ROJO</code>) in the <code>data</code>
|
|
array. In the input file, <code>WHITE</code> was less than
|
|
<code>BLACK</code>; in the application the opposite is true.
|
|
|
|
<p>In fact, the ability to change the order of symbols is often
|
|
convenient when the enumeration type is used only to group
|
|
related symbols that don't have any well defined order
|
|
relationship.
|
|
|
|
<h4>Internationalization</h4>
|
|
|
|
<p>The HDF enumeration type conversion features can also be used
|
|
to provide internationalization of debugging output. A program
|
|
written with the <code>c_en_colors</code> datatype could define
|
|
a separate HDF datatype for languages such as English, Spanish,
|
|
and French and cast the enumerated value to one of these HDF
|
|
types to print the result.
|
|
|
|
<p>
|
|
<table width="90%" bgcolor="white">
|
|
<tr>
|
|
<td>
|
|
<code><pre>
|
|
|
|
c_en_colors val, *data=...;
|
|
|
|
hid_t hdf_sp_colors = H5Tcreate(H5T_ENUM, sizeof val);
|
|
H5Tenum_insert(hdf_sp_colors, "ROJO", (val=RED, &val));
|
|
H5Tenum_insert(hdf_sp_colors, "VERDE", (val=GREEN, &val));
|
|
H5Tenum_insert(hdf_sp_colors, "AZUL", (val=BLUE, &val));
|
|
H5Tenum_insert(hdf_sp_colors, "BLANCO", (val=WHITE, &val));
|
|
H5Tenum_insert(hdf_sp_colors, "NEGRO", (val=BLACK, &val));
|
|
|
|
hid_t hdf_fr_colors = H5Tcreate(H5T_ENUM, sizeof val);
|
|
H5Tenum_insert(hdf_fr_colors, "OUGE", (val=RED, &val));
|
|
H5Tenum_insert(hdf_fr_colors, "VERT", (val=GREEN, &val));
|
|
H5Tenum_insert(hdf_fr_colors, "BLEU", (val=BLUE, &val));
|
|
H5Tenum_insert(hdf_fr_colors, "BLANC", (val=WHITE, &val));
|
|
H5Tenum_insert(hdf_fr_colors, "NOIR", (val=BLACK, &val));
|
|
|
|
void
|
|
nameof(lang_t language, c_en_colors val, char *name, size_t size)
|
|
{
|
|
switch (language) {
|
|
case ENGLISH:
|
|
H5Tenum_nameof(hdf_en_colors, &val, name, size);
|
|
break;
|
|
case SPANISH:
|
|
H5Tenum_nameof(hdf_sp_colors, &val, name, size);
|
|
break;
|
|
case FRENCH:
|
|
H5Tenum_nameof(hdf_fr_colors, &val, name, size);
|
|
break;
|
|
}
|
|
}</pre></code>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<h3>8.10. Goals That Have Been Met</h3>
|
|
|
|
<p>The main goal of enumeration types is to provide communication
|
|
of enumerated data using symbolic equivalence. That is, a
|
|
symbol written to a dataset by one application should be read as
|
|
the same symbol by some other application.
|
|
|
|
<p>
|
|
<table width="90%">
|
|
<tr>
|
|
<td valign=top><b>Architecture Independence</b></td>
|
|
<td valign=top>Two applications shall be able to exchange
|
|
enumerated data even when the underlying integer values
|
|
have different storage formats. HDF accomplishes this for
|
|
enumeration types by building them upon integer types.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top><b>Preservation of Order Relationship</b></td>
|
|
<td valign=top>The relative order of symbols shall be
|
|
preserved between two applications that use equivalent
|
|
enumeration datatypes. Unlike numeric values that have
|
|
an implicit ordering, enumerated data has an explicit
|
|
order defined by the enumeration datatype and HDF
|
|
records this order in the file.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top><b>Order Independence</b></td>
|
|
<td valign=top>An application shall be able to change the
|
|
relative ordering of the symbols in an enumeration
|
|
datatype. This is accomplished by defining a new type with
|
|
different integer values and converting data from one type
|
|
to the other.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top><b>Subsets</b></td>
|
|
<td valign=top>An application shall be able to read
|
|
enumerated data from an archived dataset even after the
|
|
application has defined additional members for the
|
|
enumeration type. An application shall be able to write
|
|
to a dataset when the dataset contains a superset of the
|
|
members defined by the application. Similar rules apply
|
|
for in-core conversions between enumerated datatypes.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top><b>Targetable</b></td>
|
|
<td valign=top>An application shall be able to target a
|
|
particular architecture or application when storing
|
|
enumerated data. This is accomplished by allowing
|
|
non-native underlying integer types and converting the
|
|
native data to non-native data.</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top><b>Efficient Data Transfer</b></td>
|
|
<td valign=top>An application that defines a file dataset
|
|
that corresponds to some native C enumerated data array
|
|
shall be able to read and write to that dataset directly
|
|
using only Posix read and write functions. HDF already
|
|
optimizes this case for integers, so the same optimization
|
|
will apply to enumerated data.
|
|
</tr>
|
|
|
|
<tr>
|
|
<td valign=top><b>Efficient Storage</b></td>
|
|
<td valign=top>Enumerated data shall be stored in a manner
|
|
which is space efficient. HDF stores the enumerated data
|
|
as integers and allows the application to chose the size
|
|
and format of those integers.</td>
|
|
</tr>
|
|
</table>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<h2>9. Variable-length Datatypes</h2>
|
|
|
|
<h3>9.1. Overview And Justification</h3>
|
|
|
|
Variable-length (VL) datatypes are sequences of an existing datatype
|
|
(atomic, VL, or compound) which are not fixed in length from one dataset location
|
|
to another. In essence, they are similar to C character strings -- a sequence of
|
|
a type which is pointed to by a particular type of <em>pointer</em> -- although
|
|
they are implemented more closely to FORTRAN strings by including an explicit
|
|
length in the pointer instead of using a particular value to terminate the
|
|
sequence.
|
|
|
|
<p>
|
|
VL datatypes are useful to the scientific community in many different ways,
|
|
some of which are listed below:
|
|
<ul>
|
|
<li>Ragged arrays: Multi-dimensional ragged arrays can be implemented with
|
|
the last (fastest changing) dimension being ragged by using a
|
|
VL datatype as the type of the element stored. (Or as a field in a
|
|
compound datatype.)
|
|
<li>Fractal arrays: If a compound datatype has a VL field of another compound
|
|
type with VL fields (a <em>nested</em> VL datatype), this can be used to
|
|
implement ragged arrays of ragged arrays, to whatever nesting depth is
|
|
required for the user.
|
|
<li>Polygon lists: A common storage requirement is to efficiently store arrays
|
|
of polygons with different numbers of vertices. VL datatypes can be
|
|
used to efficiently and succinctly describe an array of polygons with
|
|
different numbers of vertices.
|
|
<li>Character strings: Perhaps the most common use of VL datatypes will be to
|
|
store C-like VL character strings in dataset elements or as attributes
|
|
of objects.
|
|
<li>Indices: An array of VL object references could be used as an index to
|
|
all the objects in a file which contain a particular sequence of
|
|
dataset values. Perhaps an array something like the following:
|
|
<pre>
|
|
Value1: Object1, Object3, Object9
|
|
Value2: Object0, Object12, Object14, Object21, Object22
|
|
Value3: Object2
|
|
Value4: <none>
|
|
Value5: Object1, Object10, Object12
|
|
.
|
|
.
|
|
</pre>
|
|
<li>Object Tracking: An array of VL dataset region references can be used as
|
|
a method of tracking objects or features appearing in a sequence of
|
|
datasets. Perhaps an array of them would look like:
|
|
<pre>
|
|
Feature1: Dataset1:Region, Dataset3:Region, Dataset9:Region
|
|
Feature2: Dataset0:Region, Dataset12:Region, Dataset14:Region,
|
|
Dataset21:Region, Dataset22:Region
|
|
Feature3: Dataset2:Region
|
|
Feature4: <none>
|
|
Feature5: Dataset1:Region, Dataset10:Region, Dataset12:Region
|
|
.
|
|
.
|
|
</pre>
|
|
</ul>
|
|
|
|
|
|
<h3>9.2. Variable-length Datatype Memory Management</h3>
|
|
|
|
With each element possibly being of different sequence lengths for a
|
|
dataset with a VL datatype, the memory for the VL datatype must be dynamically
|
|
allocated. Currently there are two methods of managing the memory for
|
|
VL datatypes: the standard C malloc/free memory allocation routines or a method
|
|
of calling user-defined memory management routines to allocate or free memory.
|
|
Since the memory allocated when reading (or writing) may be complicated to
|
|
release, an HDF5 routine is provided to traverse a memory buffer and free the
|
|
VL datatype information without leaking memory.
|
|
|
|
|
|
<h4>Variable-length datatypes cannot be divided</h4>
|
|
|
|
VL datatypes are designed so that they cannot be subdivided by the library
|
|
with selections, etc. This design was chosen due to the complexities in
|
|
specifying selections on each VL element of a dataset through a selection API
|
|
that is easy to understand. Also, the selection APIs work on dataspaces, not
|
|
on datatypes. At some point in time, we may want to create a way for
|
|
dataspaces to have VL components to them and we would need to allow selections
|
|
of those VL regions, but that is beyond the scope of this document.
|
|
|
|
|
|
<h4>What happens if the library runs out of memory while reading?</h4>
|
|
|
|
It is possible for a call to <code>H5Dread</code> to fail while reading in
|
|
VL datatype information if the memory required exceeds that which is available.
|
|
In this case, the <code>H5Dread</code> call will fail gracefully and any
|
|
VL data which has been allocated prior to the memory shortage will be returned
|
|
to the system via the memory management routines detailed below.
|
|
It may be possible to design a <em>partial read</em> API function at a
|
|
later date, if demand for such a function warrants.
|
|
|
|
|
|
<h4>Strings as variable-length datatypes</h4>
|
|
|
|
Since character strings are a special case of VL data that is implemented
|
|
in many different ways on different machines and in different programming
|
|
languages, they are handled somewhat differently from other VL datatypes in HDF5.
|
|
|
|
<p>
|
|
HDF5 has native VL strings for each language API, which are stored the
|
|
same way on disk, but are exported through each language API in a natural way
|
|
for that language. When retrieving VL strings from a dataset, users may choose
|
|
to have them stored in memory as a native VL string or in HDF5's <code>hvl_t</code>
|
|
struct for VL datatypes.
|
|
|
|
<p>
|
|
VL strings may be created in one of two ways: by creating a VL datatype with
|
|
a base type of <code>H5T_NATIVE_ASCII</code>, <code>H5T_NATIVE_UNICODE</code>,
|
|
etc., or by creating a string datatype and setting its length to
|
|
<code>H5T_STRING_VARIABLE</code>. The second method is used to access
|
|
native VL strings in memory. The library will convert between the two types,
|
|
but they are stored on disk using different datatypes and have different
|
|
memory representations.
|
|
|
|
<p>
|
|
Multi-byte character representations, such as UNICODE or <em>wide</em>
|
|
characters in C/C++, will need the appropriate character and string datatypes
|
|
created so that they can be described properly through the datatype API.
|
|
Additional conversions between these types and the current ASCII characters
|
|
will also be required.
|
|
|
|
<p>
|
|
Variable-width character strings (which might be compressed data or some
|
|
other encoding) are not currently handled by this design. We will evaluate
|
|
how to implement them based on user feedback.
|
|
|
|
|
|
<h3>9.3. Variable-length Datatype API</h3>
|
|
|
|
<h4>Creation</h4>
|
|
|
|
VL datatypes are created with the <code>H5Tvlen_create()</code> function
|
|
as follows:
|
|
<dl>
|
|
<dd><em>type_id</em> = <code>H5Tvlen_create</code>(<em>hid_t</em> <code>base_type_id</code>);
|
|
</dl>
|
|
|
|
<p>
|
|
The base datatype will be the datatype that the sequence is composed of,
|
|
characters for character strings, vertex coordinates for polygon lists, etc.
|
|
The base datatype specified for the VL datatype can be of any HDF5 datatype,
|
|
including another VL datatype, a compound datatype, or an atomic datatype.
|
|
|
|
|
|
<h4>Query base datatype of VL datatype</h4>
|
|
|
|
It may be necessary to know the base datatype of a VL datatype before
|
|
memory is allocated, etc. The base datatype is queried with the
|
|
<code>H5Tget_super()</code> function, described in the H5T documentation.
|
|
|
|
|
|
<h4>Query minimum memory required for VL information</h4>
|
|
|
|
It order to predict the memory usage that <code>H5Dread</code> may need
|
|
to allocate to store VL data while reading the data, the
|
|
<code>H5Dget_vlen_size()</code> function is provided:
|
|
<dl>
|
|
<dd><em>herr_t</em>
|
|
<code>H5Dget_vlen_buf_size</code>(<em>hid_t</em> <code>dataset_id</code>,
|
|
<em>hid_t</em> <code>type_id</code>,
|
|
<em>hid_t</em> <code>space_id</code>,
|
|
<em>hsize_t</em> *<code>size</code>)
|
|
</dl>
|
|
(This function is not implemented in Release 1.2.x)
|
|
|
|
<p>
|
|
This routine checks the number of bytes required to store the VL data from
|
|
the dataset, using the <code>space_id</code> for the selection in the dataset
|
|
on disk and the <code>type_id</code> for the memory representation of the
|
|
VL data in memory. The *<code>size</code> value is modified according to
|
|
how many bytes are required to store the VL data in memory.
|
|
|
|
|
|
<h4>Specifying how to manage memory for the VL datatype</h4>
|
|
|
|
The memory management method is determined by dataset transfer properties
|
|
passed into the <code>H5Dread</code> and <code>H5Dwrite</code> functions
|
|
with the dataset transfer property list.
|
|
|
|
<p>
|
|
Default memory management is set by using <code>H5P_DEFAULT</code>
|
|
for the dataset transfer property list identifier.
|
|
If <code>H5P_DEFAULT</code> is used with <code>H5Dread</code>,
|
|
the system <code>malloc</code> and <code>free</code> calls
|
|
will be used for allocating and freeing memory.
|
|
In such a case, <code>H5P_DEFAULT</code> should also be passed
|
|
as the property list identifier to <code>H5Dvlen_reclaim</code>.
|
|
|
|
<p>
|
|
The rest of this subsection is relevant only to those who choose
|
|
<i>not</i> to use default memory management.
|
|
|
|
<p>
|
|
The user can choose whether to use the
|
|
system <code>malloc</code> and <code>free</code> calls or
|
|
user-defined, or custom, memory management functions.
|
|
If user-defined memory management functions are to be used,
|
|
the memory allocation and free routines must be defined via
|
|
<code>H5Pset_vlen_mem_manager()</code>, as follows:
|
|
<dl>
|
|
<dd><em>herr_t</em>
|
|
<code>H5Pset_vlen_mem_manager</code>(<em>hid_t</em> <code>plist_id</code>,
|
|
<em>H5MM_allocate_t</em> <code>alloc</code>,
|
|
<em>void</em> *<code>alloc_info</code>,
|
|
<em>H5MM_free_t</em> <code>free</code>,
|
|
<em>void</em> *<code>free_info</code>)
|
|
</dl>
|
|
|
|
|
|
<p>
|
|
The <code>alloc</code> and <code>free</code> parameters
|
|
identify the memory management routines to be used.
|
|
If the user has defined custom memory management routines,
|
|
<code>alloc</code> and/or <code>free</code> should be set to make
|
|
those routine calls (i.e., the name of the routine is used as
|
|
the value of the parameter);
|
|
if the user prefers to use the system's <code> malloc</code>
|
|
and/or <code>free</code>, the <code>alloc</code> and
|
|
<code>free</code> parameters, respectively, should be set to
|
|
<code> NULL</code>
|
|
<p>
|
|
The prototypes for the user-defined functions would appear as follows:
|
|
<dl>
|
|
<dd><code>typedef</code> <em>void</em>
|
|
*(*<code>H5MM_allocate_t</code>)(<em>size_t</em> <code>size</code>,
|
|
<em>void</em> *<code>info</code>) ;
|
|
<dd><code>typedef</code> <em>void</em>
|
|
(*<code>H5MM_free_t</code>)(<em>void</em> *<code>mem</code>,
|
|
<em>void</em> *<code>free_info</code>) ;
|
|
</dl>
|
|
|
|
<p>
|
|
The <code>alloc_info</code> and <code>free_info</code> parameters can be
|
|
used to pass along any required information to the user's memory management
|
|
routines.
|
|
|
|
<p>
|
|
In summary, if the user has defined custom memory management
|
|
routines, the name(s) of the routines are passed in the
|
|
<code>alloc</code> and <code>free</code> parameters and the
|
|
custom routines' parameters are passed in the
|
|
<code>alloc_info</code> and <code>free_info</code> parameters.
|
|
If the user wishes to use the system <code> malloc</code> and
|
|
<code>free</code> functions, the <code>alloc</code> and/or
|
|
<code>free</code> parameters are set to <code> NULL</code>
|
|
and the <code>alloc_info</code> and <code>free_info</code>
|
|
parameters are ignored.
|
|
|
|
<h4>Recovering memory from VL buffers read in</h4>
|
|
|
|
The complex memory buffers created for a VL datatype may be reclaimed with
|
|
the <code>H5Dvlen_reclaim()</code> function call, as follows:
|
|
<dl>
|
|
<dd><em>herr_t</em>
|
|
<code>H5Dvlen_reclaim</code>(<em>hid_t</em> <code>type_id</code>,
|
|
<em>hid_t</em> <code>space_id</code>,
|
|
<em>hid_t</em> <code>plist_id</code>,
|
|
<em>void</em> *<code>buf</code>);
|
|
</dl>
|
|
|
|
<p>
|
|
The <code>type_id</code> must be the datatype stored in the buffer,
|
|
<code>space_id</code> describes the selection for the memory buffer
|
|
to free the VL datatypes within,
|
|
<code>plist_id</code> is the dataset transfer property list which
|
|
was used for the I/O transfer to create the buffer, and
|
|
<code>buf</code> is the pointer to the buffer to free the VL memory within.
|
|
The VL structures (<code>hvl_t</code>) in the user's buffer are
|
|
modified to zero out the VL information after it has been freed.
|
|
|
|
<p>
|
|
If nested VL datatypes were used to create the buffer,
|
|
this routine frees them from the bottom up,
|
|
releasing all the memory without creating memory leaks.
|
|
|
|
|
|
<h3>9.4. Code Examples</h3>
|
|
|
|
The following example creates the following one-dimensional array
|
|
of size 4 of variable-length datatype.
|
|
<pre>
|
|
0 10 20 30
|
|
11 21 31
|
|
22 32
|
|
33
|
|
</pre>
|
|
Each element of the VL datatype is of H5T_NATIVE_UINT type.
|
|
<p>
|
|
The array is stored in the dataset and then read back into memory.
|
|
Default memory management routines are used for writing the VL data.
|
|
Custom memory management routines are used for reading the VL data and
|
|
reclaiming memory space.
|
|
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: Variable-length Datatypes</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<pre>
|
|
#include <hdf5.h>
|
|
|
|
#define FILE "tvltypes.h5"
|
|
#define MAX(X,Y) ((X)>(Y)?(X):(Y))
|
|
|
|
/* 1-D dataset with fixed dimensions */
|
|
#define SPACE1_NAME "Space1"
|
|
#define SPACE1_RANK 1
|
|
#define SPACE1_DIM1 4
|
|
|
|
void *vltypes_alloc_custom(size_t size, void *info);
|
|
void vltypes_free_custom(void *mem, void *info);
|
|
|
|
/****************************************************************
|
|
**
|
|
** vltypes_alloc_custom(): VL datatype custom memory
|
|
** allocation routine. This routine just uses malloc to
|
|
** allocate the memory and increments the amount of memory
|
|
** allocated.
|
|
**
|
|
****************************************************************/
|
|
void *vltypes_alloc_custom(size_t size, void *info)
|
|
{
|
|
void *ret_value=NULL; /* Pointer to return */
|
|
int *mem_used=(int *)info; /* Get the pointer to the memory used */
|
|
size_t extra; /* Extra space needed */
|
|
|
|
/*
|
|
* This weird contortion is required on the DEC Alpha to keep the
|
|
* alignment correct.
|
|
*/
|
|
extra=MAX(sizeof(void *),sizeof(int));
|
|
|
|
if((ret_value=malloc(extra+size))!=NULL) {
|
|
*(int *)ret_value=size;
|
|
*mem_used+=size;
|
|
} /* end if */
|
|
ret_value=((unsigned char *)ret_value)+extra;
|
|
return(ret_value);
|
|
}
|
|
|
|
/****************************************************************
|
|
**
|
|
** vltypes_free_custom(): VL datatype custom memory
|
|
** allocation routine. This routine just uses free to
|
|
** release the memory and decrements the amount of memory
|
|
** allocated.
|
|
**
|
|
****************************************************************/
|
|
void vltypes_free_custom(void *_mem, void *info)
|
|
{
|
|
unsigned char *mem;
|
|
int *mem_used=(int *)info; /* Get the pointer to the memory used */
|
|
size_t extra; /* Extra space needed */
|
|
|
|
/*
|
|
* This weird contortion is required on the DEC Alpha to keep the
|
|
* alignment correct.
|
|
*/
|
|
extra=MAX(sizeof(void *),sizeof(int));
|
|
|
|
if(_mem!=NULL) {
|
|
mem=((unsigned char *)_mem)-extra;
|
|
*mem_used-=*(int *)mem;
|
|
free(mem);
|
|
} /* end if */
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
hvl_t wdata[SPACE1_DIM1]; /* Information to write */
|
|
hvl_t rdata[SPACE1_DIM1]; /* Information read in */
|
|
hid_t fid1; /* HDF5 File IDs */
|
|
hid_t dataset; /* Dataset ID */
|
|
hid_t sid1; /* Dataspace ID */
|
|
hid_t tid1; /* Datatype ID */
|
|
hid_t xfer_pid; /* Dataset transfer property list ID */
|
|
hsize_t dims1[] = {SPACE1_DIM1};
|
|
uint i,j; /* counting variables */
|
|
int mem_used=0; /* Memory used during allocation */
|
|
herr_t ret; /* Generic return value */
|
|
|
|
|
|
/*
|
|
* Allocate and initialize VL data to write
|
|
*/
|
|
for(i=0; i<SPACE1_DIM1; i++) {
|
|
|
|
wdata[i].p=malloc((i+1)*sizeof(unsigned int));
|
|
wdata[i].len=i+1;
|
|
for(j=0; j<(i+1); j++)
|
|
((unsigned int *)wdata[i].p)[j]=i*10+j;
|
|
} /* end for */
|
|
|
|
/*
|
|
* Create file.
|
|
*/
|
|
fid1 = H5Fcreate(FILE, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
|
|
|
|
/*
|
|
* Create dataspace for datasets.
|
|
*/
|
|
sid1 = H5Screate_simple(SPACE1_RANK, dims1, NULL);
|
|
|
|
/*
|
|
* Create a datatype to refer to.
|
|
*/
|
|
tid1 = H5Tvlen_create (H5T_NATIVE_UINT);
|
|
|
|
/*
|
|
* Create a dataset.
|
|
*/
|
|
dataset=H5Dcreate(fid1,"Dataset1",tid1,sid1,H5P_DEFAULT);
|
|
|
|
/*
|
|
* Write dataset to disk.
|
|
*/
|
|
ret=H5Dwrite(dataset,tid1,H5S_ALL,H5S_ALL,H5P_DEFAULT,wdata);
|
|
|
|
/*
|
|
* Change to the custom memory allocation routines for reading VL data
|
|
*/
|
|
xfer_pid=H5Pcreate(H5P_DATASET_XFER);
|
|
|
|
ret=H5Pset_vlen_mem_manager(xfer_pid,vltypes_alloc_custom,
|
|
&mem_used,vltypes_free_custom,&mem_used);
|
|
|
|
/*
|
|
* Read dataset from disk. vltypes_alloc_custom and
|
|
* will be used to manage memory.
|
|
*/
|
|
ret=H5Dread(dataset,tid1,H5S_ALL,H5S_ALL,xfer_pid,rdata);
|
|
|
|
/*
|
|
* Display data read in
|
|
*/
|
|
for(i=0; i<SPACE1_DIM1; i++) {
|
|
printf("%d-th element length is %d \n", i, (unsigned) rdata[i].len);
|
|
for(j=0; j<rdata[i].len; j++) {
|
|
printf(" %d ",((unsigned int *)rdata[i].p)[j] );
|
|
}
|
|
printf("\n");
|
|
} /* end for */
|
|
|
|
/*
|
|
* Reclaim the read VL data. vltypes_free_custom will be used
|
|
* to reclaim the space.
|
|
*/
|
|
ret=H5Dvlen_reclaim(tid1,sid1,xfer_pid,rdata);
|
|
|
|
|
|
/*
|
|
* Reclaim the write VL data. C language free function will be used
|
|
* to reclaim space.
|
|
*/
|
|
ret=H5Dvlen_reclaim(tid1,sid1,H5P_DEFAULT,wdata);
|
|
|
|
/*
|
|
* Close Dataset
|
|
*/
|
|
ret = H5Dclose(dataset);
|
|
|
|
/*
|
|
* Close datatype
|
|
*/
|
|
ret = H5Tclose(tid1);
|
|
|
|
/*
|
|
* Close disk dataspace
|
|
*/
|
|
ret = H5Sclose(sid1);
|
|
|
|
/*
|
|
* Close dataset transfer property list
|
|
*/
|
|
ret = H5Pclose(xfer_pid);
|
|
|
|
/*
|
|
* Close file
|
|
*/
|
|
ret = H5Fclose(fid1);
|
|
|
|
}
|
|
</pre>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
And the output from this sample code would be as follows:
|
|
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: Variable-length Datatypes, Sample Output</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<pre>
|
|
0-th element length is 1
|
|
0
|
|
1-th element length is 2
|
|
10 11
|
|
2-th element length is 3
|
|
20 21 22
|
|
3-th element length is 4
|
|
30 31 32 33
|
|
</pre>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>
|
|
For further samples of VL datatype code, see the tests in <code>test/tvltypes.c</code>
|
|
in the HDF5 distribution.
|
|
|
|
|
|
|
|
|
|
<h2>10. Sharing Datatypes among Datasets</h2>
|
|
|
|
<p>If a file has lots of datasets which have a common datatype,
|
|
then the file could be made smaller by having all the datasets
|
|
share a single datatype. Instead of storing a copy of the
|
|
datatype in each dataset object header, a single datatype is stored
|
|
and the object headers point to it. The space savings is
|
|
probably only significant for datasets with a compound datatype,
|
|
since the atomic datatypes can be described with just a few
|
|
bytes anyway.
|
|
|
|
<p>To create a bunch of datasets that share a single datatype
|
|
just create the datasets with a committed (named) datatype.
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: Shared Types</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>To create two datasets that share a common datatype
|
|
one just commits the datatype, giving it a name, and
|
|
then uses that datatype to create the datasets.
|
|
|
|
<p><code><pre>
|
|
hid_t t1 = ...some transient type...;
|
|
H5Tcommit (file, "shared_type", t1);
|
|
hid_t dset1 = H5Dcreate (file, "dset1", t1, space, H5P_DEFAULT);
|
|
hid_t dset2 = H5Dcreate (file, "dset2", t1, space, H5P_DEFAULT);
|
|
</code></pre>
|
|
|
|
<p>And to create two additional datasets later which
|
|
share the same type as the first two datasets:
|
|
|
|
<p><code><pre>
|
|
hid_t dset1 = H5Dopen (file, "dset1");
|
|
hid_t t2 = H5Dget_type (dset1);
|
|
hid_t dset3 = H5Dcreate (file, "dset3", t2, space, H5P_DEFAULT);
|
|
hid_t dset4 = H5Dcreate (file, "dset4", t2, space, H5P_DEFAULT);
|
|
</code></pre>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<a name="Datatypes-DataConversion">
|
|
<h2>11. Data Conversion</h2>
|
|
</a>
|
|
|
|
<p>The library is capable of converting data from one type to
|
|
another and does so automatically when reading or writing the
|
|
raw data of a dataset, attribute data, or fill values. The
|
|
application can also change the type of data stored in an array.
|
|
|
|
<p>In order to insure that data conversion exceeds disk I/O rates,
|
|
common data conversion paths can be hand-tuned and optimized for
|
|
performance. The library contains very efficient code for
|
|
conversions between most native datatypes and a few non-native
|
|
datatypes, but if a hand-tuned conversion function is not
|
|
available, then the library falls back to a slower but more
|
|
general conversion function. The application programmer can
|
|
define additional conversion functions when the libraries
|
|
repertoire is insufficient. In fact, if an application does
|
|
define a conversion function which would be of general interest,
|
|
we request that the function be submitted to the HDF5
|
|
development team for inclusion in the library.
|
|
|
|
<p><b>Note:</b> The HDF5 library contains a deliberately limited
|
|
set of conversion routines. It can convert from one integer
|
|
format to another, from one floating point format to another,
|
|
and from one struct to another. It can also perform byte
|
|
swapping when the source and destination types are otherwise the
|
|
same. The library does not contain any functions for converting
|
|
data between integer and floating point formats. It is
|
|
anticipated that some users will find it necessary to develop
|
|
float to integer or integer to float conversion functions at the
|
|
application level; users are invited to submit those functions
|
|
to be considered for inclusion in future versions of the
|
|
library.
|
|
|
|
<p>A conversion path contains a source and destination datatype
|
|
and each path contains a <em>hard</em> conversion function
|
|
and/or a <em>soft</em> conversion function. The only difference
|
|
between hard and soft functions is the way in which the library
|
|
chooses which function applies: A hard function applies to a
|
|
specific conversion path while a soft function may apply to
|
|
multiple paths. When both hard and soft functions apply to a
|
|
conversion path, then the hard function is favored and when
|
|
multiple soft functions apply, the one defined last is favored.
|
|
|
|
<p>A data conversion function is of type <code>H5T_conv_t</code>
|
|
which is defined as:
|
|
|
|
<p>
|
|
<code><pre>
|
|
typedef herr_t (*H5T_conv_t)(hid_t <em>src_type</em>,
|
|
hid_t <em>dest_type</em>,
|
|
H5T_cdata_t *<em>cdata</em>,
|
|
size_t <em>nelmts</em>,
|
|
void *<em>buffer</em>,
|
|
void *<em>background</em>);
|
|
</pre></code>
|
|
|
|
<p>The conversion function is called with the source and
|
|
destination datatypes (<em>src_type</em> and
|
|
<em>dst_type</em>), path-constant data (<em>cdata</em>), the
|
|
number of instances of the datatype to convert
|
|
(<em>nelmts</em>), a buffer which initially contains an array of
|
|
data having the source type and on return will contain an array
|
|
of data having the destination type (<em>buffer</em>), and a
|
|
temporary or background buffer (<em>background</em>). Functions
|
|
return a negative value on failure and some other value on
|
|
success.
|
|
|
|
<p>The <code>command</code> field of the <em>cdata</em> argument
|
|
determines what happens within the conversion function. It's
|
|
values can be:
|
|
|
|
<dl>
|
|
<dt><code>H5T_CONV_INIT</code>
|
|
<dd>This command is to hard conversion functions when they're
|
|
registered or soft conversion functions when the library is
|
|
determining if a conversion can be used for a particular path.
|
|
The <em>src_type</em> and <em>dst_type</em> are the end-points
|
|
of the path being queried and <em>cdata</em> is all zero. The
|
|
library should examine the source and destination types and
|
|
return zero if the conversion is possible and negative
|
|
otherwise (hard conversions need not do this since they've
|
|
presumably been registered only on paths they support). If
|
|
the conversion is possible the library may allocate and
|
|
initialize private data and assign the pointer to the
|
|
<code>priv</code> field of <em>cdata</em> (or private data can
|
|
be initialized later). It should also initialize the
|
|
<code>need_bkg</code> field described below. The <em>buf</em>
|
|
and <em>background</em> pointers will be null pointers.
|
|
|
|
<br><br>
|
|
<dt><code>H5T_CONV_CONV</code>
|
|
<dd>This command indicates that data points should be converted.
|
|
The conversion function should initialize the
|
|
<code>priv</code> field of <em>cdata</em> if it wasn't
|
|
initialize during the <code>H5T_CONV_INIT</code> command and
|
|
then convert <em>nelmts</em> instances of the
|
|
<em>src_type</em> to the <em>dst_type</em>. The
|
|
<em>buffer</em> serves as both input and output. The
|
|
<em>background</em> buffer is supplied according to the value
|
|
of the <code>need_bkg</code> field of <em>cdata</em> (the
|
|
values are described below).
|
|
|
|
<br><br>
|
|
<dt><code>H5T_CONV_FREE</code>
|
|
<dd>The conversion function is about to be removed from some
|
|
path and the private data (the
|
|
<code><em>cdata</em>->priv</code> pointer) should be freed and
|
|
set to null. All other pointer arguments are null, the
|
|
<em>src_type</em> and <em>dst_type</em> are invalid
|
|
(negative), and the <em>nelmts</em> argument is zero.
|
|
|
|
<br><br>
|
|
<dt><em>Others...</em>
|
|
<dd>Other commands might be implemented later and conversion
|
|
functions that don't support those commands should return a
|
|
negative value.
|
|
</dl>
|
|
|
|
|
|
<p>Whether a background buffer is supplied to a conversion
|
|
function, and whether the background buffer is initialized
|
|
depends on the value of <code><em>cdata</em>->need_bkg</code>
|
|
which the conversion function should have initialized during the
|
|
H5T_CONV_INIT command. It can have one of these values:
|
|
|
|
<dl>
|
|
<dt><code>H5T_BKG_NONE</code>
|
|
<dd>No background buffer will be supplied to the conversion
|
|
function. This is the default.
|
|
|
|
<br><br>
|
|
<dt><code>H5T_BKG_TEMP</code>
|
|
<dd>A background buffer will be supplied but it will not be
|
|
initialized. This is useful for those functions requiring some
|
|
extra buffer space as the buffer can probably be allocated
|
|
more efficiently by the library (the application can supply
|
|
the buffer as part of the dataset transfer property list).
|
|
|
|
<br><br>
|
|
<dt><code>H5T_BKG_YES</code>
|
|
<dd>An initialized background buffer is passed to the conversion
|
|
function. The buffer is initialized with the current values
|
|
of the destination for the data which is passed in through the
|
|
<em>buffer</em> argument. It can be used to "fill in between
|
|
the cracks". For instance, if the destination type is a
|
|
compound datatype and we are initializing only part of the
|
|
compound datatype from the source type then the background
|
|
buffer can be used to initialize the other part of the
|
|
destination.
|
|
</dl>
|
|
|
|
<p>The <code>recalc</code> field of <em>cdata</em> is set when the
|
|
conversion path table changes. It can be used by conversion
|
|
function that cache other conversion paths so they know when
|
|
their cache needs to be recomputed.
|
|
|
|
|
|
<p>Once a conversion function is written it can be registered and
|
|
unregistered with these functions:
|
|
|
|
<dl>
|
|
<dt><code>herr_t H5Tregister(H5T_pers_t <em>pers</em>, const
|
|
char *<em>name</em>, hid_t <em>src_type</em>, hid_t
|
|
<em>dest_type</em>, H5T_conv_t <em>func</em>)</code>
|
|
<dd>Once a conversion function is written, the library must be
|
|
notified so it can be used. The function can be registered as
|
|
a hard (<code>H5T_PERS_HARD</code>) or soft
|
|
(<code>H5T_PERS_SOFT</code>) conversion depending on the value
|
|
of <em>pers</em>, displacing any previous conversions for all
|
|
applicable paths. The <em>name</em> is used only for
|
|
debugging but must be supplied. If <em>pers</em> is
|
|
<code>H5T_PERS_SOFT</code> then only the type classes of the
|
|
<em>src_type</em> and <em>dst_type</em> are used. For
|
|
instance, to register a general soft conversion function that
|
|
can be applied to any integer to integer conversion one could
|
|
say: <code>H5Tregister(H5T_PERS_SOFT, "i2i", H5T_NATIVE_INT,
|
|
H5T_NATIVE_INT, convert_i2i)</code>. One special conversion
|
|
path called the "no-op" conversion path is always defined by
|
|
the library and used as the conversion function when no data
|
|
transformation is necessary. The application can redefine this
|
|
path by specifying a new hard conversion function with a
|
|
negative value for both the source and destination datatypes,
|
|
but the library might not call the function under certain
|
|
circumstances.
|
|
|
|
<br><br>
|
|
<dt><code>herr_t H5Tunregister (H5T_pers_t <em>pers</em>, const
|
|
char *<em>name</em>, hid_t <em>src_type</em>, hid_t
|
|
<em>dest_type</em>, H5T_conv_t <em>func</em>)</code>
|
|
<dd>Any conversion path or function that matches the critera
|
|
specified by a call to this function is removed from the type
|
|
conversion table. All fields have the same interpretation as
|
|
for <code>H5Tregister()</code> with the added feature that any
|
|
(or all) may be wild cards. The
|
|
<code>H5T_PERS_DONTCARE</code> constant should be used to
|
|
indicate a wild card for the <em>pers</em> argument. The wild
|
|
card <em>name</em> is the null pointer or empty string, the
|
|
wild card for the <em>src_type</em> and <em>dest_type</em>
|
|
arguments is any negative value, and the wild card for the
|
|
<em>func</em> argument is the null pointer. The special no-op
|
|
conversion path is never removed by this function.
|
|
</dl>
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: A conversion
|
|
function</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>Here's an example application-level function that
|
|
converts Cray <code>unsigned short</code> to any other
|
|
16-bit unsigned big-endian integer. A cray
|
|
<code>short</code> is a big-endian value which has 32
|
|
bits of precision in the high-order bits of a 64-bit
|
|
word.
|
|
|
|
<p><code><pre>
|
|
1 typedef struct {
|
|
2 size_t dst_size;
|
|
3 int direction;
|
|
4 } cray_ushort2be_t;
|
|
5
|
|
6 herr_t
|
|
7 cray_ushort2be (hid_t src, hid_t dst,
|
|
8 H5T_cdata_t *cdata,
|
|
9 size_t nelmts, void *buf,
|
|
10 const void *background)
|
|
11 {
|
|
12 unsigned char *src = (unsigned char *)buf;
|
|
13 unsigned char *dst = src;
|
|
14 cray_ushort2be_t *priv = NULL;
|
|
15
|
|
16 switch (cdata->command) {
|
|
17 case H5T_CONV_INIT:
|
|
18 /*
|
|
19 * We are being queried to see if we handle this
|
|
20 * conversion. We can handle conversion from
|
|
21 * Cray unsigned short to any other big-endian
|
|
22 * unsigned integer that doesn't have padding.
|
|
23 */
|
|
24 if (!H5Tequal (src, H5T_CRAY_USHORT) ||
|
|
25 H5T_ORDER_BE != H5Tget_order (dst) ||
|
|
26 H5T_SGN_NONE != H5Tget_signed (dst) ||
|
|
27 8*H5Tget_size (dst) != H5Tget_precision (dst)) {
|
|
28 return -1;
|
|
29 }
|
|
30
|
|
31 /*
|
|
32 * Initialize private data. If the destination size
|
|
33 * is larger than the source size, then we must
|
|
34 * process the elements from right to left.
|
|
35 */
|
|
36 cdata->priv = priv = malloc (sizeof(cray_ushort2be_t));
|
|
37 priv->dst_size = H5Tget_size (dst);
|
|
38 if (priv->dst_size>8) {
|
|
39 priv->direction = -1;
|
|
40 } else {
|
|
41 priv->direction = 1;
|
|
42 }
|
|
43 break;
|
|
44
|
|
45 case H5T_CONV_FREE:
|
|
46 /*
|
|
47 * Free private data.
|
|
48 */
|
|
49 free (cdata->priv);
|
|
50 cdata->priv = NULL;
|
|
51 break;
|
|
52
|
|
53 case H5T_CONV_CONV:
|
|
54 /*
|
|
55 * Convert each element, watch out for overlap src
|
|
56 * with dst on the left-most element of the buffer.
|
|
57 */
|
|
58 priv = (cray_ushort2be_t *)(cdata->priv);
|
|
59 if (priv->direction<0) {
|
|
60 src += (nelmts - 1) * 8;
|
|
61 dst += (nelmts - 1) * dst_size;
|
|
62 }
|
|
63 for (i=0; i<n; i++) {
|
|
64 if (src==dst && dst_size<4) {
|
|
65 for (j=0; j<dst_size; j++) {
|
|
66 dst[j] = src[j+4-dst_size];
|
|
67 }
|
|
68 } else {
|
|
69 for (j=0; j<4 && j<dst_size; j++) {
|
|
70 dst[dst_size-(j+1)] = src[3-j];
|
|
71 }
|
|
72 for (j=4; j<dst_size; j++) {
|
|
73 dst[dst_size-(j+1)] = 0;
|
|
74 }
|
|
75 }
|
|
76 src += 8 * direction;
|
|
77 dst += dst_size * direction;
|
|
78 }
|
|
79 break;
|
|
80
|
|
81 default:
|
|
82 /*
|
|
83 * Unknown command.
|
|
84 */
|
|
85 return -1;
|
|
86 }
|
|
87 return 0;
|
|
88 }
|
|
</pre></code>
|
|
|
|
<p>The <em>background</em> argument is ignored since
|
|
it's generally not applicable to atomic datatypes.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
<p>
|
|
<center>
|
|
<table border align=center width="100%">
|
|
<caption align=bottom><h4>Example: Soft
|
|
Registration</h4></caption>
|
|
<tr>
|
|
<td>
|
|
<p>The convesion function described in the previous
|
|
example applies to more than one conversion path.
|
|
Instead of enumerating all possible paths, we register
|
|
it as a soft function and allow it to decide which
|
|
paths it can handle.
|
|
|
|
<p><code><pre>
|
|
H5Tregister(H5T_PERS_SOFT, "cus2be",
|
|
H5T_NATIVE_INT, H5T_NATIVE_INT,
|
|
cray_ushort2be);
|
|
</pre></code>
|
|
|
|
<p>This causes it to be consulted for any conversion
|
|
from an integer type to another integer type. The
|
|
first argument is just a short identifier which will
|
|
be printed with the datatype conversion statistics.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</center>
|
|
|
|
|
|
<p><b>NOTE:</b> The idea of a master soft list and being able to
|
|
query conversion functions for their abilities tries to overcome
|
|
problems we saw with AIO. Namely, that there was a dichotomy
|
|
between generic conversions and specific conversions that made
|
|
it very difficult to write a conversion function that operated
|
|
on, say, integers of any size and order as long as they don't
|
|
have zero padding. The AIO mechanism required such a function
|
|
to be explicitly registered (like
|
|
<code>H5Tregister_hard()</code>) for each an every possible
|
|
conversion path whether that conversion path was actually used
|
|
or not.
|
|
|
|
|
|
|
|
<hr>
|
|
<center>
|
|
<table border=0 width=98%>
|
|
<tr><td valign=top align=left>
|
|
<a href="index.html">HDF5 documents and links</a> <br>
|
|
<a href="H5.intro.html">Introduction to HDF5</a> <br>
|
|
<a href="RM_H5Front.html">HDF5 Reference Manual</a> <br>
|
|
<!--
|
|
<a href="Glossary.html">Glossary</a><br>
|
|
-->
|
|
</td>
|
|
<td valign=top align=right>
|
|
And in this document, the
|
|
<a href="H5.user.html"><strong>HDF5 User's Guide:</strong></a>
|
|
<br>
|
|
<a href="Files.html">Files</a>
|
|
<a href="Datasets.html">Datasets</a>
|
|
Datatypes
|
|
<a href="Dataspaces.html">Dataspaces</a>
|
|
<a href="Groups.html">Groups</a>
|
|
<br>
|
|
<a href="References.html">References</a>
|
|
<a href="Attributes.html">Attributes</a>
|
|
<a href="Properties.html">Property Lists</a>
|
|
<a href="Errors.html">Error Handling</a>
|
|
<br>
|
|
<a href="Filters.html">Filters</a>
|
|
<a href="Caching.html">Caching</a>
|
|
<a href="Chunking.html">Chunking</a>
|
|
<a href="MountingFiles.html">Mounting Files</a>
|
|
<br>
|
|
<a href="Performance.html">Performance</a>
|
|
<a href="Debugging.html">Debugging</a>
|
|
<a href="Environment.html">Environment</a>
|
|
<a href="ddl.html">DDL</a>
|
|
<br>
|
|
<a href="Ragged.html">Ragged Arrays</a>
|
|
</td></tr>
|
|
</table>
|
|
</center>
|
|
|
|
|
|
<hr>
|
|
<address>
|
|
<a href="mailto:hdfhelp@ncsa.uiuc.edu">HDF Help Desk</a>
|
|
</address>
|
|
|
|
<!-- Created: Thu Dec 4 14:57:32 EST 1997 -->
|
|
<!-- hhmts start -->
|
|
Last modified: 2 June 2000
|
|
<!-- hhmts end -->
|
|
<br>
|
|
Describes HDF5 Release 1.2.2, June 2000
|
|
|
|
|
|
</body>
|
|
</html>
|