#include "storage.h" #include <fcntl.h> #include "ndbm.h" #include <stream.h> #include <iostream.h> #include <fstream.h> #include <string.h> #include <errno.h> #pragma implementation static struct DBM* obj_db; int var_found_on; /** the following number (MAX_OBJ_SIZE) needs some comment to prevent future *** troubles: to make diskbasing work fast, i need some static block of memory *** to shove the objects into before i send them off to disk. Unfortunately, *** this is kind of open-ended, so i am using a semi-arbitrary value of around *** .25Mb -- if you ever get an object to exceed .25Mb, you\'ll most likely get *** Seg.Faults, etc. ***/ #define MAX_OBJ_SIZE 100000 /** no objects over 100k **/ static char bigbuffer [MAX_OBJ_SIZE]; Object_Store::Object_Store(char* dbfilename, int init){ datum key; int o = -1; if (! (obj_db = dbm_open (dbfilename, (O_RDWR), 0777))) {compile (dbfilename); cout << "Compile successful.\n"; return;} key.dptr = (char*)&(o); /** i store the number of objects in key -1 **/ key.dsize = sizeof(o); key = dbm_fetch (obj_db , key); /** then retrieve these **/ if (! key.dptr) perror ("dbm_fetch"); UNPACK_INT (key.dptr, num); cout << "Number of objects in database: " << num << "\n"; allocated = init + num; oarray = new struct o_record [allocated]; for (o = 0 ; o < allocated ; o++) {oarray[o].flags = 0; oarray[o].optr = (Object*) NULL;} } void Object_Store::compile (char* dbfilename, int init){ datum key, dat; int obtotal = -1; ifstream *txtfile; char filename[40]; allocated = init; /** i just assume this is enough to compile the file with. **/ oarray = new struct o_record [init]; strcpy (filename , dbfilename); strcat (filename , ".txt"); txtfile = new ifstream (filename , ios::in); if (txtfile->fail()) {cout << "Could not find text file " << filename << "\n"; exit (1);} for (num = 0 ; txtfile->peek() != EOF && txtfile->peek() != '/' ; num++) {oarray[num] . flags = DIRTY_FLAG | ACTIVE_FLAG | LOADED_FLAG; cout << "Compiling object " << num << " from file " << filename << "\n"; oarray[num] . optr = new Object (txtfile, num);} if (! (obj_db = dbm_open (dbfilename, (O_RDWR | O_CREAT | O_EXCL), 0777))) {cout << "Could not create initial database\n"; exit (1);} update(); delete txtfile; } Object_Store::~Object_Store(){ int i; update(); for (i=0 ; i<num ; i++) if (oarray[i].flags & LOADED_FLAG) delete oarray[i].optr; delete oarray; dbm_close (obj_db); } Object* Object_Store::get(int index){ datum retdat; Object* outobj; long i, j; /** assuming 32 bit longs **/ if (!(index>=0 && index<num)) {cout << "Attempt to access object number out of range: Object_Store::get\n"; cout << "Object number: " << index << "\n"; return (Object*)NULL;} if (oarray[index].flags & LOADED_FLAG) {oarray[index].flags |= ACTIVE_FLAG; return oarray[index].optr;} i = 0; do {j = index | (i << 24); retdat.dptr = (char*) &(j); retdat.dsize = sizeof (j); retdat = dbm_fetch (obj_db , retdat); if (! retdat.dptr) {cout << "No data returned by index: " << index << "\n"; perror ("dbm_fetch"); return (Object*)NULL;} memcpy (& (bigbuffer[(i * dbm_block_size)]) , retdat.dptr , retdat.dsize); i++;} while (retdat.dsize == dbm_block_size); outobj = new Object (& (bigbuffer[0]) , index); oarray[index].flags = LOADED_FLAG | ACTIVE_FLAG; oarray[index].optr = outobj; return outobj; } void Object_Store::update (){ char* bufptr; long i, j, k, l; datum d, key; i = -1; key.dptr = (char*)&(i); key.dsize = sizeof (i); d.dptr = (char*)&(num); d.dsize = sizeof (num); if (dbm_store (obj_db , key , d , DBM_REPLACE) < 0) {cout << "Unable to store number of objects\n"; perror ("dbm_store"); exit (1);} for (i=0 ; i<num ; i++) {if (oarray [i].flags & ACTIVE_FLAG) {if (oarray[i].flags & DIRTY_FLAG) {bufptr = &(bigbuffer[0]); bufptr = oarray[i].optr->pack_object(bufptr); /**** slice it up into little dbm_block_size - pieces ****/ k = ((bufptr - &(bigbuffer[0]) + 1) / dbm_block_size + 1); for (j = 0 ; j < k ; j++) {l = i | (j << 24); key.dptr = (char*) &(l); key.dsize = sizeof(l); d.dptr = & (bigbuffer [j * dbm_block_size]); d.dsize = ((j < (k - 1)) ? dbm_block_size : ((bufptr - d.dptr + 1) % dbm_block_size)); if (dbm_store (obj_db , key , d , DBM_REPLACE) < 0) {perror ("dbm_store"); cout << "Unable to archive " << d.dsize << " bytes.\n"; exit (1);}} /** now make sure the object did not shrink ... **/ do {l = i | (j << 24); key.dptr = (char*) &(l); key.dsize = sizeof (l); j++;} while (! dbm_delete (obj_db , key));} oarray[i] . flags = LOADED_FLAG;} else if (oarray [i].flags == LOADED_FLAG) {oarray[i].flags = 0; delete oarray[i].optr; oarray[i].optr = (Object*) NULL;}} } void Object_Store::grow (int by){ struct o_record * temp; int i; allocated += by; temp = new struct o_record [allocated]; for (i=0 ; i<num ; i++) temp[i] = oarray[i]; delete oarray; oarray = temp; } #define CHECK_OBJ_RANGE(a) if ((a)<0 || (a)>num) \ {cout << "Object access out of range: " << (a) << "\n"; \ return 0;} #define CHECK_OBJ_RANGE_PTR(a) if ((a)<0 || (a)>num) \ {cout << "Object access out of range\n"; \ return NULL;} #define DIRTY(a) oarray[(a)].flags |= DIRTY_FLAG; int Object_Store::addvar (int obid, String* sym, Value* dat){ int retval; CHECK_OBJ_RANGE(obid); retval = (get(obid))->set_sym (dat, sym); DIRTY(obid); return retval; } int Object_Store::rmvar (int obid , String* sym){ int retval; CHECK_OBJ_RANGE(obid); retval = (get(obid))->rm_sym (sym); DIRTY(obid); return retval; } Value* Object_Store::listvars (int obid) { CHECK_OBJ_RANGE_PTR(obid); return (get(obid))->list_vars(); } #define lookup_one(a,b) ((get((a)))->lookup_var((b))) #define MAX_PLIST_LENGTH 250 static long plist [MAX_PLIST_LENGTH]; int Object_Store::fill_plist (int obid){ int psize, index, i, j; intlist* current_p; for (psize = 1, plist[index = 0] = obid ; index < psize ; index++) {if (plist[index] < 0) continue; current_p = (get(plist[index]))->parents; for (i=0 ; i < (current_p->size) ; i++) {if (plist[psize-1] != current_p->list[i]) {plist[psize] = current_p->list[i]; for (j=0 ; current_p->list[i] != plist[j] ; j++); if (j < psize) plist[j] = -1; psize++;}}} return psize; } Value* Object_Store::pass_vlookup (int obid, String* sym){ int psize, index; Value* retval; CHECK_OBJ_RANGE_PTR(obid); psize = fill_plist (obid); for (index = 1 ; index < psize ; index++) if (plist[index] >= 0) if (retval = lookup_one(plist[index] , sym)) {var_found_on = plist[index]; return retval;} var_found_on = -1; return (Value*)NULL; } Value* Object_Store::lookup_var (int obid, String* sym){ Value* retval; CHECK_OBJ_RANGE_PTR(obid); if (! (retval = lookup_one(obid, sym))) return pass_vlookup (obid, sym); var_found_on = obid; return retval; } Val_List* Object_Store::collect_var (int obid, String* sym){ int psize, index; Val_List* retval=NULL; Value* tempval; psize = fill_plist (obid); for (index = 0 ; index < psize ; index++) if (plist[index] >= 0) if (tempval = lookup_one(plist[index] , sym)) retval = new Val_List (tempval->grab(), retval); return retval; } int Object_Store::addcmd (int obid , Val_List* key , String* data){ int retval; CHECK_OBJ_RANGE(obid); retval = (get(obid))->add_cmd (key, data); DIRTY(obid); return retval; } int Object_Store::rmcmd (int obid , String* sym){ int retval; CHECK_OBJ_RANGE(obid); retval = (get(obid))->rm_cmd (sym); DIRTY(obid); return retval; } int Object_Store::purgecmds (int obid){ int retval; CHECK_OBJ_RANGE(obid); (get(obid))->purge_cmds(); DIRTY(obid); return 1; } #define match_one(a,b) ((get((a)))->match_cmd((b))) String* Object_Store::match_cmd (int obid , String* input){ String* retval; int psize, i; CHECK_OBJ_RANGE_PTR(obid); psize = fill_plist (obid); for (i=0 ; i < psize ; i++) if (plist[i] >= 0 && (retval = match_one(plist[i], input))) return retval; return NULL; } Value* Object_Store::listmatches (int obid, String* input){ Val_List* retlist = NULL; Val_List* templist; int psize, i; CHECK_OBJ_RANGE_PTR(obid); psize = fill_plist (obid); for (i=(psize-1) ; i >= 0 ; i--) if (plist[i] >= 0 && (templist = (get(plist[i]))->all_matches(input))) {templist->cat (retlist); retlist = templist;} return new Value (retlist); } Value* Object_Store::listcmds (int obid){ CHECK_OBJ_RANGE_PTR(obid); return (get(obid))->list_cmds(); } int Object_Store::clone_obj (int obid){ CHECK_OBJ_RANGE(obid); oarray[num].flags = LOADED_FLAG | ACTIVE_FLAG | DIRTY_FLAG; oarray[num].optr = new Object (num, obid); num++; if (num >= allocated) grow(); return (num-1); } Value* Object_Store::listparents (int obid){ CHECK_OBJ_RANGE_PTR(obid); return (get(obid))->get_parent_list(); } int Object_Store::inparents (int obid, int cparent){ return (get(obid))->parents->check_is_inlist (cparent); } int Object_Store::chparents (int obid, Val_List* newparents){ Value* tempval; int i, j, anc, ilist[MAXPARENTS+1]; intlist* current_p; CHECK_OBJ_RANGE(obid); for (i=0 ; newparents && i<MAXPARENTS ; i++ , newparents=newparents->next) {tempval = newparents->elem; ilist[i] = tempval->obj_val(); for (j=0 ; j<i ; j++) if (ilist[j] == ilist[i]) return 0; CHECK_OBJ_RANGE( ilist[i] );} ilist[i] = -1; /** check for cycles ... expand out the full parent tree and look for obid **/ memcpy (plist , ilist , (i * (sizeof (int)))); for (anc = 0 ; anc < i ; anc++) {if (plist[anc] == obid) return 0; current_p = (get(plist[anc]))->parents; memcpy ((plist + i) , current_p->list, (current_p->size * (sizeof (int)))); i += current_p->size;} i = (get(obid))->change_parents (ilist); DIRTY(obid); return i; } int Object_Store::obj_exists (int index){ return (index>=0 && index<num); } void Object_Store::dump_to_stdout(){ int i; Object* ob; for (i=0 ; i<num ; i++) {ob = get(i); ob->dump_to_stdout(); delete ob; oarray[i].flags = 0; oarray[i].optr = NULL;} }