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

Generated on Thu May 24 23:01:45 2007 for Vesta by  doxygen 1.5.1