/**************************************************************** Copyright (C) 2000 Lucent Technologies All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the name of Lucent or any of its entities not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ****************************************************************/ /* Example of simplified table handlers for .bit and .abt files, */ /* both of which have the same binary format, which coincides with */ /* the one used by AMPL's builtin "bit" (binary table) handlers. */ /* The simplified reader does not permit column names to be */ /* reordered, does not permit "extra" columns, and only deals */ /* with the native binary format. */ /* The simplified writer makes no attempt to preserve any rows */ /* or columns it is not writing. */ /* * The essence of a table handler is to provide one or more table * readers and writers. This example provides one table reader, called * Read_ampl_abt, and one table writer, called Write_ampl_ab. They are * static functions (i.e., their names are known only in this file) * that are made known to AMPL in the call on add_table_handler in * funcadd(), which is defined at the end of this file. * * Aside from the routines just mentioned, the other routines defined * below are auxiliary routines that are helpful in this example. * * Recall that a table column may contain only numeric values, only * symbolic (string) values, or a mixture of numeric and string values. * Some of the complexity below is for dealing with string values. * * All string data are recorded in a single string table, which starts * with column names, followed by an array of column types (see comments * in the declaration of struct BIntaryTable_header below) and ends with * string values for each column. * * Numeric columns are simply arrays of numeric values. * String columns */ #include #include #include "funcadd.h" #include "arith.h" /* for Arith_Kind_ASL and Long */ #define TM(len) (*ae->Tempmem)(TI->TMI,len) static int cantopen(AmplExports *ae, TableInfo *TI, char *tname) { /* Report failure to open a file. */ sprintf(TI->Errmsg = (char*)TM(strlen(tname) + 24), "Cannot open \"%s\".", tname); return DB_Error; } static char* tabname(TableInfo *TI) { /* Recognize .abt and .bit table names */ /* and reject everything else. */ char *s, *tname; switch(TI->nstrings) { case 2: if (strcmp(TI->strings[0], "simple-bit")) return 0; tname = TI->strings[1]; break; case 1: tname = TI->strings[0]; break; default: return 0; /* reject */ } return !(s = strrchr(tname,'.')) || strcmp(s,".abt") && strcmp(s,".bit") ? 0 : tname; } typedef struct /* information for auxiliary routines */ Tinfo { AmplExports *ae; TableInfo *TI; char *tname; char **fields; char *buf; FILE *f; unsigned long buflen; } Tinfo; static int readerr(Tinfo *ti, char *msg) { /* report an error to AMPL */ AmplExports *ae = ti->ae; TableInfo *TI = ti->TI; int len = strlen(ti->tname) + strlen(msg); sprintf(TI->Errmsg = (char*)TM(len + 32), "Error reading file %s: %s.", ti->tname, msg); return DB_Error; } #ifndef Long /* 32-bit int */ #define Long int #endif typedef struct /* Start of .abt file */ BIntaryTable_header { char magic[12]; /* \0\nampl.bit\n\0 */ char arkind[4]; /* kind of arithmetic: "1\0\0\0" or "2\0\0\0" */ /* for little- or big-endian IEEE arithmetic */ Long arity; Long ncols; /* not counting first arity columns */ Long nrows; Long nrcols; /* number of real columns (nonzero DbCol.dval) */ Long nscols; /* number of symbolic columns (nonzero DbCol.sval) */ /* nrcols + nscols >= arity + ncols */ Long strtablen; /* length of string table following this header */ /* String table starts with an array of 1-character */ /* column descriptors: */ /* '1' = just real, '2' = just string, '3' = both. */ /* Then come arity + ncols column names. */ /* Then come the columns; for type '3', the real */ /* column comes first. String columns are arrays */ /* of Longs, 0 = NULL, 1 = Missing, >= 2 ==> next */ /* string table entry. */ } BIntaryTable_header; static int fwr(void *v, size_t vlen, Tinfo *ti, char *what) { /* write to .abt file, complaining about write failures */ AmplExports *ae; TableInfo *TI; int len; ae = ti->ae; if (fwrite(v, vlen, 1, ti->f) == 1) return 0; /* all went well */ /* Something went wrong; report failure... */ TI = ti->TI; len = strlen(ti->tname) + strlen(what); sprintf(TI->Errmsg = (char*)TM(len + 32), "could not write %s to file %s.", what, ti->tname); return DB_Error; } static char magic[12] = "\0\nampl.bit\n"; static int Write_ampl_abt(AmplExports *ae, TableInfo *TI) { /* .abt table writer */ BIntaryTable_header bh; DbCol *db, *dbe; Long *Lt, *Lt0, ns; Tinfo ti; char *Missing, *s, *s0, *s1, *se, **sp, **sp1; int i, nc; size_t Ld, Ls; unsigned long nr1; /* check for name.abt */ if (!(ti.tname = tabname(TI))) return DB_Refuse; /* Try to open the file */ if (!(ti.f = fopen(ti.tname,"wb"))) return cantopen(ae,TI,ti.tname); /* initialize ti and header (bh) */ ti.ae = ae; ti.TI = TI; memset(&bh, 0, sizeof(bh)); memcpy(bh.magic, magic, 12); i = sprintf(bh.arkind, "%d", Arith_Kind_ASL); while(i < 4) bh.arkind[i++] = 0; bh.arity = TI->arity; bh.ncols = TI->ncols; bh.nrows = (Long)TI->nrows; bh.nrcols = bh.nscols = 0; nc = (int)(bh.arity + bh.ncols); db = TI->cols; for(dbe = db + nc; db < dbe; db++) { if (db->dval) bh.nrcols++; if (db->sval) bh.nscols++; } /* Compute length of string table */ bh.strtablen = 2*nc + 1; sp = TI->colnames; sp1 = sp + nc; while(sp < sp1) bh.strtablen += strlen(*sp++); Missing = TI->Missing; if (bh.nscols) { for(db = TI->cols; db < dbe; db++) if ((sp = db->sval)) for(sp1 = sp + bh.nrows; sp < sp1;) if ((s = *sp++) && s != Missing) bh.strtablen += strlen(s) + 1; } /* nr1 := smallest even integer >= bh.nrows */ nr1 = bh.nrows; if (nr1 & 1) nr1++; /* round up strtablen */ bh.strtablen = (bh.strtablen + 7) & ~7; /* allocate string table */ if (bh.nscols) { Lt0 = (Long*)TM(nr1*sizeof(Long)); Lt0[nr1-1] = 0; /* in case it's not otherwise set */ } s = s0 = (char*)TM(bh.strtablen); se = s + bh.strtablen; /* record column types -- numeric, string, or both */ for(db = TI->cols; db < dbe; db++) { i = db->dval ? '1' : '0'; if (db->sval) i += 2; *s++ = i; } *s++ = 0; /* copy column names to string table */ sp = TI->colnames; for(db = TI->cols; db < dbe; db++) for(s1 = *sp++; (*s++ = *s1); s1++); if (bh.nscols) { /* copy string values to string table */ for(db = TI->cols; db < dbe; db++) if ((sp = db->sval)) for(sp1 = sp + bh.nrows; sp < sp1;) if ((s1 = *sp++) && s1 != Missing) while((*s++ = *s1)) s1++; } /* zero fill end of string table */ while(s < se) *s++ = 0; /* write the header and string table */ if (fwr(&bh, sizeof(bh), &ti, "header") || fwr(s0, bh.strtablen, &ti, "strings")) return DB_Error; /* write the data columns */ Ld = bh.nrows * sizeof(real); Ls = (size_t)(nr1 * sizeof(Long)); ns = nc; for(db = TI->cols; db < dbe; db++) { if (db->dval) /* numeric values for this column */ if (fwr(db->dval, Ld, &ti, "real column")) return DB_Error; if ((sp = db->sval)) { /* string values for this column */ Lt = Lt0; for(sp1 = sp + bh.nrows; sp < sp1; Lt++) { *Lt = 0; if ((s = *sp++)) *Lt = s == Missing ? 1 : ++ns; } if (fwr(Lt0, Ls, &ti, "string column")) return DB_Error; } } /* finish */ fclose(ti.f); return DB_Done; } static int frd(void *v, size_t vlen, Tinfo *ti, char *what) { /* Read from .abt file, complaining if the read fails. */ AmplExports *ae; TableInfo *TI; int len; ae = ti->ae; if (fread(v, vlen, 1, ti->f) == 1) return 0; TI = ti->TI; len = strlen(ti->tname) + strlen(what); sprintf(TI->Errmsg = (char*)TM(len + 32), "could not read %s from file %s.", what, ti->tname); return DB_Error; } static int Read_ampl_abt(AmplExports *ae, TableInfo *TI) { /* .abt table reader */ BIntaryTable_header bh; DbCol *db, *dbe; Long *si, *sie, *sinfo; Tinfo ti; char buf[64], *ct, *s, **sp, *stab; int a, i, nc, rs; real *r; size_t Lr, Ls, nr1; /* check for name.abt */ if (!(ti.tname = tabname(TI))) return DB_Refuse; /* Try to open the file */ if (!(ti.f = fopen(ti.tname,"rb"))) return cantopen(ae,TI,ti.tname); ti.ae = ae; ti.TI = TI; /* read header */ if (frd(&bh, sizeof(bh), &ti, "header")) return DB_Error; /* check header */ a = (int)bh.arity; if (a != TI->arity) { sprintf(buf, "header gives arity %d rather than %d", a, TI->arity); return readerr(&ti, buf); } nc = (int)bh.ncols; if (nc != TI->ncols) { sprintf(buf, "header says %d columns; expected %d", a + nc, a + TI->ncols); return readerr(&ti, buf); } /* read string table */ nc += a; ct = stab = (char*)TM(bh.strtablen); if (frd(stab, bh.strtablen, &ti, "string table")) return DB_Error; /* allocate memory for numeric and string columns */ nr1 = (size_t) bh.nrows; Lr = nr1 * sizeof(real); if (nr1 & 1) nr1++; Ls = nr1 * sizeof(Long); if (bh.nrcols) r = (real*)TM(Lr * bh.nrcols); if (bh.nscols) { sinfo = (Long*)TM(nr1*sizeof(Long)); sie = sinfo + bh.nrows; sp = (char**)TM(bh.nrows*bh.nscols*sizeof(char**)); } db = TI->cols; s = stab; /* skip past column types */ while(*s++); /* check column names */ for(i = 0; i < nc; i++) { if (strcmp(s,TI->colnames[i])) return readerr(&ti, "wrong column names"); while(*s++); } /* read the columns */ for(dbe = db + nc; db < dbe; db++) { rs = *ct++ - '0'; if (rs & 1) { /* read numeric values for this column */ if (frd(db->dval = r, Lr, &ti, "real data")) return DB_Error; r += bh.nrows; } else db->dval = 0; if (rs & 2) { /* read string table pointers for this column */ if (frd(si = sinfo, Ls, &ti, "symbol pointers")) return DB_Error; db->sval = sp; while(si < sie) switch(*si++) { case 0: *sp++ = 0; break; case 1: *sp++ = TI->Missing; break; default: *sp++ = s; while(*s++); } } else db->sval = 0; } /* send all rows to AMPL at once */ if ((*TI->AddRows)(TI, TI->cols, bh.nrows)) return DB_Error; /* indicate that we're done... */ return DB_Done; } void funcadd(AmplExports *ae) { /* description of .abt handlers */ static char info[] = "simple-bit\n" "Library file.bit and file.abt (binary table) handler:\n" "one or two strings (an optional 'simple-bit' and the file name,\n" "ending in \".bit\" or \".abt\") expected before \":[...]\"."; /* Inform AMPL about the .abt handlers */ add_table_handler(Read_ampl_abt, Write_ampl_abt, info, 0, 0); }