% Copyleft (c) 1997 by Mark Koennecke at PSI, Switzerland. % % % This software is distributed in the hope that it will be useful, % but WITHOUT ANY WARRANTY; without even the implied warranty of % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the % GNU General Public License for more details. % % You may already have a copy of the GNU General Public License; if % not, write to the Free Software Foundation, Inc., 675 Mass Ave, % Cambridge, MA 02139, USA. % \documentclass[12pt]{article} \usepackage[dvips] \setlength{\oddsidemargin}{-.1in} \setlength{\evensidemargin}{0in} \setlength{\topmargin}{0in} \addtolength{\topmargin}{-\headheight} \addtolength{\topmargin}{-\headsep} \setlength{\textheight}{8.9in} \setlength{\textwidth}{6.2in} \setlength{\marginparwidth}{0.5in} \begin{document} \title{The NeXus Application Programmer's Interface} \author{Mark K\"onnecke\\ Labor f\"ur Neutronenstreuung\\ Paul Scherrer Institut\\ CH-5232 Villigen PSI\\ Switzerland\\ Mark.Koennecke@psi.ch \\ Przemek K\l{}osowski\\ U. of Maryland \& NIST \\ przemek.klosowski@nist.gov } } \maketitle \tableofcontents \vskip.3in \centerline{\large\bf Abstract} \vskip.2in \begin{center} \parbox{.8\textwidth}{ There is a proposed portable data exchange format for neutron and X-ray scattering communities, NeXus (described in a separate publication). The present document supplements the NeXus proposal, by defining a simplified, NeXus-compliant programming interface for reading and writing NeXus files. } \end{center} \section{Introduction} \label{chap:intro} This file defines an Application Programmer's Interface (API) to HDF library as used for the NeXus data format. It encapsulates a subset of HDF and provides some helper routines to simplify creating and reading NeXus data files. The API is designed to be modal; there is a hidden state that determines which groups and datasets (Vgroups and SDSes) are open at any given moment, and subsequent operations are implicitly performed on these entities. This cuts down the number of parameters to pass around in API calls, at the cost of forcing certain pre-approved {\em mode d'emploi}. This mode d'emploi will be familiar to most: it is very similar to navigating a directory hierarchy. In our case HDF--VGroups are the directories, which can hold other directories and data items which are restricted to being HDF--scientific data sets (SDS). The API comprises the following functional groups: \begin{enumerate} \item General initialization and shutdown: opening and closing the file, creating or opening an existing group or dataset, and closing them. \item Reading and writing data and attributes to previously opened datasets. \item Routines to obtain meta-data and to iterate over component datasets and attributes. \item Handling of linking and group hierarchy. \end{enumerate} \section{Implementation} \subsection{Data Structures} The approach used in this version is to maintain a datastructure for each open file. This datastructure will hold a stack of Vgroups traversed and will thus allow stepping back and forth in the NeXus file structure. The stack is implemented using an array. The datastructure looks like this: \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap1} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ typedef struct __NexusFile {@\\ \mbox{}\verb@ int iNXID;@\\ \mbox{}\verb@ int32 iVID;@\\ \mbox{}\verb@ int32 iSID;@\\ \mbox{}\verb@ int32 iCurrentVG;@\\ \mbox{}\verb@ int32 iCurrentSDS;@\\ \mbox{}\verb@ int32 iStack[NXMAXSTACK];@\\ \mbox{}\verb@ int iStackPtr; @\\ \mbox{}\verb@ int iNDir;@\\ \mbox{}\verb@ int iCurDir;@\\ \mbox{}\verb@ int *iRefDir;@\\ \mbox{}\verb@ int *iTagDir;@\\ \mbox{}\verb@ } NexusFile, *pNexusFile;@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} The fields in more detail: \begin{itemize} \item iNXID is a test value against memory corruption. \item iVID is the file ID to use for the Vgroup interface. \item iSID is the file ID to use for the SDS interface. \item iCurrentVG is the ID of the current open Vgroup. Is 0, if there is no Vgroup open. \item iCurrentSDS is the ID of the current open SDS. Is 0, if there is no SDS open. \item iStack is the stack array. \item iStackPtr is the pointer to the current Vgroup in iStack. \item iNDir, iCurDir, iRefDir, iTagDir are data fields used during an directory search with NXgetnextentry. They are: \begin{itemize} \item iNDir: number of directory entries. \item iCurDir current directory entry. \item iRefDir array of reference numbers. \item iTagDir array of tag numbers. \end{itemize} \end{itemize} The other datastructure used is NXlink. It encapsulates all information necessary to do a link between Vgroups and SDS's. This is easy: \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap2} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ typedef struct {@\\ \mbox{}\verb@ int32 iTag;@\\ \mbox{}\verb@ int32 iRef;@\\ \mbox{}\verb@ } NXlink;@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} Before diving into code, it may be helpful to highlight a few pecularities of the HDF interface which help confuse the code. The first is, that there is no single ID to refer to a given file. The Vgroup interface and the SDS interface use two different ID's which have to be initialised at file opening and used apropriatetly. The other feature is the plethora of integer ID's associated with each HDF--object. First there is something called a reference ID which seems to be an ID which identifies an object within an HDF file. Coming with it there is a tag, which denotes the type of the object. When things need to be done to an object, HDF requires to attach or open the object. These calls usually return another ID which can than be used furtheron. This becomes invalid once you close the object again. So much bookeeping is needed to keep track of all these ID's. \label{ididid} \subsection{General initialization and shutdown} \label{ss:gen} The routines in this group are for opening and closing the NeXus/HDF file, creating or opening an existing group or dataset, and closing them. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap3} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@NXhandle NXopen(char * filename, NXaccess access_method);@\\ \mbox{}\verb@NXstatus NXclose(NXhandle fileid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@NXstatus NXmakegroup (NXhandle fileid, char* Vgroup, char* NXclass);@\\ \mbox{}\verb@NXstatus NXopengroup (NXhandle fileid, char* Vgroup, char* NXclass);@\\ \mbox{}\verb@NXstatus NXclosegroup(NXhandle fileid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@NXstatus NXmakedata (NXhandle fileid, char* label, int datatype, int rank, int dim[]);@\\ \mbox{}\verb@NXstatus NXopendata (NXhandle fileid, char* label);@\\ \mbox{}\verb@NXstatus NXclosedata(NXhandle fileid);@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXopen} NXopen opens the HDF--file filename and creates and initialises a NexusFile structure. The returned handle is actually the pointer to this structure. This danger of this aproach is, that somebody might try arithemetic on the handle and thereby corrupt the system. In order to test for this the NexusFile structure contains the NXID field which can be checked against a predefined value. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap4} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXhandle NXopen(char *filename, NXaccess am)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pNew = NULL;@\\ \mbox{}\verb@ char pBuffer[512];@\\ \mbox{}\verb@ int iRet;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* get memory */@\\ \mbox{}\verb@ pNew = (pNexusFile)malloc(sizeof(NexusFile));@\\ \mbox{}\verb@ if(!pNew)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData, "ERROR: no memory to create File datastructure");@\\ \mbox{}\verb@ return NULL;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ memset(pNew,0,sizeof(NexusFile));@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* start SDS interface */@\\ \mbox{}\verb@ pNew->iSID = SDstart(filename,am);@\\ \mbox{}\verb@ if(pNew->iSID <= 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: cannot open file: %s",filename);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ free(pNew);@\\ \mbox{}\verb@ return NULL;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* otherwise we try to create the file two times which makes HDF@\\ \mbox{}\verb@ Trow up on us.@\\ \mbox{}\verb@ */@\\ \mbox{}\verb@ if(am == NXACC_CREATE)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ am = NXACC_RDWR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* start Vgroup API */@\\ \mbox{}\verb@ pNew->iVID = Hopen(filename,am,100);@\\ \mbox{}\verb@ if(pNew->iVID <= 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: cannot open file: %s",filename);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ free(pNew);@\\ \mbox{}\verb@ return NULL;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ iRet = Vstart(pNew->iVID);@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot initalise Vgroup interface");@\\ \mbox{}\verb@@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ pNew->iNXID = NXSIGNATURE;@\\ \mbox{}\verb@ pNew->iStack[0] = 0; /* root! */@\\ \mbox{}\verb@@\\ \mbox{}\verb@ return (NXhandle)pNew; @\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXclose} NXclose closes an Nexus file and deletes all associated datastructures from memory. The handle fileid will no longer be valid after this. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap5} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXclose(NXhandle fid)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile = NULL;@\\ \mbox{}\verb@ int iRet; @\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* close links into vGroups or SDS */@\\ \mbox{}\verb@ if(pFile->iCurrentVG != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vdetach(pFile->iCurrentVG);@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@ if(pFile->iCurrentSDS != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDendaccess(pFile->iCurrentSDS);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: ending access to SDS");@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* close the SDS and Vgroup API's */@\\ \mbox{}\verb@ Vend(pFile->iVID);@\\ \mbox{}\verb@ iRet = SDend(pFile->iSID);@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot close SDS interface");@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@@\\ \mbox{}\verb@ iRet = Hclose(pFile->iVID);@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot close HDF file");@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* release memory */@\\ \mbox{}\verb@ NXIKillDir(pFile);@\\ \mbox{}\verb@ free(pFile);@\\ \mbox{}\verb@ return NX_OK; @\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXmakegroup} NXmakegroup creates a new Vgroup at the current level in the Vgroup hierarchy. The new Vgroup will have the name Vgroup and have the class descriptor NXclass. This call does not open the new group automatically. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap6} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXmakegroup(NXhandle fid, char *name, char *class)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int32 iNew, iRet;@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ pFile = NXIassert(fid); @\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* create and configure the group */@\\ \mbox{}\verb@ iNew = Vattach(pFile->iVID,-1,"w"); @\\ \mbox{}\verb@ if(iNew < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF could not create Vgroup");@\\ \mbox{}\verb@ return NX_ERROR; @\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ Vsetname(iNew,name);@\\ \mbox{}\verb@ Vsetclass(iNew,class);@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* insert it into the hierarchy, when apropriate */@\\ \mbox{}\verb@ if(pFile->iCurrentVG != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = Vinsert(pFile->iCurrentVG,iNew);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ Vdetach(iNew);@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF failed to insert Vgroup");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXopengroup} NXopengroup opens an existing Vgroup for writing and reading data to it. This routine maintains the Vgroup stack. There are several possible situations. The first is that we are at root level (iCurrentVG = 0). The vGroup must be found and attatched to. Than the Stack has to be incremented. The next situation is, that we are already in a Vgroup. Than we have to find the new Vgroup, detach the current Vgroup and attach to the new one, thereby incrementing the stack. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap7} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@/*------------------------------------------------------------------------*/@\\ \mbox{}\verb@ NXstatus NXopengroup(NXhandle fid, char *name, char *class)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int32 iNew, iRef;@\\ \mbox{}\verb@ char pBuffer[256];@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ iRef = NXIFindVgroup(pFile,name,class);@\\ \mbox{}\verb@ if(iRef < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: Vgroup %s, class %s NOT found",name,class);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* are we at root level ? */@\\ \mbox{}\verb@ if(pFile->iCurrentVG == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pFile->iCurrentVG = Vattach(pFile->iVID,iRef,"w");@\\ \mbox{}\verb@ if(pFile->iCurrentVG < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: cannot attach to vGroup %s",name);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ pFile->iStackPtr++;@\\ \mbox{}\verb@ pFile->iStack[pFile->iStackPtr] = iRef;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vdetach(pFile->iCurrentVG);@\\ \mbox{}\verb@ pFile->iStackPtr++;@\\ \mbox{}\verb@ pFile->iStack[pFile->iStackPtr] = iRef;@\\ \mbox{}\verb@ pFile->iCurrentVG = Vattach(pFile->iVID,@\\ \mbox{}\verb@ pFile->iStack[pFile->iStackPtr],@\\ \mbox{}\verb@ "w");@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ NXIKillDir(pFile);@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXclosegroup} NXclosegroup closes an open Vgroup and travels one back in the Vgroup hierarchy. The usual hassle with the tons of different indices again. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap8} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXclosegroup(NXhandle fid)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* first catch the trivial case: we are at root and cannot get @\\ \mbox{}\verb@ deeper into a negative directory hierarchy (anti-directory)@\\ \mbox{}\verb@ */@\\ \mbox{}\verb@ if(pFile->iCurrentVG = 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIKillDir(pFile);@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else /* Sighhh. Some work to do */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* close the current VG and decrement stack */@\\ \mbox{}\verb@ Vdetach(pFile->iCurrentVG);@\\ \mbox{}\verb@ pFile->iStackPtr--;@\\ \mbox{}\verb@ if(pFile->iStackPtr <= 0) /* we hit root */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pFile->iStackPtr = 0;@\\ \mbox{}\verb@ pFile->iCurrentVG = 0;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* attach to the lower Vgroup */@\\ \mbox{}\verb@ pFile->iCurrentVG = Vattach(pFile->iVID,@\\ \mbox{}\verb@ pFile->iStack[pFile->iStackPtr],@\\ \mbox{}\verb@ "w");@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ NXIKillDir(pFile);@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXmakedata} NXmakedata creates a new scientific datset. As argument it takes the usual filehandle, Than there is an integer denoting the datatype. This needs to be one of the HDF defined type identifiers. Commonly used types are: DFNT\_FLOAT for 32 bit floats, DFNT\_INT32 for 32-bit integers, DFNT\_UINT8 for unsigned bytes (or characters). rank is the dimensionality of the data. The parameter dims is an integer array which has rank values. Each value denotes the length of the data in the dimensions. Note that this function does not open a dataset. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap9} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXmakedata(NXhandle fid, char *name, int datatype, int rank, @\\ \mbox{}\verb@ int dimensions[])@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int32 iNew;@\\ \mbox{}\verb@ char pBuffer[256];@\\ \mbox{}\verb@ int i, iRet;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* do some argument checking */@\\ \mbox{}\verb@ if( (datatype != DFNT_FLOAT32) && (datatype != DFNT_FLOAT64) &&@\\ \mbox{}\verb@ (datatype != DFNT_INT8) && (datatype != DFNT_UINT8) &&@\\ \mbox{}\verb@ (datatype != DFNT_INT16) && (datatype != DFNT_UINT16) &&@\\ \mbox{}\verb@ (datatype != DFNT_INT32) && (datatype != DFNT_UINT32))@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: unknown datatype specified for SDS %s",@\\ \mbox{}\verb@ name);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(rank <= 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: invalid rank specified for SDS %s",@\\ \mbox{}\verb@ name);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ for(i = 0; i < rank; i++)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ if(dimensions[i] <= 0 )@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,@\\ \mbox{}\verb@ "ERROR: invalid dimension %d, value %d given for SDS %s",@\\ \mbox{}\verb@ i,dimensions[i], name);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* behave nicely, if there is still an SDS open */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ SDendaccess(pFile->iCurrentSDS);@\\ \mbox{}\verb@ pFile->iCurrentSDS = 0;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* dataset creation */@\\ \mbox{}\verb@ iNew = SDcreate(pFile->iSID,name,datatype,rank,dimensions);@\\ \mbox{}\verb@ if(iNew < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: cannot create SDS %s, check argumenst",@\\ \mbox{}\verb@ name);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR; @\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ /* link into Vgroup, if in one */@\\ \mbox{}\verb@ if(pFile->iCurrentVG != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = Vaddtagref(pFile->iCurrentVG,DFTAG_SDG,SDidtoref(iNew));@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ iRet = SDendaccess(iNew);@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot end access to SDS");@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXopendata} NXopendata opens a scientific data set for further manipulation, i.e. reading and writing of data or getting information about it. The scientific dataset must exist. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap10} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXopendata(NXhandle fid, char *name)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int32 iNew; @\\ \mbox{}\verb@ char pBuffer[256];@\\ \mbox{}\verb@ int iRet;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* first find the reference number of the SDS */@\\ \mbox{}\verb@ iNew = NXIFindSDS(fid,name);@\\ \mbox{}\verb@ if(iNew < 0) @\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: SDS %s not found at this level",name);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* be nice: properly close the old open SDS silently if there is@\\ \mbox{}\verb@ still an SDS open.@\\ \mbox{}\verb@ */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDendaccess(pFile->iCurrentSDS);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = 1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot end access to SDS");@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* clear pending attribute directories first */@\\ \mbox{}\verb@ NXIKillDir(pFile);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* open the SDS */@\\ \mbox{}\verb@ iNew = SDreftoindex(pFile->iSID, iNew);@\\ \mbox{}\verb@ pFile->iCurrentSDS = SDselect(pFile->iSID,iNew);@\\ \mbox{}\verb@ if(pFile->iCurrentSDS < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF error opening SDS");@\\ \mbox{}\verb@ pFile->iCurrentSDS = 0;@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXclosedata} NXclosedata ends the access to the currently active scientific dataset. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap11} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXclosedata(NXhandle fid)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int iRet;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ if(pFile->iCurrentSDS != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDendaccess(pFile->iCurrentSDS);@\\ \mbox{}\verb@ pFile->iCurrentSDS = 0;@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot end access to SDS");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: no SDS open --> nothing to do");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ NXIKillDir(pFile); /* for attribute data */@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsection{Reading and writing} \label{ss:rw} Routines to read and write data and attributes to previously opened datasets: \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap12} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@NXstatus NXgetdata(NXhandle fileid, void* data);@\\ \mbox{}\verb@NXstatus NXgetslab(NXhandle fileid, void* data, int start[], int size[]);@\\ \mbox{}\verb@NXstatus NXgetattr(NXhandle fileid, char* name, char* data, int datalen);@\\ \mbox{}\verb@@\\ \mbox{}\verb@NXstatus NXputdata(NXhandle fileid, void* data);@\\ \mbox{}\verb@NXstatus NXputslab(NXhandle fileid, void* data, int start[], int size[]);@\\ \mbox{}\verb@NXstatus NXputattr(NXhandle fileid, char* name, char* data, int datalen);@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} Please note, that all data reading and writing routines require that the scientific dataset has been opened beforehand with NXopenadata. \subsubsection{NXgetdata} NXgetdata reads data from the currently open scientific data set into data. Please note, that memory overwrite occurs if the caller has not allocated enough memory to hold all the data available. There are functions in the section \ref{ss:meta} which allow to inquire the data size first. Note as well that the scientific dataset must have been opened with NXopendata before this function can succeed. Before it can do its job, NXgetdata has to get dimension information first. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap13} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXgetdata(NXhandle fid, void *data)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int32 iStart[MAX_VAR_DIMS], iEnd[MAX_VAR_DIMS];@\\ \mbox{}\verb@ NXname pBuffer;@\\ \mbox{}\verb@ int32 iRank, iAtt, iType;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* check if there is an SDS open */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: no SDS open");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* first read dimension information */@\\ \mbox{}\verb@ memset(iStart,0,MAX_VAR_DIMS*sizeof(int32));@\\ \mbox{}\verb@ SDgetinfo(pFile->iCurrentSDS,pBuffer,&iRank,iEnd,&iType,&iAtt);@\\ \mbox{}\verb@ /* actually read */@\\ \mbox{}\verb@ SDreaddata(pFile->iCurrentSDS,iStart,NULL,iEnd,data);@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXgetslab} NXgetslab reads a subset of the data in the current scientific data set. The start dimensions to read from are specified in iStart, the end in iEnd. The caller is responsable for allocation enough memory for data. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap14} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXgetslab(NXhandle fid, void *data, int iStart[], int iEnd[])@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* check if there is an SDS open */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: no SDS open");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* actually read */@\\ \mbox{}\verb@ SDreaddata(pFile->iCurrentSDS,iStart,NULL,iEnd,data);@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXgetattr} HDF has the concept of attributes. This is additional data which goes with usually an SDS. Such attributes are used to denotes axis, units etc. The other class of attributes are global attributes. This function reads such attributes. If an SDS is open, it reads the attributes associated with the SDS, else the it tries to find a global attribute. The data read is transfered to data, but maximum datalen bytes. The caller is responsible for allocating at least datalen bytes for data. In order to enable this scheme, NXgetattr has to read the first into an internal temporary buffer before it copies the data to the datat buffer provided. And discards with the temporary buffer. Note that searching for attributes is handled differently if attributes at global level are searched compared to searching attributes in an SDS. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap15} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXgetattr(NXhandle fid, char *name, char *data, int datalen)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int32 iNew;@\\ \mbox{}\verb@ void *pData = NULL;@\\ \mbox{}\verb@ int32 iLen, iType, iRet;@\\ \mbox{}\verb@ char pBuffer[256];@\\ \mbox{}\verb@ NXname pNam; @\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* find attribute */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* SDS attribute */@\\ \mbox{}\verb@ iNew = SDfindattr(pFile->iCurrentSDS,name);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* global attribute */@\\ \mbox{}\verb@ iNew = SDfindattr(pFile->iSID,name);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(iNew < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: attribute %s not found",name);@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* get more info, allocate temporary data space */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDattrinfo(pFile->iCurrentSDS,iNew,pNam,&iType,&iLen);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDattrinfo(pFile->iSID,iNew,pNam,&iType,&iLen);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: HDF could not read attribute info");@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ iLen = iLen*DFKNTsize(iType); @\\ \mbox{}\verb@ pData = (void *)malloc(iLen);@\\ \mbox{}\verb@ if(!pData)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: allocating memory in NXgetattr");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ memset(pData,0,iLen);@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* finally read the data */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDreadattr(pFile->iCurrentSDS,iNew,pData);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDreadattr(pFile->iSID,iNew,pData);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pBuffer,"ERROR: HDF could not read attribute data");@\\ \mbox{}\verb@ NXIReportError(NXpData,pBuffer);@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* copy data to caller */@\\ \mbox{}\verb@ memset(data,0,datalen);@\\ \mbox{}\verb@ if(datalen < iLen)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iLen = datalen - 1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ memcpy(data,pData,iLen);@\\ \mbox{}\verb@ free(pData);@\\ \mbox{}\verb@ return NX_OK; @\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXputdata} NXputdata copies data into the currently open scientific data set in the HDF file. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap16} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXputdata(NXhandle fid, void *data)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int32 iStart[MAX_VAR_DIMS], iEnd[MAX_VAR_DIMS], iStride[MAX_VAR_DIMS];@\\ \mbox{}\verb@ NXname pBuffer;@\\ \mbox{}\verb@ int32 iRank, iAtt, iType, iRet, i;@\\ \mbox{}\verb@ char pError[512];@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* check if there is an SDS open */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: no SDS open");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ @\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* first read dimension information */@\\ \mbox{}\verb@ memset(iStart,0,MAX_VAR_DIMS*sizeof(int32));@\\ \mbox{}\verb@ SDgetinfo(pFile->iCurrentSDS,pBuffer,&iRank,iEnd,&iType,&iAtt);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* initialise stride to 1 */@\\ \mbox{}\verb@ for(i = 0; i < iRank; i++)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iStride[i] = 1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* actually write */@\\ \mbox{}\verb@ iRet = SDwritedata(pFile->iCurrentSDS,iStart,iStride,iEnd,data);@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sprintf(pError,"ERROR: failure to write data to %s",pBuffer);@\\ \mbox{}\verb@ NXIReportError(NXpData,pError);@\\ \mbox{}\verb@ return NX_ERROR; @\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXputslab} NXputslab writes an subset of data as specified by iStart to iEnd into the currently open SDS. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap17} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXputslab(NXhandle fid, void *data, int iStart[], int iEnd[])@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int iRet;@\\ \mbox{}\verb@ int iStride[MAX_VAR_DIMS], i;@\\ \mbox{}\verb@ @\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* check if there is an SDS open */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: no SDS open");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* initialise stride to 1 */@\\ \mbox{}\verb@ for(i = 0; i < MAX_VAR_DIMS; i++)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iStride[i] = 1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* actually write */@\\ \mbox{}\verb@ iRet = SDwritedata(pFile->iCurrentSDS,iStart,iStride,iEnd,data);@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: writing slab failed");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXputattr} Nxputattr puts an attribute into an Nexus file.If an SDS is open, the SDS will get the attribute. If not, a global attribute will be generated. Attributes are name = value pairs. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap18} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXputattr(NXhandle fid, char *name, char *data, int datalen)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int iRet;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ if(pFile->iCurrentSDS != 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* SDS attribute */@\\ \mbox{}\verb@ iRet = SDsetattr(pFile->iCurrentSDS,name,DFNT_UINT8,@\\ \mbox{}\verb@ datalen,data);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* global attribute */@\\ \mbox{}\verb@ iRet = SDsetattr(pFile->iSID,name,DFNT_UINT8,@\\ \mbox{}\verb@ datalen, data);@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDf failed to store attribute ");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsection{Meta-data} \label{ss:meta} Routines to obtain meta-data and to iterate over component datasets and attributes. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap19} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@NXstatus NXgetinfo (NXhandle fileid, int* rank, int dimension[], int* datatype);@\\ \mbox{}\verb@NXstatus NXgetnextentry (NXhandle fileid, NXname name, NXname class, int* datatype);@\\ \mbox{}\verb@NXstatus NXgetnextattr(NXhandle fileid, NXname pName, int *iLength, int *iType);@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXgetinfo} NXgetinfo gets information about an SDS. rank is the dimesionality of the data, dimension will contain the size of the data in each dimension and datatype results to one of the HDF--datatypes. The SDS has to be open in order for this routine to work. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap20} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXgetinfo(NXhandle fid, int *rank,int dimension[], int *iType)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ NXname pBuffer;@\\ \mbox{}\verb@ int32 iAtt;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* check if there is an SDS open */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: no SDS open");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* read information */@\\ \mbox{}\verb@ SDgetinfo(pFile->iCurrentSDS,pBuffer,rank,dimension,iType,&iAtt);@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXgetnextentry} NXgetnextentry implements a directory search facility on the current Vgroup level. The first call will initialize Vgroup searching facilities and return information on the first data item in the list. Subsequent calls will yield information about the next item in the Vgroup. If the end of the list is reached, NXgetentry will return NX\_EOD. Before, the usual NX\_ERROR or NX\_OK are returned. Information returned is different for each type of data. For Vgroups the name and class of the Vgroup will be returned, iType will be 0. For scientific data sets, the name field will be the name, class will be SDS and iType will denote the number type of the scientific data set. For types not known to Nexus (but to HDF) both name and class will be set to "UNKNOWN". \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap21} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ int NXgetnextentry(NXhandle fid, NXname name, NXname class, int *datatype)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int iRet; @\\ \mbox{}\verb@ int32 iTemp, iD1, iD2, iA;@\\ \mbox{}\verb@ int32 iDim[MAX_VAR_DIMS];@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* first case to check for: no directory entry */@\\ \mbox{}\verb@ if(pFile->iRefDir == NULL)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = NXIInitDir(pFile);@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData, @\\ \mbox{}\verb@ "ERROR: no memory to store directory info");@\\ \mbox{}\verb@ return -1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* next case: end of directory */@\\ \mbox{}\verb@ if(pFile->iCurDir >= pFile->iNDir)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIKillDir(pFile);@\\ \mbox{}\verb@ return NX_EOD;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* next case: we have data! suppy it and increment counter */@\\ \mbox{}\verb@ if(pFile->iCurrentVG == 0) /* root level */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iTemp = Vattach(pFile->iVID,pFile->iRefDir[pFile->iCurDir],@\\ \mbox{}\verb@ "r");@\\ \mbox{}\verb@ if(iTemp < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot attach to Vgroup");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ Vgetname(iTemp,name);@\\ \mbox{}\verb@ Vgetclass(iTemp,class);@\\ \mbox{}\verb@ *datatype = DFTAG_VG;@\\ \mbox{}\verb@ pFile->iCurDir++;@\\ \mbox{}\verb@ Vdetach(iTemp);@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else /* in Vgroup */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ if(pFile->iTagDir[pFile->iCurDir] == DFTAG_VG) /* Vgroup */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iTemp = Vattach(pFile->iVID,pFile->iRefDir[pFile->iCurDir],@\\ \mbox{}\verb@ "r");@\\ \mbox{}\verb@ if(iTemp < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot attach to Vgroup");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ Vgetname(iTemp,name);@\\ \mbox{}\verb@ Vgetclass(iTemp,class);@\\ \mbox{}\verb@ *datatype = DFTAG_VG;@\\ \mbox{}\verb@ pFile->iCurDir++;@\\ \mbox{}\verb@ Vdetach(iTemp);@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else if( (pFile->iTagDir[pFile->iCurDir] == DFTAG_SDG) ||@\\ \mbox{}\verb@ (pFile->iTagDir[pFile->iCurDir] == DFTAG_NDG) )@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iTemp = SDreftoindex(pFile->iSID, @\\ \mbox{}\verb@ pFile->iRefDir[pFile->iCurDir]);@\\ \mbox{}\verb@ iTemp = SDselect(pFile->iSID,iTemp);@\\ \mbox{}\verb@ SDgetinfo(iTemp,name,&iA,iDim,&iD1, &iD2);@\\ \mbox{}\verb@ strcpy(class,"SDS");@\\ \mbox{}\verb@ *datatype = iD1;@\\ \mbox{}\verb@ SDendaccess(iTemp);@\\ \mbox{}\verb@ pFile->iCurDir++;@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else /* unidentified */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ strcpy(name,"UNKNOWN");@\\ \mbox{}\verb@ strcpy(class,"UNKNOWN");@\\ \mbox{}\verb@ pFile->iCurDir++;@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ return NX_ERROR; /* not reached */@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXgetnextattr} NXgetnextattr works very much like NXgetnextentry except, that it works on SDS attributes and not on Vgroup entries. This function allows to scan the names of attributes available. iLength will be filled with the length of the attributes data in byte. iType will be filled with the HDF type of the attribute. Be warned: this routine returns global attributes when no SDS is currently open. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap22} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@/*-------------------------------------------------------------------------*/@\\ \mbox{}\verb@ NXstatus NXgetnextattr(NXhandle fileid, NXname pName, @\\ \mbox{}\verb@ int *iLength, int *iType)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ int iRet;@\\ \mbox{}\verb@ int32 iPType, iCount;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fileid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* first check if we have to start a new attribute search */@\\ \mbox{}\verb@ if(pFile->iNDir == 0) @\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = NXIInitAttDir(pFile);@\\ \mbox{}\verb@ if(iRet == NX_ERROR)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* are we done ? */@\\ \mbox{}\verb@ if(pFile->iCurDir >= pFile->iNDir)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIKillDir(pFile);@\\ \mbox{}\verb@ return NX_EOD;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* well, there must be data to copy */@\\ \mbox{}\verb@ if(pFile->iCurrentSDS == 0) /* global attribute */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDattrinfo(pFile->iSID,pFile->iCurDir, pName, &iPType,&iCount); @\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDattrinfo(pFile->iCurrentSDS,pFile->iCurDir,@\\ \mbox{}\verb@ pName, &iPType,&iCount);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot read attribute info");@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ *iLength = iCount*DFKNTsize(iPType);@\\ \mbox{}\verb@ *iType = iPType;@\\ \mbox{}\verb@ pFile->iCurDir++;@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsection{ Handling of linking and group hierarchy} \label{ss:link} \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap23} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@NXlink NXgetgroupID(NXhandle fileid);@\\ \mbox{}\verb@NXlink NXgetdataID(NXhandle fileid);@\\ \mbox{}\verb@NXstatus NXmakelink(NXhandle fileid, NXlink link);@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{ NXgetgroupID} NXgetgroupID retrieves the ID and tag of the currently open Vgroup in an NXlink strcuture. In case of an error the iTag field of this structure will contain NX\_ERROR. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap24} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXlink NXgetgroupID(NXhandle fileid)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ NXlink sRes;@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ pFile = NXIassert(fileid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ if(pFile->iCurrentVG == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sRes.iTag = NX_ERROR;@\\ \mbox{}\verb@ return sRes;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sRes.iTag = DFTAG_VG;@\\ \mbox{}\verb@ sRes.iRef = pFile->iCurrentVG;@\\ \mbox{}\verb@ return sRes;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ /* not reached */@\\ \mbox{}\verb@ sRes.iTag = NX_ERROR;@\\ \mbox{}\verb@ return sRes;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{NXgetdataID} NXgetdataID retrieves the tag and reference number of the current data object. Returns NX\_ERROR if none is open. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap25} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXlink NXgetdataID(NXhandle fid)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@ NXlink sRes;@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ if(pFile->iCurrentSDS == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sRes.iTag = NX_ERROR;@\\ \mbox{}\verb@ return sRes;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ sRes.iTag = DFTAG_SDS;@\\ \mbox{}\verb@ sRes.iRef = SDidtoref(pFile->iCurrentSDS);@\\ \mbox{}\verb@ return sRes;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ sRes.iTag = NX_ERROR;@\\ \mbox{}\verb@ return sRes; /* not reached */@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{ NXlink} NXlink links a Vgroup or SDS into a Vgroup. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap26} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ NXstatus NXmakelink(NXhandle fid, NXlink sLink)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pFile;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ if(pFile->iCurrentVG == 0) /* root level, can not link here */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@@\\ \mbox{}\verb@ if(sLink.iTag == DFTAG_VG)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vinsert(pFile->iCurrentVG,sLink.iRef);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else @\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vaddtagref(pFile->iCurrentVG, sLink.iTag,sLink.iRef);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsection{Internal routines} There are a couple of internal Nexus API routines which are declared static and are not visible outside of the module. First a few defines. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap27} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@#include @\\ \mbox{}\verb@#include @\\ \mbox{}\verb@#include @\\ \mbox{}\verb@#include "fortify.h"@\\ \mbox{}\verb@#include "napi.h"@\\ \mbox{}\verb@@\\ \mbox{}\verb@#define NXMAXSTACK 50@\\ \mbox{}\verb@#define NXSIGNATURE 959697@\\ \mbox{}\verb@@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} MAXSTACK denotes the maximum depth of Vgroup stacking permissible. If there are problems, feel free to increase this value and recompile. 50 is probably fairly generous. NXSIGNATURE is just the signature which the Nexus API will check for on handles in order to prevent against tampering with the handles. NXMAXNAME is the number of characters permitted for names and Vgroup names. The internal function NXIassert will convert a filehandle to a pointer to a NexusFile datastructure. Furthermore, it will check the signature and throw an assertion, when the signature does not match. \subsubsection{NX error handling} As each piece of non trivial software the Nexus API needs a error handling policy. The policy implemented is as follows: The NX routines terminate when an invalid NXhandle has been specified. This is a serious programmer error. In any other case the NX routines are meant to complain, recover and present an NX-ERROR return to the higher level code. That code is than responsible to deal with the problem. The problem left is error reporting. This is done througout the code by a call to NXIReportError. This function takes as first parameter a pointer to void and as next parameter the string with the complaint. The default implementation of NXIReportError will print to stdout. However, there are environments where this strategy is not feasible because stdout is supported only in a very strange way. Think about operating system like MS-Windows, MAC-OS (going to die anyway) or other windowing systems. In order to cater for this there is an inofficial support function which allows to set both a pointer to a datastructure and a new function for error reporting. This is called NXMSetError. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap28} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@/*---------------------------------------------------------------------*/@\\ \mbox{}\verb@ static void NXNXNXReportError(void *pData, char *string)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ printf("%s \n",string);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@/*---------------------------------------------------------------------*/@\\ \mbox{}\verb@ static void *NXpData = NULL;@\\ \mbox{}\verb@ static void (*NXIReportError)(void *pData, char *string) =@\\ \mbox{}\verb@ NXNXNXReportError;@\\ \mbox{}\verb@/*---------------------------------------------------------------------*/@\\ \mbox{}\verb@ void NXMSetError(void *pData, void (*NewError)(void *pD, char *text))@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXpData = pData;@\\ \mbox{}\verb@ NXIReportError = NewError;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap29} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@/*--------------------------------------------------------------------*/@\\ \mbox{}\verb@ static pNexusFile NXIassert(NXhandle fid)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile pRes;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ assert(fid);@\\ \mbox{}\verb@ pRes = (pNexusFile)fid;@\\ \mbox{}\verb@ assert(pRes->iNXID == NXSIGNATURE);@\\ \mbox{}\verb@ return pRes;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{ Object finding dunctions} Routines for finding objects in an HDF file have to cope with two quirks in the HDF interface. The first is that the root level is no Vgroup. This implies that the root level is searched for objects using different routines than searching for objects in a Vgroup. The last one uses, of course, Vgroup interface routines. Finding routines have to cope with the plethora of different integer ID's for any given HDF object. See \ref{ididid} for more details. NXIFindVgroup searches the current Vgroup level in the file for the occurence of a Vgroup with a specified name and of a specified class. If no suitable Vgroup can be found NIXFinfVgroup returns -1, else the ID of the Vgroup. NXIFindVgroup has to cope with the plethora of different integer ID's for any give Vgroup. There is the Vgroup ID, than there is the reference number which can be obtained by Vgettagref and which can refer to any object in the HDF file. Additionally there is a Tag which denotes the type of the object. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap30} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@/*----------------------------------------------------------------------*/@\\ \mbox{}\verb@ static int32 NXIFindVgroup(pNexusFile pFile, char *name, char *class)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ int32 iNew, iRef, iTag;@\\ \mbox{}\verb@ int iN, i;@\\ \mbox{}\verb@ int32 *pArray = NULL;@\\ \mbox{}\verb@ NXname pText;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ assert(pFile);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ if(pFile->iCurrentVG == 0) /* root level */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* get the number and ID's of all lone Vgroups in the file */@\\ \mbox{}\verb@ iN = Vlone(pFile->iVID,NULL,0);@\\ \mbox{}\verb@ pArray = (int32 *)malloc(iN*sizeof(int32));@\\ \mbox{}\verb@ if(!pArray)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: out of memory in NXIFindVgroup");@\\ \mbox{}\verb@ return -1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ Vlone(pFile->iVID,pArray,iN);@\\ \mbox{}\verb@ @\\ \mbox{}\verb@ /* loop and check */@\\ \mbox{}\verb@ for(i = 0; i < iN; i++)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iNew = Vattach(pFile->iVID,pArray[i],"r");@\\ \mbox{}\verb@ Vgetname(iNew, pText);@\\ \mbox{}\verb@ if(strcmp(pText,name) == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vgetclass(iNew,pText);@\\ \mbox{}\verb@ if(strcmp(pText,class) == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* found !*/@\\ \mbox{}\verb@ Vdetach(iNew);@\\ \mbox{}\verb@ iNew = pArray[i];@\\ \mbox{}\verb@ free(pArray);@\\ \mbox{}\verb@ return iNew;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ Vdetach(iNew);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ /* nothing found */@\\ \mbox{}\verb@ free(pArray); @\\ \mbox{}\verb@ return -1; @\\ \mbox{}\verb@ } @\\ \mbox{}\verb@ else /* case in Vgroup */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iN = Vntagrefs(pFile->iCurrentVG);@\\ \mbox{}\verb@ for(i = 0; i < iN; i++)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vgettagref(pFile->iCurrentVG,i,&iTag, &iRef);@\\ \mbox{}\verb@ if(iTag == DFTAG_VG)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iNew = Vattach(pFile->iVID,iRef,"r");@\\ \mbox{}\verb@ Vgetname(iNew, pText);@\\ \mbox{}\verb@ if(strcmp(pText,name) == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vgetclass(iNew,pText);@\\ \mbox{}\verb@ if(strcmp(pText,class) == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* found !*/@\\ \mbox{}\verb@ Vdetach(iNew);@\\ \mbox{}\verb@ return iRef;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ Vdetach(iNew);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ } /* end for */ @\\ \mbox{}\verb@ } /* end else */ @\\ \mbox{}\verb@ /* not found */@\\ \mbox{}\verb@ return -1;@\\ \mbox{}\verb@ } @\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} NXIFindSDS searches the current Vgroup level for an SDS name. It returns the reference ID of the SDS when found and -1 when no SDS of this name can be found on this level. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap31} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ static int32 NXIFindSDS(NXhandle fid, char *name)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ pNexusFile self;@\\ \mbox{}\verb@ int32 iNew, iRet, iTag, iRef;@\\ \mbox{}\verb@ int32 i, iN, iA, iD1, iD2;@\\ \mbox{}\verb@ NXname pNam;@\\ \mbox{}\verb@ int32 iDim[MAX_VAR_DIMS];@\\ \mbox{}\verb@@\\ \mbox{}\verb@ self = NXIassert(fid);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ /* root level search */@\\ \mbox{}\verb@ if(self->iCurrentVG == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ i = SDfileinfo(self->iSID,&iN,&iA);@\\ \mbox{}\verb@ if(i < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData, "ERROR: failure to read file information");@\\ \mbox{}\verb@ return -1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ for(i = 0; i < iN; i++)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iNew = SDselect(self->iSID,i);@\\ \mbox{}\verb@ SDgetinfo(iNew,pNam,&iA,iDim,&iD2, &iD2);@\\ \mbox{}\verb@ if(strcmp(pNam,name) == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDidtoref(iNew);@\\ \mbox{}\verb@ SDendaccess(iNew);@\\ \mbox{}\verb@ return iRet;@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ SDendaccess(iNew);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ /* not found */@\\ \mbox{}\verb@ return -1;@\\ \mbox{}\verb@ }/* end root level */@\\ \mbox{}\verb@ else /* search in a Vgroup */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iN = Vntagrefs(self->iCurrentVG);@\\ \mbox{}\verb@ for(i = 0; i < iN; i++)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vgettagref(self->iCurrentVG,i,&iTag, &iRef);@\\ \mbox{}\verb@ if( (iTag == DFTAG_SDG) || (iTag == DFTAG_NDG) || @\\ \mbox{}\verb@ (iTag == DFTAG_SDS) )@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iNew = SDreftoindex(self->iSID, iRef);@\\ \mbox{}\verb@ iNew = SDselect(self->iSID,iNew);@\\ \mbox{}\verb@ SDgetinfo(iNew,pNam,&iA,iDim,&iD2, &iD2);@\\ \mbox{}\verb@ if(strcmp(pNam,name) == 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ SDendaccess(iNew);@\\ \mbox{}\verb@ return iRef;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ SDendaccess(iNew);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ } /* end for */ @\\ \mbox{}\verb@ } /* end Vgroup */@\\ \mbox{}\verb@ /* we get here, only if nothing found */@\\ \mbox{}\verb@ return -1; @\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \subsubsection{Helper routines for directory search} NXIInitDir initialises the directory data fields in the Nexus File structure for a subsequent directory request. Please note, that at root level only Vgroups will be searched. When searching SDS's at root level, all SDS's in the whole file seem to be returned. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap32} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ static int NXIInitDir(pNexusFile self)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ int i;@\\ \mbox{}\verb@ int32 iTag, iRef;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ if(self->iCurrentVG == 0) /* root level */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* get the number and ID's of all lone Vgroups in the file */@\\ \mbox{}\verb@ self->iNDir = Vlone(self->iVID,NULL,0);@\\ \mbox{}\verb@ self->iRefDir = (int32 *)malloc(self->iNDir*sizeof(int32));@\\ \mbox{}\verb@ if(!self->iRefDir)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: out of memory in NXIInitDir");@\\ \mbox{}\verb@ return -1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ Vlone(self->iVID,self->iRefDir,self->iNDir);@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ /* Vgroup level */@\\ \mbox{}\verb@ self->iNDir = Vntagrefs(self->iCurrentVG);@\\ \mbox{}\verb@ self->iRefDir = (int32 *)malloc(self->iNDir*sizeof(int32));@\\ \mbox{}\verb@ self->iTagDir = (int32 *)malloc(self->iNDir*sizeof(int32));@\\ \mbox{}\verb@ if( (!self->iRefDir) || (!self->iTagDir))@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: out of memory in NXIInitDir");@\\ \mbox{}\verb@ return -1;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ for(i = 0; i < self->iNDir; i++)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ Vgettagref(self->iCurrentVG,i,&iTag, &iRef);@\\ \mbox{}\verb@ self->iRefDir[i] = iRef;@\\ \mbox{}\verb@ self->iTagDir[i] = iTag;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ } @\\ \mbox{}\verb@ self->iCurDir = 0;@\\ \mbox{}\verb@ return 1;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} NXIKillDir removes all data associated with a directory search from memory. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap33} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ static void NXIKillDir(pNexusFile self)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ if(self->iRefDir)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ free(self->iRefDir);@\\ \mbox{}\verb@ self->iRefDir = NULL;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(self->iTagDir)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ free(self->iTagDir);@\\ \mbox{}\verb@ self->iTagDir = NULL;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ self->iCurDir = 0;@\\ \mbox{}\verb@ self->iNDir = 0;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} NXIInitAttDir will initialise the counters for reading either SDS or global attribute data. \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap34} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@/*-------------------------------------------------------------------------*/@\\ \mbox{}\verb@ static int NXIInitAttDir(pNexusFile pFile)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ int iRet;@\\ \mbox{}\verb@ int32 iData, iAtt, iRank, iType;@\\ \mbox{}\verb@ int32 iDim[MAX_VAR_DIMS];@\\ \mbox{}\verb@ NXname pNam;@\\ \mbox{}\verb@@\\ \mbox{}\verb@ pFile->iCurDir = 0;@\\ \mbox{}\verb@ if(pFile->iCurrentSDS != 0) /* SDS level */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDgetinfo(pFile->iCurrentSDS,pNam, &iRank, iDim,&iType, @\\ \mbox{}\verb@ &iAtt); @\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ else /* global level */@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ iRet = SDfileinfo(pFile->iSID,&iData,&iAtt); @\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ if(iRet < 0)@\\ \mbox{}\verb@ {@\\ \mbox{}\verb@ NXIReportError(NXpData,"ERROR: HDF cannot read attribute numbers");@\\ \mbox{}\verb@ pFile->iNDir = 0;@\\ \mbox{}\verb@ return NX_ERROR;@\\ \mbox{}\verb@ }@\\ \mbox{}\verb@ pFile->iNDir = iAtt;@\\ \mbox{}\verb@ return NX_OK;@\\ \mbox{}\verb@ }@\\ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} \end{minipage}\\[4ex] \end{flushleft} \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap35} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@/*---------------------------------------------------------------------------@\\ \mbox{}\verb@ Nexus API header file@\\ \mbox{}\verb@@\\ \mbox{}\verb@ copyleft: Mark Koennecke, March 1997 at LNS,PSI, Switzerland@\\ \mbox{}\verb@ Przemek Klosowski, U. of Maryland & NIST, USA @\\ \mbox{}\verb@@\\ \mbox{}\verb@ No warranties of any kind taken.@\\ \mbox{}\verb@----------------------------------------------------------------------------*/@\\ \mbox{}\verb@#ifndef NEXUSAPI@\\ \mbox{}\verb@#define NEXUSAPI@\\ \mbox{}\verb@@\\ \mbox{}\verb@#include @\\ \mbox{}\verb@@\\ \mbox{}\verb@typedef enum {NXACC_READ = DFACC_READ, @\\ \mbox{}\verb@ NXACC_RDWR = DFACC_RDWR, @\\ \mbox{}\verb@ NXACC_CREATE = DFACC_CREATE } NXaccess;@\\ \mbox{}\verb@typedef void * NXhandle;@\\ \mbox{}\verb@typedef int NXstatus;@\\ \mbox{}\verb@typedef char NXname[VGNAMELENMAX];@\\ \mbox{}\verb@@\\ \mbox{}\verb@#define NX_OK 1@\\ \mbox{}\verb@#define NX_ERROR 0@\\ \mbox{}\verb@#define NX_EOD -1@\\ \mbox{}\verb@/*-------------------------------------------------------------------------@\\ \mbox{}\verb@ HDF Datatype values for datatype parameters @\\ \mbox{}\verb@ in the Nexus API@\\ \mbox{}\verb@@\\ \mbox{}\verb@ DFNT_FLOAT32 32 bit float@\\ \mbox{}\verb@ DFNT_FLOAT64 64 nit float == double@\\ \mbox{}\verb@ DFNT_INT8 8 bit integer ==char, byte@\\ \mbox{}\verb@ DFNT_UINT8 8 bit unsigned integer@\\ \mbox{}\verb@ DFNT_INT16 16 bit integer@\\ \mbox{}\verb@ DFNT_UINT16 16 bit unsigned integer@\\ \mbox{}\verb@ DFNT_INT32 32 bit integer@\\ \mbox{}\verb@ DFNT_UINT32 32 bit unsigned integer@\\ \mbox{}\verb@@\\ \mbox{}\verb@ This list is a edited version of the one found in the HDF header file@\\ \mbox{}\verb@ htndefs.h. That source will always be the real reference, this is@\\ \mbox{}\verb@ documented here for your convenience.@\\ \mbox{}\verb@--------------------------------------------------------------------------*/ @\\ \mbox{}\verb@@\\ \mbox{}\verb@/*-----------------------------------------------------------------------@\\ \mbox{}\verb@ A non Nexus standars function to set an error handler @\\ \mbox{}\verb@*/@\\ \mbox{}\verb@ void NXMSetError(void *pData, void (*ErrFunc)(void *pD, char *text));@\\ \mbox{}\verb@@\\ \mbox{}\verb@#endif@\\ \mbox{}\verb@@\\ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] \end{flushleft} \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap36} \vspace{-1ex} \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@/*---------------------------------------------------------------------------@\\ \mbox{}\verb@ Nexus API implementation file.@\\ \mbox{}\verb@@\\ \mbox{}\verb@ For documentation see the Napi.tex file which comes with this @\\ \mbox{}\verb@ distribution.@\\ \mbox{}\verb@@\\ \mbox{}\verb@ copyleft: Mark Koennecke@\\ \mbox{}\verb@ Labor fuer Neutronenstreuung@\\ \mbox{}\verb@ Paul Scherrer Institut@\\ \mbox{}\verb@ CH-5232 Villigen-PSI@\\ \mbox{}\verb@ Switzerland@\\ \mbox{}\verb@ Mark.Koenencke@{\tt @}\verb@psi.ch@\\ \mbox{}\verb@ Przemek Klosowski@\\ \mbox{}\verb@ U. of Maryland & NIST @\\ \mbox{}\verb@ przemek.klosowski@{\tt @}\verb@nist.gov @\\ \mbox{}\verb@@\\ \mbox{}\verb@ no warranties of any kind, whether explicit or implied, taken.@\\ \mbox{}\verb@ Distributed under the GNU copyleft license as documented elsewhere.@\\ \mbox{}\verb@@\\ \mbox{}\verb@ March 1997@\\ \mbox{}\verb@@\\ \mbox{}\verb@ Version: 1.0@\\ \mbox{}\verb@----------------------------------------------------------------------------*/@\\ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] \end{flushleft} \end{document}