Initializing help system before first use

User-defined drivers

Topics covered in this section:

The contents of this section is quite advanced material and we recommend its reading only to those who are interested in the Mosel Native Interface (NI).

In this section we discuss two examples of user-defined drivers, performing compression and code generation respectively. New drivers are defined via the NI, in the form of services. For every driver some or all of a list of standard I/O functions may be defined, depending on its purpose.

The full source code of both examples is provided with the examples of the Mosel distribution. This compression module zlib is also included in compiled form among the DSOs of the Mosel distribution. For more detail on the implementation of new I/O drivers see the `Mosel NI User Guide' and `Mosel NI Reference Manual'.

Example: compression

With the gzip I/O driver for file compression implemented by the example module zlib, compressed files may be used for Mosel input and output. For example, we may generate and run a compressed BIM file with the following command:

mosel comp mymodel.mos -c "example of compression" -o zlib.gzip:mymodel.gz
mosel run zlib.gzip:mymodel.gz

To use compressed data files in a model we may write:

initializations from "zlib.gzip:burglar.dat.gz"

Another use of this driver may be to generate compressed output files:

declarations
  Ar: array(R:range,S1:set of string) of real
end-declarations
diskdata(ETC_SKIPH+ETC_OUT+ETC_CSV+ETC_DATAFRAME, "zlib.gzip:out.csv.gz", Ar)

declarations
  myxml: xmldoc
end-declarations
save(myxml, "zlib.gzip:results.xml.gz")

The I/O driver gzip implements an interface to the gzip compression format of the ZLIB library. The complete example module zlib also implements a second driver, compress, for a different compression format defined by this library. The driver gzip works with physical files, compress is a more general driver that works with streams. The ZLIB library is required to execute this example: it can be found at http://www.zlib.org.

Implementation

A C program defining new I/O drivers has the following components

  • Interface structures: the DSO interface table, the table of services, the table of drivers, and a table of functions per driver.
  • Module initialization function
  • Implementation of driver access functions: opening and closing streams, standard reading and writing, special reading and writing for initializations blocks, error handling, deleting and moving files.

A driver must implement at least the stream open functionality and one of the standard or special reading or writing functions; everything else is optional.

The definition of the interface structures of module zlib is the following. The structure of all tables and the function prototypes are defined by the Mosel NI.

                                /* Functions of the 'gzip' driver */
static void *gzip_open(XPRMcontext ctx, int *mode, const char *fname);
static int gzip_close(XPRMcontext ctx, gzFile gzf, int mode);
static long gzip_read(XPRMcontext ctx, gzFile gzf, void *buffer,
                                                        unsigned long size);
static long gzip_write(XPRMcontext ctx, gzFile gzf, void *buffer,
                                                        unsigned long size);
static XPRMiofcttab iodrv_gzip[]=
        {
         {XPRM_IOCTRL_OPEN, gzip_open},
         {XPRM_IOCTRL_CLOSE, gzip_close},
         {XPRM_IOCTRL_READ, gzip_read},
         {XPRM_IOCTRL_WRITE, gzip_write},
         {XPRM_IOCTRL_INFO, "filename"},
         {0,NULL}
        };
                                /* Drivers of the zlib module */
static XPRMiodrvtab iodrv_zlib[]=
        {
         {"gzip", iodrv_gzip},
         {NULL,NULL}
        };
                                /* Table of services: only IO drivers */
static XPRMdsoserv tabserv[]=
        {
         {XPRM_SRV_IODRVS, iodrv_zlib}
        };
                                /* DSO interface: only services */
static XPRMdsointer dsointer=
        {
         0, NULL,
         0, NULL,
         0, NULL,
         sizeof(tabserv)/sizeof(mm_dsoserv), tabserv
        };

static XPRMnifct mm;            /* For storing Mosel NI function table */

The previous code extract is best read from bottom to top. It declares an object of type XPRMnifct to store the NI function table (to be retrieved during the initialization of the module). The main DSO interface table dsointer lists the functionality provided by this module: it only defines services. The table of services, tabserv, indicates which services are defined, namely new I/O drivers. The list of I/O drivers is given in the table iodrv_zlib. The functionality implemented by the I/O driver gzip is listed in the table iodrv_gzip. The function bodies of gzip_open, gzip_close, etc., are printed below. The string set with INFO will be used by the `help' command of Mosel to indicate the usage of this driver (it must be used with the name of a physical file, not with extended filenames).

The module initialization function performs the standard initialization of Mosel modules: retrieving the Mosel NI function table and the NI version number and returning the module DSO interface table to Mosel. In addition, we check the version of the ZLIB library.

DSO_INIT zlib_init(XPRMnifct nifct, int *interver, int *libver,
                     XPRMdsointer **interf)
{
 const char *zlv;

 mm=nifct;                      /* Save the table of Mosel NI functions */
 *interver=XPRM_NIVERS;         /* The interface version we are using */

                                /* We check the ZLIB library version here */
 zlv=zlibVersion();
 if((zlv==NULL)||(zlv[0]!=ZLIB_VERSION[0]))
 {
  mm->dispmsg(NULL,"ZLIB: wrong version (expect %s got %s).\n",ZLIB_VERSION,
                                                          (zlv!=NULL)?zlv:"0");
  return 1;
 }
 else
 {
  *libver=XPRM_MKVER(0,0,1);    /* The version of the module: 0.0.1 */
  *interf=&dsointer;            /* Our interface */
  return 0;
 }
}

Below are printed the functions implementing the gzip driver. Besides the obligatory `open' function the gzip driver implements the functions `closing a file', `reading' (= uncompress), and `writing' (= compress).

/**** Open a gzip-file for (de)compression ****/
static void *gzip_open(XPRMcontext ctx, int *mode, const char *fname)
{
 char cmode[16];
 int cml;

 cml=2;
 cmode[0]=((*mode)&XPRM_F_WRITE)?'w':'r';
 cmode[1]=((*mode)&XPRM_F_BINARY)?'b':'t';
 if((*mode)&XPRM_F_APPEND) cmode[cml++]='a';
 cmode[cml]='\0';
 return gzopen(fname,cmode);
}

/**** Close the gzip-file ****/
static int gzip_close(XPRMcontext ctx, gzFile gzf, int mode)
{ return gzclose(gzf); }

/**** Uncompress a block of data ****/
static long gzip_read(XPRMcontext ctx, gzFile gzf, void *buffer,
                                                        unsigned long size)
{ return gzread(gzf, buffer, size); }

/**** Compress a block of data ****/
static long gzip_write(XPRMcontext ctx, gzFile gzf, void *buffer,
                                                        unsigned long size)
{ return gzwrite(gzf, buffer, size); }

As mentioned earlier, the full zlib example module in the module examples of the Mosel distribution defines a second compression driver, compress, in a very similar way to what has been shown here for the gzip driver.

Example: code generation

In this section we present a driver that includes the output of Mosel into a C program. It will typically be used for generating a C program that includes the BIM file of a given model, for example using a command like

mosel comp mymodel.mos -o export.toC:mymodel.c

As with all Mosel I/O drivers, the toC driver may be combined with other drivers. For instance, we may use the compress driver of the zlib module to obtain a compressed BIM file within the generated C program:

mosel comp mymodel.mos -o zlib.compress:export.toC:mymodel.c

Note: The deploy module that comes as a part of the standard Mosel distribution has been designed for the specific task of generating a C program that includes the BIM file of a given model; the command

mosel comp mymodel.mos -o deploy.csrc:runmymodel.c

has the same purpose as the use of 'export.toC' shown above. However, the main interest of the deploy module is the possibility to generate immediately a standalone executable to run a model:

mosel comp mymodel.mos -o deploy.exe:runmymodel

Implementation

The module export that defines the toC I/O driver has the following components (common to all I/O driver modules):

  • Interface structures
  • Module initialization function
  • Implementation of driver access functions

The file export.c implementing the module export with the I/O driver toC starts with the definition of the C code template, divided into three parts:

static char prg_part1[]=
"#include <stdio.h>\n"
"#include \"xprm_rt.h\"\n\n"

"static unsigned int bimfile[]={";

static char prg_part2[]=
"0};\n\n"

"int main(int argc,char *argv[])\n"
"{\n"
" char modname[40];\n"
" XPRMmodel mod;\n"
" int rts;\n\n"

" rts=XPRMinit();\n"
" if((rts!=0)&&(rts!=32))\n"
" {\n"
"  char msg[512];\n\n"

"  XPRMgetlicerrmsg(msg,512);\n"
"  fprintf(stderr,\"%s\",msg);\n"
"  return 1;\n"
" }\n\n"

" sprintf(modname,\"mem:%p/%u\", bimfile,";

static char prg_part3[]=
");\n"
" if((mod=XPRMloadmod(modname,NULL))==NULL)\n"
"  return 2;\n"
" if(XPRMrunmod(mod,&rts,NULL))\n"
"  return 3;\n"
" else\n"
"  return rts;\n"
"}\n";

The generated output will be inserted in between parts 1 and 2, its size will be inserted in between parts 2 and 3.

The definition of the NI interface structures follows the same scheme like what we have seen in the previous example: the table of driver functions, the table of drivers, the table of services, the main DSO interface table, and an address for storing the NI function table. The toC driver implements `open', `close', and `write' functionality and works with generalized files.

static void *toc_open(XPRMcontext ctx, int *mode, const char *fname);
static int toc_close(XPRMcontext ctx, s_tocdata *td, int mode);
static long toc_write(XPRMcontext ctx, s_tocdata *td, char *buffer,
                                                        unsigned long size);
static XPRMiofcttab iodrv_toc[]=
        {
         {XPRM_IOCTRL_OPEN, (void *)toc_open},
         {XPRM_IOCTRL_CLOSE, (void *)toc_close},
         {XPRM_IOCTRL_WRITE, (void *)toc_write},
         {XPRM_IOCTRL_INFO, "extended_filename"},
         {0,NULL}
        };
                                 /* Drivers of module `export': toC */
static XPRMiodrvtab iodrv_export[]=
        {
         {"toC", iodrv_toc},
         {NULL,NULL}
        };
                                 /* Table of services: only I/O drivers */
static XPRMdsoserv tabserv[]=
        {
         {XPRM_SRV_IODRVS, iodrv_export}
        };
                                 /* DSO interface: only services */
static XPRMdsointer dsointer=
        {
         0,NULL,
         0,NULL,
         0,NULL,
         sizeof(tabserv)/sizeof(mm_dsoserv),tabserv
        };

static XPRMnifct mm;             /* For storing Mosel NI function table */

This module's initialization function performs the standard module initialization procedure where the module's interface structure is passed on to Mosel and the NI function table is saved for use by this module.

DSO_INIT export_init(XPRMnifct nifct, int *interver, int *libver,
                     XPRMdsointer **interf)
{
 mm=nifct;                       /* Save the table of Mosel NI functions */
 *interver=XPRM_NIVERS;          /* The interface version we are using */
 *libver=XPRM_MKVER(0,0,1);      /* The version of the module: 0.0.1 */
 *interf=&dsointer;              /* Our interface */
 return 0;
}

The functions implemented by this I/O driver are restricted to opening and closing a file and writing to a file. The `open' and `close' functions print the predefined part of the generated C file. The function for writing the binary part of the output (toc_write) is somewhat tricky since it needs to make sure that all bytes are aligned correctly. This function is called with a buffer as parameter. For every four bytes it prints the corresponding integer. Since the buffer size is not necessarily a multiple of 4 there may be a few bytes left over at the end of the buffer which must be carried over to the next call to this function or to the `close' function. Below we print the `open' and `close' functions. The definition of the `write' function is left out here, the interested reader may be reminded that the complete module source is a part of the Mosel module examples.

static void *toc_open(XPRMcontext ctx, int *mode, const char *fname)
{
 s_tocdata *td;
                                       /* First time: open actual file */
 if(mm->fopen(ctx,*mode,fname)<0)
 {
  *mode|=XPRM_F_SILENT;                /* Error message already displayed */
                                       /* switch so silent mode */
  return NULL;
 }
 else
 {
  td=(s_tocdata *)malloc(sizeof(s_tocdata));
  td->total=0;
  td->nb_remain=0;
  td->nb_printed=0;
  mm->printf(ctx, "%s\n", prg_part1);  /* Display the beginning of program */
  return td;
 }
}

static int toc_close(XPRMcontext ctx, s_tocdata *td, int mode)
{
 if(td->nb_remain>0)                   /* Send bytes to be written */
 {
  td->total+=td->nb_remain;
  for(;td->nb_remain<4;td->nb_remain++)
   td->map.c[td->nb_remain]=0;
  mm->printf(ctx, "%#x, ", td->map.i);
 }                                     /* and complete the program */
 mm->printf(ctx, "%s%u%s", prg_part2, td->total, prg_part3);
 return mm->fclose(ctx,mode);
}

These two functions work with the following structure for storing information concerning the printed output. Function `open' initializes the structure (td), function `write' copies the remaining bytes from every buffer into this structure and sets the counters, and function `close' checks whether there is anything left to be printed before completing and closing the output file.

typedef struct
        {
         unsigned int total;      /* Total number of bytes written so far */
         union
         {
          char c[4];
          unsigned int i;
         } map;                   /* Mapping between 4 chars and an integer */
         int nb_remain;           /* Number of bytes not yet written */
         int nb_printed;          /* Number of numbers written on the line */
        } s_tocdata;

© 2001-2024 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.