Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

SPKFile.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 #include <Basics.H>
00020 #include <FS.H>
00021 #include <FP.H>
00022 
00023 // cache-common
00024 #include <BitVector.H>
00025 #include <FV.H>
00026 #include <CompactFV.H>
00027 #include <TextIO.H>
00028 #include <Debug.H>
00029 
00030 // cache-server
00031 #include "SPKFileRep.H"
00032 #include "IntIntTblLR.H"
00033 #include "CacheEntry.H"
00034 #include "VPKFileChkPt.H"
00035 #include "SPKFile.H"
00036 
00037 using std::streampos;
00038 using std::ostream;
00039 using std::ifstream;
00040 using std::cerr;
00041 using std::endl;
00042 
00043 // Update Methods -------------------------------------------------------------
00044 
00045 bool SPKFile::Update(const VPKFileChkPt *chkpt, const BitVector *toDelete,
00046   /*OUT*/ BitVector* &exCommonNames, /*OUT*/ BitVector* &exUncommonNames,
00047   /*OUT*/ BitVector* &packMask, /*OUT*/ IntIntTblLR* &reMap,
00048   /*OUT*/ bool &isEmpty) throw ()
00049 /* REQUIRES ((chkpt != NULL) && chkpt->hasNewEntries) || (toDelete != NULL) */
00050 /* ENSURES (packMask == NULL) == (reMap == NULL) */
00051 /* See the spec for this method in "SPKFile.H". Here is an overview of how
00052    the method is implemented.
00053 
00054    First, all the entries in "this->entries" and in the checkpoint are
00055    scanned. During this scanning process, any entries named in "toDelete"
00056    are ignored. The scanning is done to compute:
00057 
00058    int totalEntries -- the total number of undeleted entries
00059    bool newChkptEntries -- does the checkpoint have any undeleted entries?
00060    BitVector newAllNames -- the union of all names in all undeleted entries
00061    BitVector newCommonNames -- the intersection of all names in all
00062      undeleted entries
00063 
00064    Comuting the intersection "newCommonNames" is a bit tricky. On the very
00065    first encountered entry, it is set to that entry's names. On all subsequent
00066    entries, it is computed as the intersection of its current value and each
00067    entry's names. The local variable "totalEntries" is used to decide which
00068    case to pursue.
00069 
00070    Second, the entries in "this->oldEntries" are updated according to
00071    "toDelete", "exCommonNames", "exUncommonNames", "packMask", and "reMap".
00072    That is, any deleted entries are removed from the table, their
00073    "uncommonNames" sets are updated and packed, and they are added back to the
00074    table under their (potentially new) common fingerprint. Similarly, the new
00075    entries in "chkpt" are added to "this->oldEntries". */
00076 {
00077     // initialize "chkpt" from this PKFile if necessary
00078     if (chkpt == (VPKFileChkPt *)NULL) {
00079         chkpt = this->CheckPoint();
00080         assert(!(chkpt->hasNewEntries));
00081     } else if (this->sourceFunc == (Text *)NULL) {
00082         // propogate "sourceFunc" if necessary
00083         this->sourceFunc = chkpt->sourceFunc;
00084     }
00085 
00086     // check precondition; initialize OUT parameters
00087     assert(chkpt->hasNewEntries || (toDelete != (BitVector *)NULL));
00088     exCommonNames = exUncommonNames = packMask = (BitVector *)NULL;
00089     reMap = (IntIntTblLR *)NULL;
00090 
00091     // compute new value for set of all names and set of common names
00092     /* Note: during the processing of ``common'' entries in "this->entries"
00093        and "chkpt->newCommon", the variables "newAllNames" and
00094        "newCommonNames" will actually contain the join and meet, respectively,
00095        of the entries' *uncommon* names, since all of these entries contain
00096        the common names by definition. After these entries have been
00097        processed, the "commonNames" are OR'ed into both variables if there
00098        were any common entries before processing the ``uncommon'' entries
00099        in "chkpt->newUncommon". */
00100     BitVector newAllNames, newCommonNames;
00101     int totalEntries = 0;        // total number of new entries
00102     bool thisDelOne = false;     // any entries deleted from this SPKFile?
00103     if (toDelete == (BitVector *)NULL) {
00104         // fast path: nothing will change, so compute "newAllNames",
00105         // "newCommonNames", and "totalEntries" from existing values
00106         newAllNames.SetInterval(0, this->allNames.len - 1);
00107         newAllNames -= this->commonNames;
00108         totalEntries = this->oldEntries.Size();
00109     } else {
00110         // scan "this->entries", skipping any to be deleted
00111         ScanCommonTable(this->oldEntries, toDelete,
00112           /*INOUT*/ newAllNames, /*INOUT*/ newCommonNames,
00113           /*INOUT*/ totalEntries, /*INOUT*/ thisDelOne);
00114     }
00115 
00116     bool newDelOne = false;   // any new entries deleted?
00117     int numCommonEntries = totalEntries; // number of new common entries (temp)
00118     int numStableEntries = totalEntries; // remember current count (temp)
00119 
00120     // scan "chkpt->newCommon" entries
00121     if (chkpt->newCommon.Size() > 0) {
00122         ScanCommonTable(chkpt->newCommon, toDelete,
00123           /*INOUT*/ newAllNames, /*INOUT*/ newCommonNames,
00124           /*INOUT*/ totalEntries, /*INOUT*/ newDelOne);
00125         numCommonEntries = totalEntries;
00126     }
00127 
00128     // include "commonNames" if there were any common entries
00129     if (numCommonEntries > 0) {
00130         newAllNames |= this->commonNames;
00131         newCommonNames |= this->commonNames;
00132     }
00133 
00134     // scan "chkpt->newUncommon" entries
00135     if (chkpt->newUncommon != (CE::List *)NULL) {
00136         ScanList(chkpt->newUncommon, toDelete,
00137           /*INOUT*/ newAllNames, /*INOUT*/ newCommonNames,
00138           /*INOUT*/ totalEntries, /*INOUT*/ newDelOne);
00139     }
00140 
00141     // does the checkpoint have any new entries?
00142     bool newChkptEntries = (totalEntries > numStableEntries);
00143     bool delAny = (thisDelOne || newDelOne);
00144 
00145     /* The new version of the PKFile will be empty if none of its original
00146        entries are preserved and if there are no new entries to add from
00147        the checkpoint. It is new if its "pkEpoch" is 0. */
00148     isEmpty = (totalEntries == 0);           // is new version empty?
00149     bool isNewPKFile = (this->pkEpoch == 0); // is this a new PKFile?
00150 
00151     // do nothing if the PKFile is unchanged
00152     /* The PKFile must be changed if there were new entries in the checkpoint
00153        to add, if some entries from the SPKFile must be deleted, or if some
00154        new entries were deleted. The latter is required so that the "pkEpoch"
00155        gets bumped even if there were no entries to add or delete from the
00156        file on disk. */
00157     bool pkfileModified = (newChkptEntries || delAny);
00158     if (!pkfileModified) return pkfileModified;
00159 
00160     // compute "exCommonNames", "exUncommonNames"
00161     if (isNewPKFile) {
00162         // PKFile is new; set "exUncommonNames" only
00163         /* NB: The code below (for the "!isNewPKFile" case) would do the
00164            exact same thing, but this is a more efficient implementation
00165            for the "isNewPKFile" case. */
00166         if (!(newCommonNames.IsEmpty())) {
00167           exUncommonNames = NEW_CONSTR(BitVector, (&newCommonNames));
00168         }
00169     } else if (isEmpty) {
00170         // the SPKFile is empty, so there are no more common names
00171         /* This is a special case that could be handled by the more general
00172            "else" branch that follows. */
00173       exCommonNames = NEW_CONSTR(BitVector, (&(this->commonNames)));
00174     } else {
00175         // set "bothCommon = (commonNames & newCommonNames)"
00176         BitVector bothCommon(&(this->commonNames));
00177         bothCommon &= newCommonNames;
00178         // Only set "exCommonNames" or "exUncommonNames" if they
00179         // would be non-empty.
00180         if (bothCommon < this->commonNames) {
00181           exCommonNames = NEW_CONSTR(BitVector, (&(this->commonNames)));
00182             *exCommonNames -= newCommonNames;
00183         }
00184         if (bothCommon < newCommonNames) {
00185           exUncommonNames = NEW_CONSTR(BitVector, (&newCommonNames));
00186             *exUncommonNames -= this->commonNames;
00187         }
00188     }
00189 
00190     // update "commonNames" if necessary
00191     bool commonNamesChanged =
00192       ((exCommonNames != NULL) || (exUncommonNames != NULL));
00193     if (commonNamesChanged) {
00194         this->commonNames = newCommonNames;
00195     }
00196 
00197     /* Note: at this point, "exCommonNames", "exUncommonNames", "commonNames",
00198        "newCommonNames", and "newAllNames" are all ``unpacked''. */
00199 
00200     if (isEmpty) {
00201         // reset data structures when the PKFile is empty
00202         // note: "this->commonNames" has already been set
00203         this->allNames.Reset();
00204         this->oldEntries.Init();
00205 
00206         // allocate empty "packMask", "reMap" so existing names in
00207         // the VPKFile will be deleted
00208         packMask = NEW(BitVector);
00209         reMap = NEW_CONSTR(IntIntTblLR, (/*sizeHint=*/ 0));
00210     } else {
00211         // The PKFile is non-empty; update its data structures
00212 
00213         // set "reMap" if some names are removed from "allNames"
00214         /* Note: this can only happen if some entries were deleted from this
00215            SPKFile or from the checkpoint (we have to consider the entries 
00216            in the checkpoint because we are comparing "newAllNames" with the
00217            names stored in the checkpoint). */
00218         if (delAny) {
00219             int nextAvail = newAllNames.NextAvail(/*setIt =*/ false);
00220             assert(nextAvail <= chkpt->allNamesLen);
00221             if (nextAvail < chkpt->allNamesLen) {
00222               // all least one of the bits in "newAllNames" is unset
00223               packMask = NEW_CONSTR(BitVector, (&newAllNames));
00224               reMap = NEW_CONSTR(IntIntTblLR,
00225                                  (/*sizeHint =*/ chkpt->allNamesLen));
00226               BVIter it(*packMask);
00227               for (int i = 0, k; it.Next(/*OUT*/ k); i++) {
00228                 // bit "k" in "newAllNames" -> bit "i" in new version
00229                 bool inTbl = reMap->Put(k, i);
00230                 assert(!inTbl);
00231               }
00232             }
00233         }
00234         assert((packMask == NULL) == (reMap == NULL));
00235         assert((packMask == NULL) || delAny);
00236 
00237         // update "allNames"
00238         // first, append any new names in the checkpoint
00239         for (int i = this->allNames.len; i < chkpt->allNamesLen; i++) {
00240             this->allNames.Append(chkpt->allNames->name[i]);
00241         }
00242         // next, pack the names if necessary
00243         this->allNames.Pack(packMask);
00244 
00245         // update "this->oldEntries" if necessary
00246         if (isNewPKFile) {
00247             // This is the first time this PKFile is being flushed, so we
00248             // don't have to update "oldEntries" at all
00249             assert(this->oldEntries.Size() == 0);
00250         } else if (thisDelOne || commonNamesChanged) {
00251             // Only update "this->oldEntries" if some of its entries are
00252             // to be deleted or if the set of common names have changed.
00253 
00254             // update "entries"
00255             assert(this->oldEntries.Size() > 0);
00256             UpdateCommonTable(this->oldEntries, toDelete, exCommonNames,
00257               exUncommonNames, packMask, reMap, /*unlazyTag=*/ true);
00258         }
00259 
00260         // add all new entries in "chkpt" to "this->oldEntries"
00261         if (chkpt->hasNewEntries) {
00262             AddEntries(*chkpt, toDelete, exCommonNames, exUncommonNames,
00263               packMask, reMap, /*unlazyTag=*/ true);
00264         }
00265 
00266         // pack "commonNames"
00267         this->commonNames.Pack(packMask);
00268     }
00269 
00270     // update epochs
00271     // Usually this->pkEpoch will already have been updated, but not
00272     //  in the case where this SPKFile does not already exist on disk.
00273     this->pkEpoch = chkpt->pkEpoch + 1;      // incr. checkpointed pkEpoch
00274     this->namesEpoch = chkpt->namesEpoch;    // store checkpointed names epoch
00275     if (reMap != NULL) {
00276         // bump names epoch since names have changed again by being collapsed
00277         this->namesEpoch++;
00278     }
00279     assert(pkfileModified);
00280     return pkfileModified;
00281 } // SPKFile::Update
00282 
00283 VPKFileChkPt *SPKFile::CheckPoint() throw ()
00284 {
00285   VPKFileChkPt *res = NEW(VPKFileChkPt);
00286 
00287   res->sourceFunc = this->sourceFunc;
00288   res->pkEpoch = this->pkEpoch;
00289   res->namesEpoch = this->namesEpoch;
00290   res->allNamesLen = this->allNames.len;
00291   res->allNames = &(this->allNames);
00292   return res;
00293 }
00294 
00295 void SPKFile::ScanCommonTable(const CFPEntryMap &cfpTbl,
00296   const BitVector *toDelete, /*INOUT*/ BitVector &uncommonJoin,
00297   /*INOUT*/ BitVector &uncommonMeet, /*INOUT*/ int &totalEntries,
00298   /*INOUT*/ bool &delOne) const throw ()
00299 {
00300     if (cfpTbl.Size() > 0) {
00301         CFPEntryIter iter(&cfpTbl);
00302         FP::Tag cfp; CE::List *curr;
00303         while (iter.Next(/*OUT*/ cfp, /*OUT*/ curr)) {
00304             ScanList(curr, toDelete, /*INOUT*/ uncommonJoin,
00305               /*INOUT*/ uncommonMeet, /*INOUT*/ totalEntries,
00306               /*INOUT*/ delOne);
00307         }
00308     }
00309 }
00310 
00311 void SPKFile::ScanList(const CE::List *head, const BitVector *toDelete,
00312   /*INOUT*/ BitVector &uncommonJoin, /*INOUT*/ BitVector &uncommonMeet,
00313   /*INOUT*/ int &totalEntries, /*INOUT*/ bool &delOne) const throw ()
00314 {
00315     for (const CE::List *curr = head; curr != NULL; curr = curr->Tail()) {
00316         CE::T *ce = curr->Head();
00317         if (toDelete != NULL && toDelete->Read(ce->CI())) {
00318             // skip this entry, since it is being deleted
00319             delOne = true;
00320         } else {
00321             // this entry is not marked for deletion
00322             uncommonJoin |= ce->UncommonNames();
00323             if (totalEntries > 0) {
00324                 // form intersection
00325                 uncommonMeet &= ce->UncommonNames();
00326             } else {
00327                 // first one seen; just assign it to start off
00328                 uncommonMeet = ce->UncommonNames();
00329             }
00330             totalEntries++;
00331         }
00332     }
00333 }
00334 
00335 CE::List* SPKFile::ExtractEntries(/*INOUT*/ CFPEntryMap &cfpTbl) throw ()
00336 {
00337     // move existing entries from the table to "res"
00338     CE::List *res = (CE::List *)NULL, *curr;
00339     FP::Tag cfp;
00340     CFPEntryIter iter(&cfpTbl);
00341     while (iter.Next(/*OUT*/ cfp, /*OUT*/ curr)) {
00342         while (curr != (CE::List *)NULL) {
00343           res = NEW_CONSTR(CE::List, (curr->Head(), res));
00344           curr = curr->Tail();
00345         }
00346     }
00347     // clear the table
00348     cfpTbl.Init();
00349     return res;
00350 }
00351 
00352 void SPKFile::UpdateCommonTable(/*INOUT*/ CFPEntryMap &cfpTbl,
00353   const BitVector *toDelete, const BitVector *exCommonNames,
00354   const BitVector *exUncommonNames, const BitVector *packMask,
00355   const IntIntTblLR *reMap, bool unlazyTag) throw ()
00356 /* REQUIRES cfpTbl.Size() > 0 */
00357 /* REQUIRES (packMask == NULL) == (reMap == NULL) */
00358 {
00359     // ensure pre-condition
00360     assert(cfpTbl.Size() > 0);
00361     assert((packMask == NULL) == (reMap == NULL));
00362 
00363     // move existing entries from the table to a list named "existing"
00364     CE::List *existing = ExtractEntries(cfpTbl);
00365 
00366     // Recompute fingerprints of existing entries, and add them
00367     // back into the table.
00368     UpdateList(cfpTbl, existing, toDelete, exCommonNames, exUncommonNames,
00369       packMask, reMap, unlazyTag);
00370 }
00371 
00372 void SPKFile::AddEntries(const VPKFileChkPt &chkpt, const BitVector *toDelete,
00373   const BitVector *exCommonNames, const BitVector *exUncommonNames,
00374   const BitVector *packMask, const IntIntTblLR *reMap, bool unlazyTag)
00375   throw ()
00376 /* REQUIRES (packMask == NULL) == (reMap == NULL) */
00377 {
00378     assert((packMask == NULL) == (reMap == NULL));
00379 
00380     // add "newCommon" entries (if any)
00381     if (chkpt.newCommon.Size() > 0) {
00382         assert(this->pkEpoch > 0);
00383         CFPEntryIter iter(&(chkpt.newCommon));
00384         FP::Tag cfp; CE::List *ents;
00385         while (iter.Next(/*OUT*/ cfp, /*OUT*/ ents)) {
00386             UpdateList(this->oldEntries, ents, toDelete, exCommonNames,
00387               exUncommonNames, packMask, reMap, unlazyTag);
00388         }
00389     }
00390 
00391     // add "newUncommon" entries
00392     /* Note: Since these entries are all ``uncommon'', their "uncomonNames"
00393        bit vectors are exactly their free variables, possibly including some
00394        of this entry's common names. Hence, we don't have to update their
00395        "uncommonNames" according to "exCommonNames" and "exUncommonNames".
00396        Instead, we must subtract off the new version of "commonNames". We do
00397        this by passing NULL for "exCommonNames" and "commonNames" for
00398        "exUncommonNames". */
00399     UpdateList(this->oldEntries, chkpt.newUncommon, toDelete,
00400       /*exCommonNames=*/ (BitVector *)NULL, /*exUncommonNames=*/ &commonNames,
00401       packMask, reMap, unlazyTag);
00402 }
00403 
00404 void SPKFile::UpdateList(/*INOUT*/ CFPEntryMap &cfpTbl,
00405   const CE::List *ents, const BitVector *toDelete,
00406   const BitVector *exCommonNames, const BitVector *exUncommonNames,
00407   const BitVector *packMask, const IntIntTblLR *reMap, bool unlazyTag)
00408   throw ()
00409 /* REQUIRES (packMask == NULL) == (reMap == NULL) */
00410 {
00411     for (/*SKIP*/; ents != (CE::List *)NULL; ents = ents->Tail()) {
00412         CE::T *ce = ents->Head();
00413         if ((toDelete == (BitVector *)NULL) || !(toDelete->Read(ce->CI()))) {
00414             // compute common fingerprint
00415             /* Note: this must be done *before* the entry is updated since
00416                "commonNames" has not been packed at this time. */
00417             FP::Tag *commonFP = ce->CombineFP(commonNames);
00418 
00419             // update the entry
00420             ce->Update(exCommonNames, exUncommonNames, packMask, reMap);
00421 
00422             // unlazy the cache entry's uncommon tag (if necessary)
00423             if (unlazyTag) ce->UnlazyUncommonFP();
00424 
00425             // add it to "cfpTbl"
00426             AddEntryToTable(cfpTbl, *commonFP, ce);
00427         }
00428     }
00429 }
00430 
00431 void SPKFile::AddEntryToTable(/*INOUT*/ CFPEntryMap &cfpTbl,
00432   const FP::Tag &cfp, CE::T *ce) const throw ()
00433 {
00434     CE::List *curr;
00435     if (!cfpTbl.Get(cfp, /*OUT*/ curr)) {
00436         curr = (CE::List *)NULL;
00437     }
00438     curr = NEW_CONSTR(CE::List, (ce, curr));
00439     (void)cfpTbl.Put(cfp, curr);
00440 }
00441 
00442 // Lookup Methods -------------------------------------------------------------
00443 
00444 /* static */
00445 CE::T* SPKFile::Lookup(ifstream &ifs, streampos origin,
00446   int version, const FP::Tag &commonTag, const FP::List &fps)
00447   throw (FS::EndOfFile, FS::Failure)
00448 /* REQUIRES FS::Posn(ifs) == origin == ``start of <PKFile>'' */
00449 {
00450     // verify precondition
00451     assert(FS::Posn(ifs) == origin);
00452 
00453     // read header
00454     SPKFileRep::Header hdr(ifs, origin, version, /*readEntries=*/ false);
00455 
00456     // seek to proper <PKEntries>
00457     streampos offset = LookupCFP(ifs, origin, version, commonTag, hdr);
00458     if (offset < 0) return (CE::T *)NULL;
00459     FS::Seek(ifs, origin + offset);
00460 
00461     // lookup result based on "version"
00462     CE::T *res;
00463     if (version <= SPKFileRep::SourceFuncVersion) {
00464         res = LookupEntryV1(ifs, fps);
00465     } else {
00466         // no support for other versions
00467         assert(false);
00468     }
00469     return res;
00470 }
00471 
00472 /* static */
00473 streampos SPKFile::LookupCFP(ifstream &ifs, streampos origin,
00474   int version, const FP::Tag &cfp, const SPKFileRep::Header &hdr)
00475   throw (FS::EndOfFile, FS::Failure)
00476 /* REQUIRES FS::Posn(ifs) == ``start of <CFPTypedHeader>'' */
00477 {
00478     streampos res;
00479     switch (hdr.type) {
00480       case SPKFileRep::HT_List:
00481         if (version <= SPKFileRep::SourceFuncVersion) {
00482             res = LookupCFPInListV1(ifs, origin, cfp, hdr.num);
00483         } else {
00484             // no support for other versions
00485             assert(false);
00486         }
00487         break;
00488       case SPKFileRep::HT_SortedList:
00489         if (version <= SPKFileRep::SourceFuncVersion) {
00490             res = LookupCFPInSortedListV1(ifs, origin, cfp, hdr.num);
00491         } else {
00492             // no support for other versions
00493             assert(false);
00494         }
00495         break;
00496       case SPKFileRep::HT_HashTable:
00497       case SPKFileRep::HT_PerfectHashTable:
00498         /*** NYI ***/
00499         assert(false);
00500       default:
00501         assert(false);
00502     }
00503     return res;
00504 }
00505 
00506 /* static */
00507 streampos SPKFile::LookupCFPInListV1(ifstream &ifs, streampos origin,
00508   const FP::Tag &cfp, int num) throw (FS::EndOfFile, FS::Failure)
00509 /* REQUIRES FS::Posn(ifs) == ``start of <CFPSeqHeader>'' */
00510 {
00511     for (int i = 0; i < num; i++) {
00512         SPKFileRep::HeaderEntry he(ifs, origin);
00513         if (he.cfp == cfp) {
00514             // found it!
00515             return he.offset;
00516         }
00517     }
00518     return (-1);
00519 }
00520 
00521 /* static */
00522 streampos SPKFile::LookupCFPInSortedListV1(ifstream &ifs, streampos origin,
00523   const FP::Tag &cfp, int num) throw (FS::EndOfFile, FS::Failure)
00524 /* REQUIRES FS::Posn(ifs) == ``start of <CFPSeqHeader>'' */
00525 {
00526     // lookup the entry using binary search
00527     int lo = 0, hi = num;
00528     streampos base = FS::Posn(ifs); // record start of entries on disk
00529     while (lo < hi) {
00530         // Loop invariant: Matching entry (if any) has index in [lo, hi).
00531         // On each iteration, the difference "hi - lo" is guaranteed to
00532         // decrease, so the loop is guaranteed to terminate.
00533 
00534         // read middle entry
00535         int mid = (lo + hi) / 2;
00536         assert(lo <= mid && mid < hi); // mid is in [lo, hi)
00537         FS::Seek(ifs, (base +
00538                        (streampos) (mid * SPKFileRep::HeaderEntry::Size())));
00539         SPKFileRep::HeaderEntry midEntry(ifs, origin);
00540 
00541         // compare middle CFP to "cfp"
00542         int cmp = Compare(cfp, midEntry.cfp);
00543         if (cmp < 0) {
00544             // matching entry in [lo, mid) => make "hi" smaller
00545             hi = mid;
00546         } else if (cmp > 0) {
00547             // matching entry in [mid+1, hi) => make "lo" larger
00548             lo = mid + 1;
00549         } else {
00550             return (streampos)(midEntry.offset);
00551         }
00552     }
00553     return (-1);
00554 }
00555 
00556 /* static */
00557 CE::T* SPKFile::LookupEntryV1(ifstream &ifs, const FP::List &fps)
00558   throw (FS::EndOfFile, FS::Failure)
00559 /* REQUIRES FS::Posn(ifs) == ``start of <PKEntries>'' */
00560 {
00561     // read number of entries
00562     SPKFileRep::UShort numEntries;
00563     FS::Read(ifs, (char *)(&numEntries), sizeof(numEntries));
00564 
00565     // look for match on secondary key
00566     for (int i = 0; i < numEntries; i++) {
00567         // read the next entry
00568       CE::T *ce = NEW_CONSTR(CE::T, (ifs));
00569         assert(ce->UncommonFPIsUnlazied());
00570         /* Note: no lock is required on the following "FPMatch"
00571            operation because "ce" is fresh. */
00572         if (ce->FPMatch(fps)) {
00573             return ce;
00574         }
00575     }
00576     return (CE::T *)NULL;
00577 }
00578 
00579 // Read/Write Methods ---------------------------------------------------------
00580 
00581 void SPKFile::Read(ifstream &ifs, streampos origin, int version,
00582   /*OUT*/ SPKFileRep::Header* &hdr, bool readEntries, bool readImmutable)
00583   throw (FS::EndOfFile, FS::Failure)
00584 /* REQUIRES FS::Posn(ifs) == ``start of <PKFile>'' */
00585 {
00586     // verify that we are positioned correctly
00587     assert(FS::Posn(ifs) == origin);
00588 
00589     // read/skip <CFPHeader>
00590     if (readEntries) {
00591       // read header and its corresponding header entries
00592       hdr = NEW_CONSTR(SPKFileRep::Header,
00593                        (ifs, origin,
00594                         version, /*readEntries=*/ true));
00595     } else {
00596         hdr = (SPKFileRep::Header *)NULL;
00597         SPKFileRep::Header::Skip(ifs, origin, version);
00598     }
00599 
00600     // read <PKFHeaderTail>
00601     if (version >= SPKFileRep::SourceFuncVersion) {
00602       Text *temp = NEW(Text);  // non-const
00603       TextIO::Read(ifs, /*OUT*/ *temp);
00604       this->sourceFunc = temp;
00605       if (this->sourceFunc->Length() == 0) this->sourceFunc = (Text *)NULL;
00606     } else {
00607         this->sourceFunc = (Text *)NULL;
00608     }
00609     FS::Read(ifs, (char *)(&(this->pkEpoch)), sizeof(this->pkEpoch));
00610     FS::Read(ifs, (char *)(&(this->namesEpoch)), sizeof(this->namesEpoch));
00611     CompactFV::List compactFV(ifs); // read CompactFV::List from file
00612     this->allNames.Grow(/*sizeHint=*/ compactFV.num + 5);
00613     compactFV.ToFVList(/*INOUT*/ this->allNames); // convert to "allNames"
00614     this->commonNames.Read(ifs);
00615 
00616     // read <PKEntries>* (if necessary)
00617     if (readEntries) {
00618         // iterate over the CFP groups
00619         for (int i = 0; i < hdr->num; i++) {
00620             // check that we are positioned correctly
00621           assert(FS::Posn(ifs) == origin + (streampos) hdr->entry[i].offset);
00622 
00623             // read <PKEntries> record
00624             CE::List *entryList = this->ReadEntries(ifs, readImmutable);
00625 
00626             // install the entries in "this->oldEntries" table
00627             bool inTbl = this->oldEntries.Put(hdr->entry[i].cfp, entryList);
00628             assert(!inTbl);
00629         }
00630     }
00631 }
00632 
00633 CE::List *SPKFile::ReadEntries(ifstream &ifs, bool readImmutable)
00634   throw (FS::EndOfFile, FS::Failure)
00635 /* REQUIRES FS::Posn(ifs) == ``start of <PKEntries>'' */
00636 {
00637     // read number of entries
00638     SPKFileRep::UShort numEntries;
00639     FS::Read(ifs, (char *)(&numEntries), sizeof(numEntries));
00640 
00641     /* Note: no lock is required on the "CE::T::T" and "CE::T::ReadExtras"
00642        methods below because the cache entry being modified is fresh.
00643        The same holds for the "CE::List::List" constructor. */
00644 
00645     CE::T *entry;
00646     CE::List *head = (CE::List *)NULL, *prev, *curr;
00647 
00648     // read <PKEntry>*, building up resulting list (in order)
00649     for (int i = 0; i < numEntries; i++) {
00650       entry = NEW_CONSTR(CE::T, (ifs, readImmutable));
00651       curr = NEW_CONSTR(CE::List, (entry, (CE::List *)NULL));
00652       if (head == (CE::List *)NULL)
00653         head = curr;      // first entry
00654       else
00655         prev->SetTail(curr);  // append new entry to end of list
00656       prev = curr;
00657     }
00658 
00659     // read <PKEntryExtra>*
00660     for (curr = head; curr != (CE::List *)NULL; curr = curr->Tail()) {
00661         curr->Head()->ReadExtras(ifs);
00662     }
00663     return head;
00664 }
00665 
00666 void SPKFile::Write(ifstream &ifs, ostream &ofs, streampos origin,
00667   /*OUT*/ SPKFileRep::Header* &hdr) const
00668   throw (FS::Failure)
00669 /* REQUIRES FS::Posn(ofs) == ``start of <PKFile>'' && hdr == NULL */
00670 {
00671     // key/value variables
00672     FP::Tag key;
00673     CE::List *entryList;
00674 
00675     // initialize SPKFileRep::Header
00676     hdr = NEW(SPKFileRep::Header);
00677     hdr->num = this->oldEntries.Size();
00678     hdr->entry = NEW_ARRAY(SPKFileRep::HeaderEntry, hdr->num);
00679     CFPEntryIter it(&(this->oldEntries));
00680     int i;
00681     for (i = 0; it.Next(/*OUT*/ key, /*OUT*/ entryList); i++) {
00682         assert(i < hdr->num);
00683         hdr->entry[i].cfp = key;
00684     }
00685 
00686     // write <CFPHeader>
00687     hdr->Write(ofs, origin);
00688 
00689     // write <PKFHeaderTail>
00690     const Text *t = (this->sourceFunc != (Text *)NULL)
00691       ? this->sourceFunc : NEW(Text);
00692     TextIO::Write(ofs, *t);
00693     FS::Write(ofs, (char *)(&(this->pkEpoch)), sizeof(this->pkEpoch));
00694     FS::Write(ofs, (char *)(&(this->namesEpoch)), sizeof(this->namesEpoch));
00695     try
00696       {
00697         CompactFV::List compactFV(this->allNames);
00698         compactFV.Write(ofs);
00699       }
00700     // Constructing the CompactFV from this->allNames uses a
00701     // PrefixTbl.  This can throw PrefixTbl::Overflow indicating that
00702     // we've exceeded the internal limits of the PrefixTbl class (by
00703     // trying to add the 2^16th entry).  If that happens we print an
00704     // error message and abort, probably dumping core.
00705     catch(PrefixTbl::Overflow)
00706       {
00707         Debug::Lock();
00708 
00709         cerr << Debug::Timestamp()
00710              << "INTERNAL ERROR: "
00711              << "PrefixTbl overflow in writing free variables to "
00712              << "stable PKFile:" << endl
00713              << "  pk         = " << pk << endl
00714              << "  sourceFunc = " << ((this->sourceFunc != (Text *)NULL)
00715                                       ? this->sourceFunc->cchars()
00716                                       : "<UNKNOWN>") << endl
00717              << "(This means you've exceeded internal limits of the" << endl
00718              << "cache server, and you may have to erase your cache.)" << endl;
00719 
00720         Debug::Unlock();
00721 
00722         // This is a fatal error.  Abort (which should also dump
00723         // core).
00724         abort();
00725       }
00726     this->commonNames.Write(ofs);
00727 
00728     // write <PKEntries>*
00729     for (i = 0; i < hdr->num; i++) {
00730         SPKFileRep::HeaderEntry *entryPtr = &(hdr->entry[i]);
00731         entryPtr->offset = FS::Posn(ofs) - origin;
00732         bool inTbl = this->oldEntries.Get(entryPtr->cfp, /*OUT*/ entryList);
00733         assert(inTbl);
00734         WriteEntries(ifs, ofs, entryList);
00735     }
00736 } // SPKFile::Write
00737 
00738 void SPKFile::WriteEntries(ifstream &ifs, ostream &ofs, CE::List *entryList)
00739   const throw (FS::Failure)
00740 /* REQUIRES FS::Posn(ofs) == ``start of <PKEntries>'' */
00741 {
00742     // write number of entries
00743     SPKFileRep::UShort numEntries = CE::List::Length(entryList);
00744     FS::Write(ofs, (char *)(&numEntries), sizeof(numEntries));
00745 
00746     // write <PKEntry>*
00747     CE::List *curr;
00748     for (curr = entryList; curr != NULL; curr = curr->Tail()) {
00749       {
00750         unsigned int missing;
00751         if(curr->Head()->CheckUsedNames(&(this->commonNames), /*OUT*/ missing))
00752           {
00753             Debug::Lock();
00754 
00755             cerr << Debug::Timestamp()
00756                  << "INTERNAL ERROR: "
00757                  << "Detected incorrect commonNames|uncommonNames:" << endl
00758                  << "  pk           = " << pk << endl
00759                  << "  ci           = " << curr->Head()->CI() << endl
00760                  << "  missing name = " << missing << endl << endl
00761                  << " (Please report this as a bug.)" << endl;
00762 
00763             Debug::Unlock();
00764 
00765             // This is a fatal error.  Abort (which should also dump
00766             // core).
00767             abort();
00768           }
00769       }
00770 
00771         curr->Head()->Write(ofs, &ifs);
00772     }
00773 
00774     // write <PKEntryExtra>*
00775     for (curr = entryList; curr != NULL; curr = curr->Tail()) {
00776         curr->Head()->WriteExtras(ofs);
00777     }
00778 }
00779 
00780 static void SPKFile_WriteBanner(ostream &os, char c, int width) throw ()
00781 {
00782     os << "// "; width -= 3;
00783     for (int i = 0; i < width; i++) os << c;
00784     os << endl;
00785 }
00786 
00787 void SPKFile::Debug(ostream &os, const SPKFileRep::Header &hdr,
00788   streampos origin, bool verbose) const throw ()
00789 {
00790     const int BannerWidth = 75;
00791 
00792     // write general header
00793     os << endl;
00794     SPKFile_WriteBanner(os, '+', BannerWidth);
00795     os << "// <PKFile> (offset " << origin << ")" << endl;
00796     SPKFile_WriteBanner(os, '+', BannerWidth);
00797     if (!verbose) {
00798         os << endl << "pk  = " << pk << endl;
00799     } else {
00800         os << "pk        = " << pk << endl;
00801     }
00802 
00803     // write <CFPHeader>
00804     hdr.Debug(os, verbose);
00805     int i;
00806     for (i = 0; i < hdr.num; i++) {
00807         hdr.entry[i].Debug(os, verbose);
00808     }
00809 
00810     if (verbose) {
00811         // write <PKFHeaderTail>
00812         os << endl << "// <PKFHeaderTail>" << endl;
00813         os << "sourceFunc  = " <<
00814           ((this->sourceFunc != (Text *)NULL)
00815             ? this->sourceFunc->cchars()
00816             : "<UNKNOWN>")
00817           << endl;
00818         os << "pkEpoch     = " << this->pkEpoch << endl;
00819         os << "nmsEpoch    = " << this->namesEpoch << endl;
00820         os << "allNames    =" << endl;
00821         this->allNames.Print(os, 2, &(this->commonNames));
00822         os << "commonNms   = {" << endl;
00823         this->commonNames.PrintAll(os, 4);
00824         os << "  } (" << this->commonNames.Cardinality() << " total)" << endl;
00825     }
00826 
00827     // write <PKEntries>*
00828     for (i = 0; i < hdr.num; i++) {
00829         // write header info
00830         os << endl;
00831         if (verbose) {
00832             SPKFile_WriteBanner(os, '-', BannerWidth);
00833         }
00834         os << "// <PKEntries> (offset " << hdr.entry[i].offset << ")" << endl;
00835         CE::List *list;
00836         bool inTbl = this->oldEntries.Get(hdr.entry[i].cfp, /*OUT*/ list);
00837         assert(inTbl);
00838         os << "numEntries = " << CE::List::Length(list) << endl;
00839 
00840         if (verbose) {
00841             // write entries
00842             CE::List *curr;
00843             int i;
00844             for (curr=list, i=1; curr != NULL; curr=curr->Tail(), i++) {
00845                 os << endl << "// <PKEntry> (#" << i << ")" << endl;
00846                 curr->Head()->Debug(os);
00847             }
00848 
00849             // write entries
00850             for (curr=list, i=1; curr != NULL; curr=curr->Tail(), i++) {
00851                 os << endl << "// <PKEntryExtra> (#" << i << ")" << endl;
00852                 curr->Head()->DebugExtras(os);
00853             }
00854         }
00855     }
00856 } // SPKFile::Debug

Generated on Mon May 8 00:48:35 2006 for Vesta by  doxygen 1.4.2