[Top] [Prev] [Next] [Bottom]

4.5 Creating and Writing to Multi-Field Vdatas

There are three steps involved in creating vdatas with more than one field, or general vdatas: define the vdata, write the data to the vdata then write the vdata to the file. These steps are usually executed within a single program, although it is also possible to define an empty vdata in anticipation of writing data to it at a later time.

4.5.1 Creating Vdatas

Creating an empty vdata involves the following steps:

1. Open a file.
2. Initialize the VS interface.
3. Create the new vdata.
4. Assign a vdata name. (optional)
5. Assign a class. (optional)
6. Define the fields.
7. Set the interlace mode.
8. Dispose of the vdata identifier.
9. Terminate access to the VS interface.
10. Close the file.
Like the high-level VH interface, the VS interface does not retain default settings from one operation to the next or from one file to the next. Each time a vdata is created, its definitions must be explicitly reset.

To create a multi-component vdata, the calling program must contain the following:

C:		file_id = Hopen(filename, file_access_mode, n_dds);
		status = Vstart(file_id);
		vdata_id = VSattach(file_id, vdata_ref, vdata_access_mode);
		status = VSsetname(vdata_id, vdata_name);
		status = VSsetclass(vdata_id, vdata_class);
		status = VSfdefine(vdata_id, fieldname, data_type, order);
		status = VSsetfields(vdata_id, fieldname_list);
		status = VSsetinterlace(vdata_id, interlace_type);
		status = VSdetach(vdata_id);
		status = Vend(file_id);
		status = Hclose(file_id);
FORTRAN:	file_id = hopen(filename, file_access_mode, n_dds)
		status = vfstart(file_id)
		vdata_id = vsfatch(file_id, vdata_ref, vdata_access_mode)
		status = vsfsnam(vdata_id, vdata_name)
		status = vsfscls(vdata_id, vdata_class)
		status = vsffdef(vdata_id, fieldname, data_type, order)
		status = vsfsfld(vdata_id, fieldname_list)
		status = vsfsint(vdata_id, interlace_type)
		status = vsfdtch(vdata_id)
		status = vfend(file_id)
		status = hclose(file_id)

In the routines that follow, vdata_id is the vdata identifier returned by VSattach.

4.5.1.1 Creating New Vdatas with VSattach

When a new vdata is created, the value of the VSattach parameter vdata_ref must be -1 and the value of the access_mode parameter must be "w".

4.5.1.2 Assigning a Vdata Name and Class: VSsetname and VSsetclass

VSsetname assigns a name to a vdata object. If not explicitly named by a call to VSsetname, the name of the vdata is set by default to NULL. Names may be assigned and reassigned at any time after the vdata is created. The parameter vdata_name contains the name to be assigned to the vdata.

VSsetclass assigns a class to a vdata. If VSsetclass is not called, the vdata's class is set by default to NULL. As with vdata names, the class may be assigned and reassigned any time after the vdata is created. The parameter vdata_class contains the class name to be assigned to the vdata.

The parameters for VSsetname and VSsetclass are further defined below. (See Table 4E on page 113.)

4.5.1.3 Defining a Field Within Vdatas: VSfdefine

VSfdefine defines a field within a newly-created vdata. Each VSfdefine call assigns the name contained in the fieldname argument, the data type contained in the data_type argument and the order contained in the order argument to one new field. Fields will appear in the vdata in the order they are defined. Once data is written to a vdata, field definitions may not be modified or deleted.

The VS interface also provides certain predefined fields. (See Table 4D.) A predefined field is assumed to have a specific name, data type and order, so there is no need to call VSfdefine to define a predefined field. Some applications may require the use of pre-defined fields in vdatas. (For instance, NCSA Polyview requires the coordinates of vertices to have the predefined field names "PX", "PY" and "PZ".)

The parameters of VSfdefine are further described below. (See Table 4E on page 113.)

TABLE 4D Predefined Data Types and Field Names for Vdata Fields
Data Type

Coordinate Point Field Names

Normal Component Field Names

x-coordinate

y-coordinate

z-coordinate

x-component

y-component

z-component

float

PX

PY

PZ

NX

NY

NZ

integer

IX

IY

IZ

None

None

None

4.5.1.4 Initializing the Fields for Write Access: VSsetfields

VSsetfields initializes write access to the fields in a vdata. It is used to specify the fields to be written and the order they are to be written in . The parameter fieldname_list is a comma-separated list of all the fields that will be accessed during the write operation. It may contain any fieldname previously defined by VSfdefine, but it may also contain names of predefined fields.

VSsetfields must be called prior to any write operation and may only be called once for each vdata. Any field not named in the fieldname_list parameter is removed from the vdata. The combined width of the fields must be less than 32,767 bytes. If an attempt is made to create a larger record than this, VSsetfields will fail and an error condition will result.

The parameters of VSsetfields are further described below. (See Table 4E on page 113.)

4.5.1.5 Specifying the Interlace Mode: VSsetinterlace

The VS interface supports two modes of interlacing: file interlacing and buffer interlacing. File interlacing determines how data is stored in a vdata and buffer interlacing determines how data is stored in memory. The VS interface can write data from a buffer to a file in an interlaced or non-interlaced manner. It can also read data from a file in an interlaced or non-interlaced manner.

The VSread and VSwrite routines set the file interlace mode. The VSwrite routine will be discussed in Section 4.5.2.2 on page 115 and the VSread routine will be discussed in Section 4.6.3 on page 123.

VSsetinterlace sets the file interlacing mode for a vdata. Setting the parameter interlace to FULL_INTERLACE fills the vdata by record, whereas specifying NO_INTERLACE fills the vdata by field. (See Figure 4d.) For multi-component fields, all components are treated as a single field. As with file interlacing, the default vdata interlace mode is FULL_INTERLACE because it is more efficient to write complete records than it is to write fields if the file and memory interlace modes are the same, although both require the same amount of disk space.

FIGURE 4d Interlaced and Non-Interlaced Vdata Contents

VSsetinterlace can only be used for operations on new vdatas as the interlacing cannot be changed once the data has been written to a vdata object. Also, although records in a fully interlaced vdata can be written with one or more write operations, all records in a non-interlaced vdata must be written at the same time.

The parameters of VSsetinterlace are further described in the following table.

TABLE 4E VSsetname, VSsetclass, VSfdefine, VSsetfields and VSsetinterlace Parameter List
Routine Name

(Fortran-77)

Parameter

Data Type

Description

C

Fortran-77

VSsetname

(vsfsnam)

vdata_id

int32

integer

Vdata identifier.

vdata_name

char *

character* (*)

Vdata name.

VSsetclass

(vsfscls)

vdata_id

int32

integer

Vdata identifier.

vdata_class

char *

character* (*)

Vdata name.

VSfdefine

(vsffdef)

vdata_id

int32

integer

Vdata identifier.

fieldname

char *

character* (*)

Name of the field to be defined.

data_type

int32

integer

Type of the field data.

order

int32

integer

Order of the new field.

VSsetfields

(vsfsfld)

vdata_id

int32

integer

Vdata identifier.

fields

char *

character* (*)

Names of the vdata fields to be accessed.

VSsetinterlace

(vsfsint)

vdata_id

int32

integer

Vdata identifier.

interlace

int32

integer

Interlace mode.

4.5.2 Writing Data to Vdatas

Writing to a vdata requires the following steps:

1. Open a file.
2. Initialize the VS interface.
3. Access the vdata.
4. Seek to the target record.
5. Write the data.
6. Dispose of the vdata identifier.
7. Terminate access to the VS interface.
8. Close the file.
To write data to a vdata, the calling program must contain the following sequence of calls:

C:		file_id = Hopen(filename, file_access_mode, n_dds);
		status = Vstart(file_id);
		vdata_id = VSattach(file_id, vdata_ref, vdata_access_mode);
		status = VSsetfields(vdata_id, *fields);
		record_pos = VSseek(vdata_id, record_index);
		num_of_recs = VSwrite(vdata_id, *databuf, n_records, interlace);
		status = VSdetach(vdata_id);
		status = Vend(file_id);
		status = Hclose(file_id);
FORTRAN:	file_id = hopen(filename, file_access_mode, n_dds)
		status = vfstart(file_id)
		vdata_id = vsfatch(file_id, vdata_ref, vdata_access_mode)
		status = vsfsfld(vdata_id, *fields);
		record_pos = vsfseek(vdata_id, record_index);
		num_of_recs = vsfwrit(vdata_id, *databuf, n_records, interlace)
		status = vsfdtch(vdata_id)
		status = vfend(file_id)
		status = hclose(file_id)

4.5.2.1 Resetting the Current Position Within Vdatas: VSseek

VSseek provides a mechanism for random-access write operations within fully-interlaced vdatas. Random-access writes are not available for non-interlaced vdatas. The parameter record_index is the position of the record to be written. The position of the first record in a vdata is specified by record_index=0: any vdata operation will be performed on this vdata before VSseek is called.

One thing that should be kept in mind while using VSseek is that it has been designed for the purpose of overwriting data, not appending data. Figure 4e illustrates a situation where this aspect of VSseek can be misused with unexpected results and how VSread is called to correctly place the record pointer at the end of the vdata object for appending.

FIGURE 4e Setting the Record Pointer at the End of a Vdata Object

In this figure we have a vdata that consists of only four records. Using VSseek to seek to the end of the fourth record by setting the record parameter to 4 results in an error condition. To avoid this situation, we set the record parameter to 3 in order to set the current record pointer to the end of the third record. We then use VSread to read the contents of the fourth record in a buffer and move the current record pointer to the end of the fourth record. The contents of the buffer can then be discarded. This method can be generalized to a vdata of any number of records; seek to the last record with VSseek, then read the last record using VSread.

4.5.2.2 Writing to a Vdata: VSwrite

VSwrite writes buffered data to the vdata in the specified HDF file. The parameter databuf is a buffer containing records to store in the vdata. The n_records parameter is the number of records to be written.

The array databuf is assumed to be organized in memory as specified by interlace. Selecting FULL_INTERLACE indicates that the array in memory is organized by record, whereas NO_INTERLACE indicates that the array is organized by field. (See Figure 4f.) Notice that file interlacing is set by VSsetinterlace when the vdata is created, whereas the buffer interlacing is set by VSwrite when data is written to file. VSwrite will write interlaced or non-interlaced data to a file as an interlaced or non-interlaced vdata. However, multiple write operations can only be used on fully-interlaced vdatas.

FIGURE 4f Writing Interlaced or Non-Interlaced Buffers into Interlaced or Non-interlaced Vdatas

The data in databuf is assumed to contain the exact amount of data in the order needed to fill the fields defined in the last call to VSsetfields. Because VSwrite writes the contents of databuf contiguously to the vdata, any "padding" due to record alignment must be removed before attempting to write from databuf to the vdata. For more information on removing alignment padding see Section 4.5.2.3 on page 118.

It should be remembered that VSwrite writes whole records, not individual fields. If a modification to one field within a previously-written record is needed, the contents of the record must first be preserved by reading it using VSread, which will be described in Section 4.6.3 on page 123, updating the field then writing it using VSwrite.

To create an empty vdata either VSwrite, VSsetname and VSsetclass must be called before VSdetach. If VSwrite is not called the vdata created will be an empty vdata. If VSsetfields is called and VSwrite, VSsetname or VSsetclass is not called, the vdata will not be written.

TABLE 4F VSseek and VSwrite Parameter List
Routine Name

(Fortran-77)

Parameter

Data Type

Description

C

Fortran-77

VSseek

(vsfseek)

vdata_id

int32

integer

Vdata identifier.

record

int32

integer

Record number to seek to.

VSwrite

(vsfwrit)

vdata_id

int32

integer

Vdata identifier.

databuf

char *

character* (*)

Buffer for records to be stored.

n_records

int32

integer

Number of records to be stored.

interlace

int32

integer

Interlace mode of the buffered data.

EXAMPLE 3. Writing Data to a New Vdata

In these examples, the VS interface is used to create a vdata containing ten rows, each comprised of one 16-bit integer field of order 3. The vdata is named "Example Vdata", the vdata class is "Example Vdata" and the field is named "Field Entries".

C:

#include "hdf.h"

#define FIELD_NAME "Field Entries"
#define NUMBER_OF_ROWS 10
#define ORDER 3

main( ) 
{

	int32	 file_id, vdata_id, status, num_of_elements;
	int16	 vdata_buf[NUMBER_OF_ROWS * ORDER], i;

	/* Open the HDF file. */
	file_id = Hopen("Example3.hdf", DFACC_CREATE, 0);

	/* Initialize HDF for subsequent the vgroup/vdata access. */
	status = Vstart(file_id);

	/* Create a new vdata. */
	vdata_id = VSattach(file_id, -1, "w");

	/* Define the field data name, type and order. */
	status = VSfdefine(vdata_id, FIELD_NAME, DFNT_INT16, ORDER);

	/* Specify the field(s) that will be written to. */
	status = VSsetfields(vdata_id, FIELD_NAME);

	/* Generate the Vset data. */
	for (i = 0; i < NUMBER_OF_ROWS * ORDER; i+=ORDER) {
	   vdata_buf[i] = i;
	   vdata_buf[i + 1] = i + 1;
	   vdata_buf[i + 2] = i + 2;
	}

	/* Write the data to the Vset. */
	num_of_elements = VSwrite(vdata_id, (char *)vdata_buf, NUMBER_OF_ROWS, 
                              FULL_INTERLACE);

	/* Set the name and class. */
	status = VSsetname(vdata_id, "Example Vdata");
	status = VSsetclass(vdata_id, "Example Vdata");

	/* Terminate access to the vdata. */
	status = VSdetach(vdata_id);

	/* Terminate access to the Vset interface and close the file. */
	status = Vend(file_id);
	status = Hclose(file_id);

}

FORTRAN:

	 PROGRAM WRITE VDATA

      integer file_id, vdata_id
      integer status
      integer vdata_buf(30), i, number_of_rows, num_of_elements
      integer hopen, vsfatch, vsffdef, vsfsfld
      integer vsfwrit, vsfsnam, vsfscls, vsfdtch
      integer hclose, vfstart, vfend

C     DFACC_CREATE, DFNT_INT16 and FULL_INTERLACE are defined 
C     in hdf.inc.
      integer DFACC_CREATE, DFNT_INT16, FULL_INTERLACE
      parameter (DFACC_CREATE = 4,
     +           DFNT_INT16 = 22,
     +           FULL_INTERLACE = 0)

      character field_name *13
      parameter (field_name = `Field Entries')
      parameter (number_of_rows = 10)

C     Create an HDF file with full access. 
      file_id = hopen(`Example3.hdf', DFACC_CREATE, 0)

C     Initialize HDF for subsequent vgroup/vdata access. 
      status = vfstart(file_id)

C     Create a new vdata. 
      vdata_id = vsfatch(file_id, -1, `w')

C     Define the field data name, type and order. 
      status = vsffdef(vdata_id, field_name, DFNT_INT16, 3)

C     Specify the field that will be written to. 
      status = vsfsfld(vdata_id, field_name)

C     Generate the Vset data. 
      do 10 i = 1, number_of_rows * 3, 3
        vdata_buf(i) = i+1
        vdata_buf(i + 1) = i+2
        vdata_buf(i + 2) = i+3
10    continue

C     Set the name and class. 
      status = vsfsnam(vdata_id, `Example Vdata')
      status = vsfscls(vdata_id, `Example Vdata')

C     Write the data to the Vset. 
      num_of_elements = vsfwrit(vdata_id, vdata_buf, number_of_rows * 
3, 
     +                FULL_INTERLACE)

C     Terminate access to the vdata. 
      status = vsfdtch(vdata_id)

C     Terminate access to the Vset interface and close the file. 
      status = vfend(file_id)
      status = hclose(file_id)

      end

4.5.2.3 Filling Records of Mixed Field Data Types

Storing fields of mixed data types is an efficient use of disk space and is useful in applications that use structures. While data structures in memory containing fields of variable lengths often contain padding or alignment bytes, data stored in a vdata does not contain padding. This is true for both fully-interlaced and non-interlaced data. Because of this, when variable-length field types are used it is not generally possible to write data directly from a structure in memory into a vdata in a file with a VSwrite call. Instead, the data from the structure must first be written to a temporary buffer array, removing all alignment bytes. This packed array is then written to a vdata using VSwrite. (See Figure 4b.)

FIGURE 4g Removing Alignment Bytes When Writing Data From a C Structure to a Vdata

EXAMPLE 4. Writing Packed Vdata Data to an HDF File

The following examples write packed data into a vdata. Note that while the memcpy routine is called for each field, the bcopy routine may be used instead.

Fortran-77 does not support record structures so there is no standard way to do packing as in C. The Fortran-77 example will work in many platforms but it is not guaranteed to be 100% portable.

C:

#include <stdio.h>
#include "hdf.h"

#define NRECORDS 20

#define FIELD_1 "Temp" 
#define FIELD_2 "Height" 
#define FIELD_3 "Speed" 
#define FIELD_4 "Ident"
#define FIELD_NAMES  "Temp,Height,Speed,Ident"

main( ) 
{

	struct {
	   float32  temp;
	   int16    height;
	   float32  speed;
	   char     ident;
	} source[NRECORDS];

	int32	file_id, vdata_id;
	uint8 *pntr;
	uchar8 *databuf;
	int i;
	int32 status;
	int32 msize = 0;

	/* Create the HDF file. */
	file_id = Hopen("Example4.hdf", DFACC_CREATE, 0);

	/* Initialize the Vset interface. */
	status = Vstart(file_id);

	/* Create a new vdata. */
	vdata_id = VSattach(file_id, -1, "w");

	/* Define the field to write. */
	status = VSfdefine(vdata_id, FIELD_1, DFNT_FLOAT32, 1); 
	status = VSfdefine(vdata_id, FIELD_2, DFNT_INT16, 1); 
	status = VSfdefine(vdata_id, FIELD_3, DFNT_FLOAT32, 1); 
	status = VSfdefine(vdata_id, FIELD_4, DFNT_CHAR8, 1); 

	/* Set the vdata name. */
	status = VSsetname(vdata_id, "Example Vset Name");

	/* Set the vdata class. */
	status = VSsetclass(vdata_id, "Example Vset Class");

	/* Set the field names. */
	status = VSsetfields(vdata_id, FIELD_NAMES);

	databuf = (uint8 *) malloc(((2 * sizeof(float32)) 
	           + sizeof(int16) + sizeof(char)) * NRECORDS);

	pntr = databuf;

	for (i = 0; i < NRECORDS; i++) {
	   source[i].temp = 1.11 * (i+1);
	   memcpy(pntr, &source[i].temp, sizeof(float32)); 
	   pntr += sizeof(float32);

	   source[i].height = i+1;
	   memcpy(pntr, &source[i].height, sizeof(int16)); 
	   pntr += sizeof(int16);

	   source[i].speed = 1.11 * (i+1);
	   memcpy(pntr, &source[i].speed, sizeof(float32)); 
	   pntr += sizeof(float32);

	   source[i].ident = `A'+i;
	   memcpy(pntr, &source[i].ident, sizeof(char)); 
	   pntr += sizeof(char);
	}

	/* Write the data to the Vset object. */
	status = VSwrite(vdata_id, databuf, NRECORDS, FULL_INTERLACE); 

	/* 
	* Terminate access to the vdata, the VS interface 
	* and the HDF file.
	*/
	status = VSdetach(vdata_id);
	status = Vend(file_id);
	status = Hclose(file_id);

}

FORTRAN:

	 PROGRAM DATA PACK

      integer hopen, vsfatch, vsffdef, vsfsnam
      integer vsfscls, vsfsfld, vsfwrit, vsfdtch
      integer hclose, vfstart

C     Parameter definitions
      integer*4 DFACC_CREATE, DFNT_FLOAT32, DFNT_INT16, DFNT_CHAR8
      integer*4 FULL_INTERLACE
      parameter (DFACC_CREATE = 4,
     +           DFNT_FLOAT32 = 5,
     +           DFNT_INT16 = 22,
     +           DFNT_CHAR8 = 4,
     +           FULL_INTERLACE = 0)

C     Example parameters.
      integer NRECORDS
      parameter (NRECORDS = 20)

C     Vdata fields.
      real temp
      integer*2 height
      real speed

C     Vdata field equivalence names for packing. 
      character ctemp*4
      character cheight*2
      character cspeed*4
      equivalence (temp, ctemp), (height, cheight), (speed, cspeed)

C     Packing buffer with size (4+2+4+1)*NRECORDS = 220.
      character buffer*220

C     More local variables.
      integer i, bufptr, status
      integer*4 file_id, vdata_id
      character*20 idents /"ABCDEFGHIJKLMNOPQRST"/

C     Create the file. 
      file_id = hopen(`Example4.hdf', DFACC_CREATE, 0)

C     Initialize the interface. 
      status = vfstart(file_id)

C     Create a new vdata. 
      vdata_id = vsfatch(file_id, -1, `w')

C     Define the fields to write. 
      status = vsffdef(vdata_id, `Temp', DFNT_FLOAT32, 1) 
      status = vsffdef(vdata_id, `Height', DFNT_INT16, 1) 
      status = vsffdef(vdata_id, `Speed', DFNT_FLOAT32, 1) 
      status = vsffdef(vdata_id, `Ident', DFNT_CHAR8, 1) 

C     Set the vdata name. 
      status = vsfsnam(vdata_id, `Example Vset Name')

C     Set the vdata class. 
      status = vsfscls(vdata_id, `Example Vset Class')

C     Set the field names. 
      status = vsfsfld(vdata_id, `Temp,Height,Speed,Ident')

C     Pack NRECORDS of data into buffer.
      bufptr = 1

      do 10 i = 1, NRECORDS
C       Pack temp data. 
        temp = 1.1 * i
        buffer(bufptr:bufptr+3) = ctemp
        bufptr = bufptr + 4

C       Pack height data.
        height = i 
        buffer(bufptr:bufptr+1) = cheight
        bufptr = bufptr + 2

C       Pack speed data.
        speed = 11.1 * i
        buffer(bufptr:bufptr+3) = cspeed
        bufptr = bufptr + 4

C       Pack ident data.
        buffer(bufptr:bufptr) = idents(i:i)
        bufptr = bufptr + 1
10    continue

      status = vsfwrit(vdata_id, buffer, NRECORDS, FULL_INTERLACE)

C     Terminate access to the vdata object, the interface 
C     and the file.
      status = vsfdtch(vdata_id)
      status = vfend(file_id)
      status = hclose(file_id)

      end




[Top] [Prev] [Next] [Bottom]

hdfhelp@ncsa.uiuc.edu
HDF User's Guide - 06/04/97, NCSA HDF Development Group.