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 // SPKFile.H -- An interface for reading/writing stable PKFile's within 00020 // MultiPKFile's on disk. This also defines the type "SPKFile", of which 00021 // "VPKFile" is a subtype. 00022 00023 #ifndef _SPK_FILE_H 00024 #define _SPK_FILE_H 00025 00026 #include <Basics.H> 00027 #include <FS.H> 00028 #include <Table.H> 00029 #include <FP.H> 00030 #include <CacheIntf.H> 00031 #include <BitVector.H> 00032 #include <FV.H> 00033 00034 #include "SPKFileRep.H" 00035 #include "IntIntTblLR.H" 00036 #include "CacheEntry.H" 00037 #include "VPKFileChkPt.H" 00038 00039 class SPKFile { 00040 public: 00041 // constructors 00042 SPKFile(const FP::Tag& pk) throw () 00043 : pk(pk), pkEpoch(0), namesEpoch(0), sourceFunc(NULL) { /*SKIP*/ } 00044 /* Create a new, empty PKFile for primary key "pk". This will be used as 00045 the initial PKFile when none exists in the stable cache. */ 00046 00047 SPKFile(const FP::Tag& pk, std::ifstream &ifs, std::streampos origin, 00048 int version, /*OUT*/ SPKFileRep::Header* &hdr, 00049 bool readEntries = true, bool readImmutable = true) 00050 throw (FS::EndOfFile, FS::Failure) 00051 : pk(pk) 00052 { Read(ifs, origin, version, /*OUT*/ hdr, readEntries, readImmutable);} 00053 /* REQUIRES FS::Posn(ifs) == ``start of <PKFile>'' */ 00054 /* Initialize a new "SPKFile" object from the disk. See "Read" below. */ 00055 00056 // update 00057 bool Update(const VPKFileChkPt *chkpt, const BitVector *toDelete, 00058 /*OUT*/ BitVector* &exCommonNames, 00059 /*OUT*/ BitVector* &exUncommonNames, 00060 /*OUT*/ BitVector* &packMask, /*OUT*/ IntIntTblLR* &reMap, 00061 /*OUT*/ bool &isEmpty) throw (); 00062 /* REQUIRES ((chkpt!=NULL) && chkpt->hasNewEntries) || (toDelete!=NULL) */ 00063 /* ENSURES (packMask == NULL) == (reMap == NULL) */ 00064 /* Update this SPKFile. If "chkpt != NULL", incorporate the new names 00065 and entries in "chkpt". If "toDelete" is non-NULL, then any entries 00066 in this SPKFile whose indices correspond to set bits in "*toDelete" 00067 should be deleted. The method returns "true" iff the "SPKFile" needs 00068 to be modified on disk in any way. This will be true if there are 00069 new entries to add from "chkpt", if some existing entries on the 00070 disk must be deleted, or if some entries in "chkpt" must be deleted. 00071 In the latter case, the contents of the SPKFile may not change, but 00072 its "pkEpoch" must be increased. 00073 00074 If any entries are added or removed, the set of common names for this 00075 SPKFile may change. In particular, if any entries are added, some names 00076 that used to be common may become uncommon. In this case, the "OUT" 00077 parameter "exCommonNames" is set to point to a new bit vector of the 00078 names that used to be common; it is set to "NULL" if there are no such 00079 names. If any entries are deleted, then names that used to be uncommon 00080 may become common. In this case, the "OUT" parameter "exUncommonNames" 00081 is set to point to a new bit vector of the names that used to be 00082 uncommon; it is set to "NULL" if there are no such names. The set bits 00083 of both the "exCommonNames" and "exUncommonNames" are interpreted 00084 relative to the "allNames" array *before* this method was called; they 00085 are *not* packed according to "packMask". 00086 00087 Finally, if entries are deleted, the union of all the names appearing 00088 in this SPKFile (its "allNames" field) may change. In this case, 00089 "packMask" is set to a new bit vector whose set bits correspond to 00090 names in the original "allNames" array that still appear in some entry 00091 in the stable PKFile. This bit vector may be used to pack the 00092 "commonNames" and "uncommonNames" bit vectors in the volatile cache. 00093 Furthermore, "reMap" is set to point to a new table mapping integers to 00094 integers. For each name that remains after the SPKFile has been 00095 updated, this table maps the index of each name in the original 00096 "allNames" array to its index in the new version of that array. 00097 Because the spec requires that the sequence of the names is preserved, 00098 a name's new index will always be at most its old index. If no name 00099 indices change, then "packMask" and "reMap" are both set to "NULL". 00100 In the event that "packMask" and "reMap" are non-NULL, the 00101 "this->allNames" array and the "this->commonNames" bit vector are 00102 packed according to "packMask". 00103 00104 Even in the event that "packMask" and "reMap" are non-NULL, the bit 00105 vectors "exCommonNames" and "exUncommonNames" (if non-NULL) are *not* 00106 packed. That is, they should be interpreted relative to the original 00107 "allNames" array in place on entry to this method. 00108 00109 If the PKFile has no entries after being flushed, then "isEmpty" is set 00110 to true. It is set to false otherwise. 00111 00112 If the PKFile changes (i.e., if this method returns "true"), the 00113 "namesEpoch" value is updated from "chkpt". If any name indices change 00114 (i.e., if "packMask" and "reMap" are set non-NIL), then the SPKFile's 00115 "namesEpoch" is incremented. 00116 00117 One special case worth considering is the one in which this "SPKFile" 00118 is brand new, and hence, empty. In this case, there are no existing 00119 common names, so "exCommonNames" will be set to "NULL" and 00120 "exUncommonNames" will be set to the *new* set of common names. Also 00121 in this case, this->pkEpoch and chkpt->pkEpoch will both be zero on 00122 entry, so this->pkEpoch must be updated to 1; in all other cases, 00123 this->pkEpoch will already have been updated to chkpt->pkEpoch + 1 by 00124 VPKFile::CheckPoint. 00125 00126 Another case worth considering is the one in which all the entries from 00127 this SPKFile have been deleted (i.e., "isEmpty" is set to true). In 00128 this case, "this->allNames" is set to the empty list, all entries are 00129 deleted from "this->oldEntries", "exCommonNames" is set to a new bit 00130 vector containing the previous set of common names, "exUncommonNames" 00131 is set to "NULL", "packMask" is set to a new, empty bit vector, and 00132 "reMap is set to a new, empty table. */ 00133 00134 // read/write 00135 void Read(std::ifstream &ifs, std::streampos origin, int version, 00136 /*OUT*/ SPKFileRep::Header* &hdr, bool readEntries = true, 00137 bool readImmutable = true) throw (FS::EndOfFile, FS::Failure); 00138 /* REQUIRES FS::Posn(ifs) == ``start of <PKFile>'' */ 00139 /* Fill in the fields of this "SPKFile" from "ifs" (namely, the 00140 <PKFHeaderTail>). 00141 00142 If "readEntries" is true, the "hdr" is set to a newly-allocated record 00143 containing the <CFPHeader> information, and all of the PKFile's 00144 <PKEntries> records are stored in this SPKFile's "oldEntries" table. 00145 Otherwise, "hdr" is set to NULL, and "ifs" is left pointing just after 00146 the <PKFHeader>. 00147 00148 If "readEntries" is true, then the "readImmutable" parameter is 00149 significant; otherwise, it is ignored. If "readImmutable" is true, 00150 then the mutable and immutable fields of all cache entries are read 00151 from disk into memory. If "readImmutable" is false, then the cache 00152 indices of the cache entries are negated, and the immutable "value", 00153 "kids", and "fps" fields are not read from disk. Instead, those 00154 pointers are made to point to records containing the length and 00155 position of the pickled representations of those values in the SPKFile. 00156 "readImmutable" should only be passed as "false" when the cache entries 00157 are intended to be written out again. */ 00158 00159 void Write(std::ifstream &ifs, std::ostream &ofs, std::streampos origin, 00160 /*OUT*/ SPKFileRep::Header* &hdr) const throw (FS::Failure); 00161 /* REQUIRES VPKFile(SELF).mu IN LL */ 00162 /* REQUIRES FS::Posn(ofs) == ``start of <PKFile>'' && hdr == NULL */ 00163 /* Write this "SPKFile" to "ofs", and fill in "hdr" to correspond to the 00164 newly-allocated header information. The "hdr" is set to the written 00165 file's <PKFHeader> so it's relative offsets can be later back-patched. 00166 "origin" should be the location of the start of this <PKFile>, namely 00167 "FS::Posn(ofs)" at the start of the call. "ifs" is the seekable input 00168 file from which the data for the immutable cache fields is read. */ 00169 00170 // other methods 00171 void Debug(std::ostream &os, const SPKFileRep::Header &hdr, 00172 std::streampos origin, bool verbose = false) const throw (); 00173 /* REQUIRES VPKFile(SELF).mu IN LL */ 00174 /* Write an ASCII represenation of this PKFile to "os", where "hdr" is the 00175 corresponding header data, and "origin" is the location of the start of 00176 the PKFile within the MultiPKFile. 00177 00178 If "verbose" is false, write a concise representation that only lists 00179 the primary key, the number of distinct secondary keys, the secondary 00180 keys themselves, and the number of entries stored under each secondary 00181 key. Otherwise, write a complete representation that includes the 00182 complete header information and contents of the entries as well. */ 00183 00184 // dictionary types 00185 typedef Table<FP::Tag,CE::List*>::Default CFPEntryMap; 00186 typedef Table<FP::Tag,CE::List*>::Iterator CFPEntryIter; 00187 00188 // accessors 00189 const FP::Tag PK() const throw () 00190 { return this->pk; } 00191 PKFile::Epoch PKEpoch() const throw () 00192 { return this->pkEpoch; } 00193 FV::Epoch NamesEpoch() const throw () 00194 { return this->namesEpoch; } 00195 const FV::ListApp *AllNames() const throw () 00196 { return &(this->allNames); } 00197 const BitVector *CommonNames() const throw () 00198 { return &(this->commonNames); } 00199 const CFPEntryMap *OldEntries() const throw () 00200 { return &(this->oldEntries); } 00201 const Text *SourceFunc() const throw () 00202 { return this->sourceFunc; } 00203 00204 protected: 00205 // data fields 00206 FP::Tag pk; // the PK for this PKFile 00207 PKFile::Epoch pkEpoch; // epoch for this PKFile 00208 FV::Epoch namesEpoch; // epoch for "allNames" 00209 FV::ListApp allNames; // list of union of names in all entries 00210 BitVector commonNames; // set of common names for all entries 00211 CFPEntryMap oldEntries; // this PKFile's cache entries: CFP -> CE::List 00212 const Text *sourceFunc; // definition site of corresponding function 00213 00214 /* The "pkEpoch" records the epoch of this PKFile. It is 00215 incremented each time the PKFile is flushed to the stable cache. 00216 Flushing is not atomic, and the field is incremented before the 00217 disk write is done, so at times it will be ahead of the value on disk. 00218 00219 The "namesEpoch" records the epoch of the "allNames" list. It is 00220 incremented whenever that list changes, which can occur either when a 00221 new entry is added, or when deletions are pending and the PKFile is 00222 flushed to disk. 00223 00224 The "commonNames" bit vector indicates which names are common to all 00225 entries in this PKFile. It is interpreted relative to the "allNames" 00226 sequence. 00227 00228 The "oldEntries" table maps common fingerprints to lists of cache 00229 entries that share the same common fingerprint. Hence, it stores all 00230 of this PKFile's entries. (In the VPKFile sub-type, it stores the 00231 ``warm'' entries that have been ``paged in'' from the stable cache.) 00232 00233 The "sourceFunc" field is a string generated by the evaluator for 00234 debugging purposes only. It is meant to denote the site where the 00235 function corresponding to the entries in this PKFile is defined. 00236 A value of NULL means the field is uninitialized; it gets initialized 00237 when entries are added to this PKFile, but may remain uninitialized 00238 if all entries in the PKFile were created before this feature was 00239 added. 00240 */ 00241 00242 void AddEntryToTable(/*INOUT*/ CFPEntryMap &tbl, 00243 const FP::Tag &cfp, CE::T *ce) const throw (); 00244 /* Add the cache entry "ce" to "tbl" under the common fingerprint 00245 "cfp". */ 00246 00247 virtual VPKFileChkPt *CheckPoint() throw (); 00248 /* Return a newly-allocated checkpoint of this "SPKFile". This does not 00249 set the "newUncommon" and "newCommon" fields of the checkpoint. */ 00250 00251 void ScanCommonTable(const CFPEntryMap &cfpTbl, const BitVector *toDelete, 00252 /*INOUT*/ BitVector &uncommonJoin, 00253 /*INOUT*/ BitVector &uncommonMeet, 00254 /*INOUT*/ int &totalEntries, 00255 /*INOUT*/ bool &delOne) const throw (); 00256 /* REQUIRES VPKFile(SELF).mu IN LL */ 00257 /* Over the set of entries in "cfpTbl" whose indices do not correspond to 00258 set bits in "toDelete", augment "uncommonJoin" to include the union of 00259 all their uncommon names, reduce "uncommonMeet" so as not to include 00260 any name not set in all of their uncommon names, and increment 00261 "totalEntries" by the number of (undeleted) entries considered. 00262 "delOne" is set to true if some entry in "cfpTbl" has an index 00263 corresponding to a set bit in "toDelete". */ 00264 00265 void ScanList(const CE::List *head, const BitVector *toDelete, 00266 /*INOUT*/ BitVector &uncommonJoin, 00267 /*INOUT*/ BitVector &uncommonMeet, 00268 /*INOUT*/ int &totalEntries, 00269 /*INOUT*/ bool &delOne) const throw (); 00270 /* REQUIRES VPKFile(SELF).mu, ListLock(*head) IN LL */ 00271 /* Same as "ScanCommonTable" above, but only consider the entries in the 00272 list "head" whose indices do not correspond to set bits in "toDelete". 00273 "delOne" is set to true if some entry in the list "head" has an index 00274 corresponding to a set bit in "toDelete". */ 00275 00276 CE::List *ExtractEntries(/*INOUT*/ CFPEntryMap &cfpTbl) throw (); 00277 /* REQUIRES VPKFile(SELF).mu IN LL */ 00278 /* Return a list of all entries in "cfpTbl", and clear the table. */ 00279 00280 void UpdateCommonTable(/*INOUT*/ CFPEntryMap &cfpTbl, 00281 const BitVector *toDelete, 00282 const BitVector *exCommonNames, 00283 const BitVector *exUncommonNames, 00284 const BitVector *packMask, 00285 const IntIntTblLR *reMap, bool unlazyTag = true) 00286 throw (); 00287 /* REQUIRES cfpTbl.Size() > 0 */ 00288 /* REQUIRES (packMask == NULL) == (reMap == NULL) */ 00289 /* Recompute the new tags for all entries in "cfpTbl" whose indices do not 00290 correspond to set bits in "toDelete", and store them back under their 00291 new common fingerprints. Use "exCommonNames", "exUncommonNames", 00292 "packMask", and "reMap" to change the common names of all entries. If 00293 "unlazyTag" is true, then the fingerprints for the entries' uncommon 00294 tags are also computed. For each entry, compute the maximum timestamp 00295 of its common names. 00296 00297 Note: The "commonNames" for this PKFile should *not* have been 00298 packed according to "packMask" before this method is called. */ 00299 00300 static CE::T* Lookup(std::ifstream &ifs, std::streampos origin, int version, 00301 const FP::Tag &commonTag, const FP::List &fps) 00302 throw (FS::EndOfFile, FS::Failure); 00303 /* REQUIRES FS::Posn(ifs) == origin == ``start of <PKFile>'' */ 00304 /* Assuming "ifs" is properly positioned at the start of the stable 00305 PKFile, lookup the cache entry with common fingerprint "commonTag" and 00306 with individual fingerprints for all free variables for this PKFiles 00307 specified by "fps". "origin" specifies the location of the start of the 00308 SPKFile within the SMultiPKFile, and "version" specifies the version 00309 number of the SMultiPKFile on disk (i.e., its format). 00310 00311 In the event of a hit, return the resulting cache entry; otherwise, 00312 return NULL. */ 00313 00314 private: 00315 void AddEntries(const VPKFileChkPt &chkpt, const BitVector *toDelete, 00316 const BitVector *exCommonNames, 00317 const BitVector *exUncommonNames, 00318 const BitVector *packMask, const IntIntTblLR *reMap, 00319 bool unlazyTag) 00320 throw (); 00321 /* REQUIRES (packMask == NULL) == (reMap == NULL) */ 00322 /* Add the entries in "chkpt" to "this->entries", recomputing their common 00323 fingerprints and updating their tags if necessary. The uncommon tags 00324 are unlazied iff "unlazyTag" is true. 00325 00326 Note: The "commonNames" for this PKFile should *not* have been 00327 packed according to "packMask" before this method is called. */ 00328 00329 void UpdateList(/*INOUT*/ CFPEntryMap &cfpTbl, 00330 const CE::List *ents, const BitVector *toDelete, 00331 const BitVector *exCommonNames, 00332 const BitVector *exUncommonNames, 00333 const BitVector *packMask, const IntIntTblLR *reMap, 00334 bool unlazyTag) 00335 throw (); 00336 /* REQUIRES VPKFile(SELF).mu, ListLock(*ents) IN LL */ 00337 /* REQUIRES (packMask == NULL) == (reMap == NULL) */ 00338 /* Add the entries in "ents" to "cfpTbl", recomputing their common 00339 fingerprints and updating their tags if necessary. If "exCommonNames" 00340 is non-NULL, then the "uncommonNames" of all entries in "ents" are 00341 augmented to include "exCommonNames". If "exUncommonNames" is 00342 non-NULL, then the "uncommonNames" of all entries in "ents" are 00343 decreased so as not to include "exUncommonNames". If "unlazyTag" is 00344 true, then the fingerprints of their uncommon tags will also be 00345 computed. 00346 00347 Note: The "commonNames" for this PKFile should *not* have been 00348 packed according to "packMask" before this method is called. */ 00349 00350 CE::List *ReadEntries(std::ifstream &ifs, bool readImmutable = true) 00351 throw (FS::EndOfFile, FS::Failure); 00352 /* REQUIRES FS::Posn(ifs) == ``start of <PKEntries>'' */ 00353 /* Read a single <PKEntries> record from "ifs" and return the 00354 corresponding list of newly allocated entries (in order). The 00355 immutable fields of the cache entries are read into memory iff 00356 "readImmutable" is "true". */ 00357 00358 void WriteEntries(std::ifstream &ifs, std::ostream &ofs, 00359 CE::List *entryList) 00360 const throw (FS::Failure); 00361 /* REQUIRES VPKFile(SELF).mu, ListLock(*entryList) IN LL */ 00362 /* REQUIRES FS::Posn(ofs) == ``start of <PKEntries>'' */ 00363 /* Write a single <PKEntries> record formed from the entries in 00364 "entryList" (in order) to "ofs". "ifs" is the input stream from 00365 which any unread immutable cache entry fields are read. */ 00366 00367 static std::streampos LookupCFP(std::ifstream &ifs, std::streampos origin, 00368 int version, const FP::Tag &cfp, 00369 const SPKFileRep::Header &hdr) 00370 throw (FS::EndOfFile, FS::Failure); 00371 /* REQUIRES FS::Posn(ifs) == ``start of <CFPTypedHeader>'' */ 00372 /* Returns the index of the <PKEntries> for common fingerprint "cfp" in 00373 the stream "ifs" if any exist; -1 otherwise. */ 00374 00375 static std::streampos LookupCFPInListV1(std::ifstream &ifs, 00376 std::streampos origin, 00377 const FP::Tag &cfp, int num) 00378 throw (FS::EndOfFile, FS::Failure); 00379 /* REQUIRES FS::Posn(ifs) == ``start of <CFPSeqHeader>'' */ 00380 /* Returns the index of the <PKEntries> for common fingerprint "cfp" in 00381 the stream "ifs" if any exist; -1 otherwise. This routine assumes 00382 the entries are an arbitrary list. */ 00383 00384 static std::streampos LookupCFPInSortedListV1(std::ifstream &ifs, 00385 std::streampos origin, 00386 const FP::Tag &cfp, int num) 00387 throw (FS::EndOfFile, FS::Failure); 00388 /* REQUIRES FS::Posn(ifs) == ``start of <CFPSeqHeader>'' */ 00389 /* Returns the index of the <PKEntries> for common fingerprint "cfp" in 00390 the stream "ifs" if any exist; -1 otherwise. This routine requires 00391 the common fingerprints to be listed in sorted order. */ 00392 00393 static CE::T* LookupEntryV1(std::ifstream &ifs, const FP::List &fps) 00394 throw (FS::EndOfFile, FS::Failure); 00395 /* REQUIRES FS::Posn(ifs) == ``start of <PKEntries>'' */ 00396 }; 00397 00398 #endif // _SPK_FILE_H