Skip to content

Domino Canonical Format

Chapter 15-1
Domino Canonical Format

Introduction

C API programs that run on non-Intel based platforms such as Macintosh and UNIX must convert certain types of data between host-specific format and Domino canonical format when accessing item data. This chapter explains when and how to perform this conversion.

C API programs for Windows do not need to perform host/canonical conversion. However, all API programs should perform host/canonical conversion because it makes the code more portable and does not increase the complexity of the code significantly. API programs for Windows can perform host/canonical conversion with no adverse side effects.


Audience

You should read this chapter if:

  1. You develop or plan to develop API programs for any platform other than Windows
  2. You are porting or plan to port API programs from OS/2 or Windows to any non-Intel platform, such as Unix.
  3. You would like to make your API programs portable to other platforms.


Domino Canonical Format

Domino canonical format is defined as Intel 8086 byte ordering with no pad bytes.

Domino and Notes store all data on disk in Domino canonical format. Domino and Notes also transmit all data over network communications links in Domino canonical format. This way, Domino running on one platform can communicate transparently with Domino or Notes running on a different platform. Since all NSF files have the same binary format regardless of the platform on which the file was created, a UNIX server (for example) can read an .NSF file created on a Windows workstation.

Generally, Domino insulates API programs from the details of canonical format. However, for certain data types, portable API programs must convert from host to canonical format before writing data to a Domino database and convert from canonical to host format after reading data from a Domino database.


Data Types that Require Conversion

The following data types require conversion:

TYPE_COMPOSITE
TYPE_COLLATION
TYPE_OBJECT
TYPE_VIEW_FORMAT
TYPE_ICON
TYPE_SIGNATURE
TYPE_SEAL
TYPE_SEALDATA
TYPE_SEAL_LIST
TYPE_WORKSHEET_DATA
TYPE_USERDATA
TYPE_QUERY
TYPE_ACTION
TYPE_ASSISTANT_INFO
TYPE_VIEWMAP_DATASET
TYPE_VIEWMAP_LAYOUT
TYPE_CALENDAR_FORMAT

For all other data types, the NSF subsystem performs the conversion automatically.

For example, let's say that the following structure is to be written out to an item of TYPE_COMPOSITE:

typedef struct {
    WORD    mem1;
    DWORD mem2;
} CDSTRUCT;
...

CDSTRUCT DNStruct;

On a UNIX, the mem2 member of DNStruct is aligned on a DWORD boundary. If you do not convert this structure to canonical format when copying it to the item buffer that is to be written to disk, then when Domino or Notes attempts to read this item, there will be two nonsense bytes where Domino expects to find the mem2 data.


When to Convert

Given item data of one of the above types:

  • Portable API programs must convert the item data to canonical format before calling NSFItemAppend or any function that appends item data to a note.
  • After calling NSFItemInfo or any equivalent function to read item data from a note, the API program must convert the item data from canonical format to host format before accessing the item data.


API programs generally do not need to perform host/canonical conversion when using higher-level functions such as NSFItemGetText, NSFItemSetText, or NSFItemGetNumber, which do not manipulate any of the data types that require conversion. Conversion is also unnecessary when using any of the "Easy" rich text routines, such as CompoundTextAddText.

Do not convert data stored in an item that is not one of the aforementioned data types. For example, do not convert a LIST structure stored in an item of TYPE_NOTELINK. Do convert a LIST structure stored in an item of TYPE_COMPOSITE.

How to Convert

The API provides three functions that allow API programs to convert data between host-specific format and Domino canonical format:

  • ODSReadMemory - Converts from Domino canonical format to host-specific format
  • ODSWriteMemory - Converts from host-specific format to Domino canonical format
  • ODSLength - Gets the length, in bytes, of a data structure in Domino canonical format


ODSReadMemory converts Domino data structures from Domino canonical format to machine-specific format and writes the results into a variable or memory buffer provided by the caller. Portable API programs use ODSReadMemory after reading item data of one of the types listed above and before accessing that data.

ODSWriteMemory converts Domino data structures from machine-specific format to Domino Canonical format and writes the results into a memory buffer provided by the caller. Portable API programs use ODSWriteMemory to prepare item data of one of the types listed above before appending the data to a note.

ODSLength returns the length, in bytes, of a data structure in Domino canonical format. Use this function when calculating the buffer size needed to contain data structures converted to Domino canonical format and when initializing the Length members of CD record header structures.

NOTE: These ODS functions are supported on all platforms. We recommend that you include these functions in the appropriate places in all API code to ensure that the code is platform-independent.

The HCL C API for Domino and Notes Reference provides detailed information about these functions.

These ODS functions perform different actions depending on the platform on which the API program is running. If your API program calls ODS functions in the proper places, it will be portable to both Intel and non-Intel architecture machines. On Intel architecture machines, these ODS functions are not strictly necessary, but if used properly will do no harm.


Example Using ODSWriteMemory

The sample API program DYNAMIC shows how to use ODSWriteMemory to convert Domino data structures from host format to canonical format.

The code fragment below adds a rich text field to a note. It creates the rich text data by setting up a buffer that consists of a series of CD records.

In order to create each CD record, DYNAMIC needs certain data structures. For example, it needs a variable of type CDPARAGRAPH. It defines these data structures as local variables. Note that when initializing the data structures, DYNAMIC sets the Length member of the record header to the ODS length of the resulting CD record. For example,

      para.Header.Length = (BYTE) ODSLength( _CDPARAGRAPH );


After initializing each data structure, it converts the data structure from host format to Domino canonical format when appending it to the buffer. The resulting buffer is entirely in Domino canonical format. The code then creates a rich text field by calling NSFItemAppend to append the buffer to a note.

Example Using ODSWriteMemory

                 
BYTE           rt_field;   / allocated rich-text field /
BYTE          
buff_ptr;   / position in allocated memory /
CDPABDEFINITION pabdef;     / rich-text paragraph style /
CDPARAGRAPH     para;       / rich-text paragraph header /
CDPABREFERENCE  ref;        / rich-text style reference /
CDTEXT          cdtext;     / rich-text text header /
char            szString1[] = "Hello world... ";
char            szString2[] = "So long world ";
WORD            wString1Len = strlen( szString1 );
WORD            wString2Len = strlen( szString2 );
FONTIDFIELDS   pFontID;    / font definitions in text header /
DWORD           rt_size;    /
size of rich-text field /


/
... steps missing ... /

rt_field = (BYTE
) malloc ( wBuffLen );

/ Keep a pointer to our current position in the buffer. /
         
buff_ptr = rt_field;
             
/ Initialize a CDPABDEFINITION structure./


pabdef.Header.Signature = SIG_CD_PABDEFINITION;
pabdef.Header.Length = ODSLength( _CDPABDEFINITION );
pabdef.PABID = PARA_STYLE_ID;
pabdef.JustifyMode = JUSTIFY_CENTER;
pabdef.LineSpacing = DEFAULT_LINE_SPACING;
pabdef.ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING;
pabdef.ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING;
pabdef.LeftMargin = DEFAULT_LEFT_MARGIN;
pabdef.RightMargin = DEFAULT_RIGHT_MARGIN;
pabdef.FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN;
pabdef.Tabs = DEFAULT_TABS;
pabdef.Tab[0] = DEFAULT_TAB_INTERVAL;
pabdef.Flags = 0;


/ Call ODSWriteMemory to convert the CDPABDEFINITION structure to
  Domino canonical format and write the converted structure into
  the buffer at location buff_ptr. This advances buff_ptr to the
  next byte in the buffer after the canonical format strucure.
/
   
ODSWriteMemory( &buff_ptr, _CDPABDEFINITION, &pabdef, 1 );


/ Put a paragraph header in the field. /
 
para.Header.Signature = SIG_CD_PARAGRAPH;
para.Header.Length = (BYTE) ODSLength( _CDPARAGRAPH );


ODSWriteMemory( &buff_ptr, _CDPARAGRAPH, &para, 1 );
   
/ Put a paragraph reference block in the field./


ref.Header.Signature = SIG_CD_PABREFERENCE;
ref.Header.Length = (BYTE) ODSLength( _CDPABREFERENCE );
ref.PABID = PARA_STYLE_ID;

ODSWriteMemory( &buff_ptr, _CDPABREFERENCE, &ref, 1 );


/ Add the CDTEXT record to the field. A CDTEXT record consists
  of a CDTEXT structure followed by a run of text. Initialize the
  CDTEXT structure by filling in the signature and the length.
  The CDTEXT structure also contains the font information that
  controls how Domino displays this first run of text.
/


cdtext.Header.Signature = SIG_CD_TEXT;
cdtext.Header.Length = ODSLength( _CDTEXT ) + wString1Len ;


pFontID = (FONTIDFIELDS ) &(cdtext.FontID);
   
pFontID->Face = FONT_FACE_SWISS;
pFontID->Attrib = ISBOLD;
pFontID->Color = FONT_COLOR_BLUE;
pFontID->PointSize = 24;


ODSWriteMemory( &buff_ptr, _CDTEXT, &cdtext, 1 );

/
Write the actual characters of this first text run to the buffer.
  Since the run of text may contain embedded null characters, use
  memcpy, not strcpy. No need to terminate the run of text with a
  null because the Header.Length member of the CDTEXT structure
  specifies the length explicitly.
/


memcpy( (char
)buff_ptr, szString1, wString1Len );
buff_ptr += wString1Len;
   

/ We are done filling the buffer with CD records. Now append the
  buffer to the note as a rich text field. First find the size of
  the buffer. Then add the rich-text field to the note by calling
  NSFItemAppend. NSFItemAppend copies the data out of the buffer
  specified by rt_field. Therfore, after calling NSFItemAppend, we
  can free the buffer.

 
/

rt_size = (DWORD)(buff_ptr - rt_field);

error = NSFItemAppend( note_handle,
           0,
           "RICH_TEXT", strlen("RICH_TEXT"),
           TYPE_COMPOSITE,
           rt_field, rt_size );



Example Using ODSReadMemory

The sample API program DUMPFONT shows how to use ODSReadMemory to convert Domino data structures from canonical format to host format. The code fragment below uses ODSReadMemory to convert a font table from canonical format to host format before printing the contents of the font table to the screen.

A font table is an item of TYPE_COMPOSITE. It consists of a CDFONTTABLE structure followed by one or more CDFACE structures. When an API program reads a CDFONTTABLE or CDFACE structure from the NSF subsystem, these structures are in canonical format. API programs must convert these canonical format data structures to host-specific format before attempting to access the members of the structures. The code below uses ODSReadMemory to perform this conversion.

Example Using ODSReadMemory

/**********
                                                                       
   FUNCTION:   ProcessOneNote
                                                                       
**********/

STATUS LNPUBLIC ProcessOneNote( void * phDB, DWORD NoteID )
{
   DBHANDLE    hDB;
   STATUS      error;
   NOTEHANDLE  hNote;
   BLOCKID bhThisItem;
   WORD    wType;
   BLOCKID bhValue;
   DWORD   dwLength;


    hDB =
( (DBHANDLE )phDB );

    if (error = NSFNoteOpen( hDB, NoteID, 0, &hNote))
   {
       printf( "Error: unable to open note.\n" );
       return( ERR(error) );
   }


    /
 Look for the "$Fonts" field within this note. /

    error = NSFItemInfo( hNote, ITEM_NAME_FONTS,
                                strlen (ITEM_NAME_FONTS),
                               &bhThisItem,  /
Item BLOCKID /
                               &wType,       /
Type         /
                               &bhValue,     /
Value BLOCKID /
                               &dwLength);   /
Value length /


    /
If no font table, just return./

    if (ERR(error) == ERR_ITEM_NOT_FOUND)
   {
       NSFNoteClose( hNote );
       return NOERROR;
   }
   else if (error)        
   {
       printf( "Error: unable to access item %d.\n", ITEM_NAME_FONTS );
       NSFNoteClose( hNote );
       return( ERR(error) );
   }


    EnumCompositeBuffer( bhValue, dwLength, PrintFontTable, NULL);

    NSFNoteClose( hNote );

    return NOERROR;
}

/
**********
                                                                       
   FUNCTION:   PrintFontTable
                                                                       
**********/


STATUS LNPUBLIC PrintFontTable( char  
RecordPtr,
                                 WORD   RecordType,
                                 DWORD  RecordLength,
                                 void  
Unused )
{
   void           pItemValue;
   CDFONTTABLE     cdFontTab;
   CDFACE          cdFace;
   WORD            wIndex;


    if (RecordType != SIG_CD_FONTTABLE)
   {
      /
EnumCompositeBuffer found a CD record in the "$Fonts" field
         that is not of type SIG_CD_FONTTABLE.  Unusual, but not fatal.
       /
       return NOERROR;
   }


   /
RecordPtr points to the item value in canonical format after the
     datatype word. The item value starts with a CDFONTTABLE structure.
     Call ODSReadMemory to convert this CDFONTTABLE to host format and
     store it in cdFontTab. ODSReadMemory increments pItemValue to
     point to the next byte in the input buffer after the CDFONTTABLE.
   */


    pItemValue = RecordPtr;
   ODSReadMemory( &pItemValue, _CDFONTTABLE, &cdFontTab, 1 );


    for (wIndex = 0; wIndex < cdFontTab.Fonts; wIndex++)
   {
       ODSReadMemory( &pItemValue, _CDFACE, &cdFace, 1 );
       printf( "    Font %d:\n", wIndex );
       printf( "       Face    = %d\n", cdFace.Face );
       printf( "       Family  = %d\n", cdFace.Family );
       printf( "       Name    = %s\n", cdFace.Name );
   }


    return (NOERROR);
}



Side-by-Side Comparison

The code fragments below show how part of the sample program DYNAMIC was modified in porting from Windows to Unix. The fragment on the left works only on Intel architecture platforms such as Windows. The fragment on the right is portable. It works on all platforms supported by Domino or Notes.

Example: DYNAMIC before porting to UNIXExample: DYNAMIC after porting to UNIX
BYTE             *rt_field;
BYTE             *buff_ptr;
CDPABDEFINITION  *def;

/* ... steps missing... */

/* Add a CDPABDEFINITION.*/

def = (CDPABDEFINITION *) buff_ptr;

def->Header.Signature =
    SIG_CD_PABDEFINITION;
def->Header.Length =

    sizeof(CDPABDEFINITION);

def->PABID = 1;
def->JustifyMode = JUSTIFY_CENTER;
def->LineSpacing =  DEFAULT_LINE_SPACING;
def->ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING;
def->ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING;
def->LeftMargin = DEFAULT_LEFT_MARGIN;
def->RightMargin = DEFAULT_RIGHT_MARGIN;
def->FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN;
def->Tabs = DEFAULT_TABS;
def->Tab[0] = DEFAULT_TAB_INTERVAL;
def->Flags = 0;


/* Advance the buffer pointer, so we know where to put the next object in
the buffer. */


buff_ptr += sizeof(CDPABDEFINITION);
BYTE           *rt_field;
BYTE           *buff_ptr;
CDPABDEFINITION pabdef;


/* .....steps missing ....*/
             
/* Init a CDPABDEFINITION*/

pabdef.Header.Signature =
    SIG_CD_PABDEFINITION;
pabdef.Header.Length =

    ODSLength( _CDPABDEFINITION );

pabdef.PABID = PARA_STYLE_ID;
pabdef.JustifyMode = JUSTIFY_CENTER;
pabdef.LineSpacing =  DEFAULT_LINE_SPACING;
pabdef.ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING;
pabdef.ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING;
pabdef.LeftMargin = DEFAULT_LEFT_MARGIN;
pabdef.RightMargin = DEFAULT_RIGHT_MARGIN;
pabdef.FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN;
pabdef.Tabs = DEFAULT_TABS;
pabdef.Tab[0] = DEFAULT_TAB_INTERVAL;
pabdef.Flags = 0;


/* Call ODSWriteMemory to convert the CDPABDEFINITION structure to
  Domino canonical format and write the converted structure into
  the buffer at location buff_ptr. This advances buff_ptr to the
  next byte in the buffer after the canonical format structure.
*/
   
ODSWriteMemory( &buff_ptr,

    _CDPABDEFINITION, &pabdef, 1 );