HL-HDF
Creating your own HDF5 product (in C)

When creating your own HDF5 product, there is one header files that should be included (hlhdf.h).

If you are going to create your own compound datatype, then include hlhdf_compound_utils.h as well.

When compiling a binary, there are three libraries that must be linked in; these are libhlhdf.a, libhdf5.a and libz.a. It is also possible to link the shared library libhdf5.so instead of libhdf5.a.

The HL-HDF package was installed with a hldef.mk file that can be included in your own Makefile in order to get the correct paths to both the zlib and the hdf5 library. It also contains information on which C++-compiler the HL-HDF package was compiled with and some other goodies.

A simple Makefile could look like this:

include /usr/local/hlhdf/mkf/hldef.mk

HLHDF_INCDIR = -I/usr/local/hlhdf/include
HLHDF_LIBDIR = -L/usr/local/hlhdf/lib

CFLAGS = $(OPTS) $(DEFS) -I. $(ZLIB_INCDIR) $(HDF5_INCDIR) \
         $(HLHDF_INCDIR)

LDFLAGS = -L. $(ZLIB_LIBDIR) $(HDF5_LIBDIR) $(HLHDF_LIBDIR)

LIBS = -lhlhdf -lhdf5 -lz -lm

TARGET=myTestProgram
SOURCES=test_program.c
OBJECTS=$(SOURCES:.c=.o)

all: $(TARGET)

$(TARGET): $(OBJECTS)
           $(CC) -o $@ $(LDFLAGS) $(OBJECTS) $(LIBS)

clean:
           @\rm -f *.o *~ so_locations core

distclean: clean
           @\rm -f $(TARGET)

distribution:
           @echo "Would bring the latest revision upto date"

install:
           @$(HL_INSTALL) -f -o -C $(TARGET) ${MY_BIN_PATH}/$(TARGET)

Now, when the Makefile has been created, it might be a good idea to write your own HDF5 product. The following example will create a dataset with a two-dimensional array of integers, and two attributes connected to this dataset. It will also create a group containing one attribute.

#include <hlhdf.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv)
{
  HL_NodeList* aList=NULL;
  HL_Node* aNode=NULL;
  HL_Compression compression;
  int* anArray=NULL;
  int anIntValue;
  float aFloatValue;
  hsize_t dims[]={10,10};
  int npts=100;
  int i;

  HL_init();  /* Initialize the HL-HDF library */
  HL_setDebugMode(2); /* Activate debugging */
  
  if(!(aList = HLNodeList_new())) {
    fprintf(stderr,"Failed to allocate nodelist");
    goto fail;
  }

  if(!(anArray = malloc(sizeof(int)*npts))) {
    fprintf(stderr,"Failed to allocate memory for array.");
    goto fail;
  }
  for(i=0;i<npts;i++)
    anArray[i]=i;

  HLNodeList_addNode(aList,(aNode = HLNode_newGroup("/group1")));
  HLNodeList_addNode(aList,(aNode = HLNode_newAttribute("/group1/attribute1")));
  anIntValue=10;
  HLNode_setScalarValue(aNode,sizeof(anIntValue),(unsigned char*)&anIntValue,"int",-1);

  HLNodeList_addNode(aList,(aNode = HLNode_newDataset("/dataset1")));
  HLNode_setArrayValue(aNode,sizeof(int),2,dims,(unsigned char*)anArray,"int",-1);

  HLNodeList_addNode(aList,(aNode = HLNode_newAttribute("/dataset1/attribute2")));
  anIntValue=20;
  HLNode_setScalarValue(aNode,sizeof(anIntValue),(unsigned char*)&anIntValue,"int",-1);

  HLNodeList_addNode(aList,(aNode = HLNode_newAttribute("/dataset1/attribute3")));
  aFloatValue=99.99;
  HLNode_setScalarValue(aNode,sizeof(aFloatValue),(unsigned char*)&aFloatValue,
                        "float",-1);

  HLNodeList_setFileName(aList,"written_hdffile.hdf");
  
  HLCompression_init(&compression, CT_ZLIB);
  compression.level = 6;
  HLNodeList_write(aList,NULL,&compression);

  HLNodeList_free(aList);
  exit(0);
  return 0; /* Won't come here */
 fail:
  HLNodeList_free(aList);
  exit(1);
  return 1; /* Won't come here */
}

When you have created your own HDF5 product, it might be a good idea to create some code for reading this file and checking its contents.

#include <hlhdf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char** argv)
{
  HL_NodeList* aList=NULL;
  HL_Node* aNode=NULL;
  int* anArray=NULL;
  int anIntValue;
  float aFloatValue;
  int npts;
  int i;

  HL_init();  /* Initialize the HL-HDF library */
  HL_setDebugMode(2); /* Activate debugging */

  if(!(aList = HLNodeList_read("written_hdffile.hdf"))) {
    fprintf(stderr,"Failed to read nodelist\n");
    goto fail;
  }

  HLNodeList_selectAllNodes(aList);  /* Select everything for retrival */

  HLNodeList_fetchMarkedNodes(aList);

  if((aNode = HLNodeList_getNodeByName(aList,"/group1")))
    printf("%s exists\n",HLNode_getName(aNode));

  if((aNode = HLNodeList_getNodeByName(aList,"/group1/attribute1"))) {
    memcpy(&anIntValue, HLNode_getData(aNode), HLNode_getDataSize(aNode));
    printf("%s exists and have value %d\n",HLNode_getName(aNode),anIntValue);
  }

  if((aNode = HLNodeList_getNodeByName(aList,"/dataset1"))) {
    anArray = (int*)HLNode_getData(aNode);
    npts = 1;
    for(i=0;i<HLNode_getRank(aNode);i++)
      npts*=HLNode_getDimension(aNode, i);
    printf("%s exists and has the values:\n",HLNode_getName(aNode));
    for(i=0;i<npts;i++) {
      printf("%d ", anArray[i]);
      if((i%HLNode_getDimension(aNode, 0))==0) {
        printf("\n");
      }
    }
    printf("\n");
  }

  if((aNode = HLNodeList_getNodeByName(aList,"/dataset1/attribute2"))) {
    memcpy(&anIntValue,HLNode_getData(aNode),HLNode_getDataSize(aNode));
    printf("%s exists and have the value %d\n",HLNode_getName(aNode),anIntValue);
  }

  if((aNode = HLNodeList_getNodeByName(aList,"/dataset1/attribute3"))) {
    memcpy(&aFloatValue,HLNode_getData(aNode),HLNode_getDataSize(aNode));
    printf("%s exists and have the value %f\n",HLNode_getName(aNode),aFloatValue);
  }
  HLNodeList_free(aList);
  exit(0);
  return 0; /* Never reached */
 fail:
  HLNodeList_free(aList);
  exit(1);
  return 1; /* Never reached */
}

A small example on how to reroute the error messages is shown below, currently there is no similar functionality for the _pyhl module.

#include <hlhdf.h>
#include <hlhdf_debug.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

void hlhdf_error_handler(char* filename, int lineno, HL_Debug lvl, const char* fmt, ...)
{
   va_list alist;
   char errbuff[512];
   va_start(alist,fmt);
   
   fprintf(stdout,"<hlhdferror>\n");
   fprintf(stdout," <filename>%s</filename>\n",filename);
   fprintf(stdout," <linenumber>%d</linenumber>\n",lineno);
   fprintf(stdout," <level>%d</level>\n",lvl);
   vsprintf(errbuff,fmt,alist);
   fprintf(stdout," <message>%s</message>\n",errbuff);
   fprintf(stdout,"</hlhdferror>\n");
}

void hdf5_error_handler(unsigned n, const H5E_error_t* rowmsg)
{
   fprintf(stdout,"<hdf5error>\n");
   fprintf(stdout," <filename>%s</filename>\n",rowmsg->file_name);
   fprintf(stdout," <linenumber>%d</linenumber>\n",rowmsg->line);
   fprintf(stdout," <funcname>%s</funcname>\n",rowmsg->func_name);
   fprintf(stdout," <desc>%s</desc>\n",rowmsg->desc);
   fprintf(stdout,"</hdf5error>\n");
}

int main(int argc, char** argv)
{
   HL_NodeList* nodelist;
   HL_init();  /* Initialize the HL-HDF library */
   HL_enableErrorReporting();
   HL_setDebugFunction(hlhdf_error_handler);
   HL_setHdf5ErrorFunction(hdf5_error_handler);
   nodelist = HLNodeList_read("thisdoesnotexist.xxx");
   return 0;
}