static char rcsver[] = "$Id: buffs.c,v 1.7 2000/07/07 17:15:22 saadat Exp $"; /** ** $Source: /tes/cvs/vanilla/buffs.c,v $ ** ** $Log: buffs.c,v $ ** Revision 1.7 2000/07/07 17:15:22 saadat ** Added support for Windows MS Visual C++ memory-mapped file support. ** Removed some unused variables from various files. ** ** Revision 1.6 2000/02/11 00:35:49 saadat ** Missed something in the last change. Fixed. ** ** Revision 1.5 2000/02/10 22:14:40 saadat ** Bug fix in V3.3.6 was not working properly, fixed. ** ** for(i=initial; (i #include #include #include "header.h" #include "mem.h" /* Creates a new TBLBUFF object for a given table object. User * supplies the record count for the buffer and the overlap count. * IMPORTANT: The overlap count MUST be at least two times the size of * the largest possible keyblock. */ PTR RefillTblBuff(TBLBUFF *b) { char *fname; if (b->buf != NULL) { MemUnMapFile(b->buf, b->len); if (b->varbuf) { MemUnMapFile(b->varbuf, b->varlen); b->varbuf = NULL; } #ifdef DEBUG /* Following debug info is misleading since b->fileidx may not always be sequential. It can take jumps of one or more. See find_jump() for details. */ /* fname = (char *)b->tbl->files->ptr[b->fileidx]; fprintf(stderr, "Unmapping %s\n", fname); */ #endif b->fileidx++; } if (b->fileidx >= b->tbl->files->number) { return(NULL); } if (b->frag) FreeFragment(b->frag); fname = (char *)b->tbl->files->ptr[b->fileidx]; b->frag = LoadFragment(fname, b->tbl); b->len = b->frag->sbuf.st_size; #ifdef DEBUG fprintf(stderr, "mapping: %s\n", fname); #endif b->buf = MemMapFile(NULL, b->len, PROT_READ, MAP_PRIVATE, fname, O_RDONLY, 0); if (b->buf == NULL){ fprintf(stderr, "vanilla: Memory Mapping %s failed. ", fname?fname:"(null)"); perror("Reason"); abort(); } b->curr = b->buf + b->frag->offset; b->end = b->buf + b->len; b->reclen = b->tbl->label->reclen; return(b->curr); } PTR GiveMeVarPtr(PTR raw, TABLE *table, int offset) { TBLBUFF *b = table->buff; struct stat sbuf; char *fname, buf[256], *p; if (b->varbuf == NULL) { fname = ((char **)(table->files->ptr))[b->fileidx]; strcpy(buf, fname); p = &buf[strlen(buf)-4]; strcpy(p, ".var"); if (stat(buf, &sbuf) != 0) { p = &buf[strlen(buf)-4]; strcpy(p, ".VAR"); /* try capital case */ if (stat(buf, &sbuf) != 0) { fprintf(stderr, "Unable to open var file: %s\n", buf); return(NULL); } } /* fd = open(buf, O_RDONLY | O_BINARY); */ b->varlen = sbuf.st_size; /* b->varbuf = mmap(NULL, b->varlen, PROT_READ, MAP_PRIVATE, fd, 0); */ b->varbuf = MemMapFile(NULL, b->varlen, PROT_READ, MAP_PRIVATE, buf, O_RDONLY, 0); if (b->varbuf == NULL){ fprintf(stderr, "vanilla: Memory Mapping %s failed. ", buf?buf:"(null)"); perror("Reason"); abort(); } } /* Saadat -- Feb 15, 1999 */ if (offset >= b->varlen){ fprintf(stderr, "Variable Pointer after EOF: File: %s. Aborting...", buf); abort(); } return(b->varbuf+offset); } short ConvertVaxVarByteCount(PTR raw, VARDATA *vdata) { char buf[4]; short s; memcpy(buf, raw, 2); s = ((short *)MSB2(buf))[0]; return(s); } TBLBUFF * NewTblBuff(TABLE *t) { TBLBUFF *b = calloc(1,sizeof(TBLBUFF)); b->tbl = t; if (RefillTblBuff(b) == NULL) return(NULL); return(b); } /* Returns the first record in a table. This will ONLY WORK if it's the * first operation performed on the table. Calling this function after * we've done find's on the table will give useless results. */ PTR GetFirstRec(TABLE * t) { if (t->buff == NULL) { t->buff = NewTblBuff(t); } return t->buff->curr; } /** **/ PTR find_jump(TABLE * t, FIELD * f, DATA d, PTR beg, PTR end, int deep) { TBLBUFF *b = t->buff; int refill = 0; /* Added by Mankan, 5/99 */ FRAGMENT *frag; char *fname; int fileidx; DATA ek; /* */ if (beg == NULL) { beg = GetFirstRec(t); b = t->buff; } if (end == NULL) { end = b->end; refill = 1; } while (1) { /* Added by Mankan 5/99 */ /* ** See if this record can even be in this fragment. ** This is terribly slow because it has to load the ODL ** header of each file. A file index would speed this up a lot */ if (deep==0) { frag = b->frag; ek = *((DATA **)(frag->end_keys->ptr))[0]; if (CompareData(ek,d,f) < 0 ) { for(fileidx = b->fileidx+1; fileidx < b->tbl->files->number; fileidx++){ fname = (char *)b->tbl->files->ptr[fileidx]; frag = LoadFragment(fname, b->tbl); /* The following line is quite restrictive */ ek = *((DATA **)(frag->end_keys->ptr))[0]; /* If a fragment containing the required key value (as stored in variable "d") is found, load it and continue with the processing */ if (CompareData(ek,d,f) >= 0){ FreeFragment(frag); break; } FreeFragment(frag); } if (fileidx < b->tbl->files->number){ /* A fragment potentially containing the required key is found, so unmap the previous fragment and load this instead. */ #ifdef DEBUG fprintf(stderr, "Unmapping %s...\n", ((char **)b->tbl->files->ptr)[b->fileidx]); #endif /* Save the fragment serial no (being referred to as fileidx here) that contains the required key value */ /* Note: RefillTblBuff() increments b->fileidx before using it, that's why b->fileidx is one less than the actual fileidx */ b->fileidx = fileidx - 1; /* Load data from the selected fragment */ beg = RefillTblBuff(b); end = b->end; } else { /* List of fragments exhausted */ #ifdef DEBUG fprintf(stderr, "find_jump[list-exhausted] %s...\n", ((char **)b->tbl->files->ptr)[b->tbl->files->number-1]); #endif return NULL; } } } /* */ while (beg < end && CompareData(ConvertFieldData(beg,f),d,f) < 0) { beg += b->reclen; #ifdef DEBUG fprintf(stderr, "find_jump[1], "); fprintf(stderr, "%d / %d %s\n", (beg - b->buf)/b->reclen, (end - b->buf)/b->reclen, b->tbl->files->ptr[b->fileidx]); #endif } if (beg == end) { if (refill) { if ((beg = RefillTblBuff(b)) == NULL) return(NULL); end = b->end; #ifdef DEBUG printf("find_jump[2], refill %s\n", (char *)b->tbl->files->ptr[b->fileidx]); #endif continue; } else { return(NULL); } } #ifdef DEBUG fprintf(stderr, "find_jump[3], match "); fprintf(stderr, "%d %s\n",(beg - b->buf)/b->reclen, b->tbl->files->ptr[b->fileidx]); #endif return(beg); } } PTR find_until(TABLE * t, FIELD * f, PTR beg, PTR end) { TBLBUFF *b = t->buff; DATA d = ConvertFieldData(beg, f); if (end == NULL) end = b->end; while ((beg += b->reclen) < end && EquivalentData(ConvertFieldData(beg, f), d, f)) { #ifdef DEBUG fprintf(stderr, "find_until: "); fprintf(stderr, "%d %s\n",(beg - b->buf)/b->reclen, b->tbl->files->ptr[b->fileidx]); #endif } return(beg); } /* */ PTR find_select(TABLE * t, PTR beg, PTR end) { DATA d; SELECT **s; int i, n, count = 0; TBLBUFF *b = t->buff; int refill = 0; PTR last = NULL; if (t->selects == NULL) return(beg); s = (SELECT **)t->selects->ptr; n = t->selects->number; if (end == NULL) { end = b->end; refill = 1; } i = 0; while (1) { while (beg < end) { d = ConvertData(beg + s[i]->start, s[i]->field); if (CompareData(d, s[i]->low, s[i]->field) < 0 || CompareData(d, s[i]->high, s[i]->field) > 0) { beg += b->reclen; continue; } /* selection matched */ if (beg != last) { last = beg; count = 0; } i = (i+1)%n; if (++count == n) { #ifdef DEBUG fprintf(stderr, "find_select[2]: match, "); fprintf(stderr, "%d %s\n",(beg - b->buf)/b->reclen, b->tbl->files->ptr[b->fileidx]); #endif return(beg); } } if (refill) { if ((beg = RefillTblBuff(b)) == NULL) return(NULL); end = b->end; #ifdef DEBUG fprintf(stderr, "find_select[3]: refill %s\n", b->tbl->files->ptr[b->fileidx]); #endif } else { return(NULL); } } } /** ** Get the maximum value of the next record across all tables. ** This function also moves us across a fragment boundary (and sets up the ** first fragment) when necessary. **/ DATA * maxFieldVal(SLICE * s, int dim, TABLE **tbl, DATA *maxValue) { int i, rv = 0; DATA value; PTR beg; for (i = 0; i < dim; i++) { if (s[i].party_key == NULL) continue; if (s[i].start_rec == NULL) { /* No buffer loaded yet */ s[i].start_rec = GetFirstRec(tbl[i]); } else if (s[i].start_rec == tbl[i]->buff->buf + tbl[i]->buff->len) { /* EOF of current bufer */ if ((beg = RefillTblBuff(tbl[i]->buff)) == NULL) return(NULL); s[i].start_rec = beg; } value = ConvertFieldData(s[i].start_rec, s[i].party_key); if (!rv) { *maxValue = value; rv++; } else if (CompareData(value, *maxValue, s[i].party_key) > 0) *maxValue = value; } return maxValue; } /* PTR find_select(TABLE * t, PTR beg, PTR end) find_select checks all the selects in a table, and returns the next record that satisfies. If we had an index for the file, we could join the relevant blocks for each field used, and have a list of rows ready to go. */