Back to index

lightning-sunbird  0.9+nobinonly
nsFastLoadFile.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla FastLoad code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Brendan Eich <brendan@mozilla.org> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include <string.h>
00040 #include "prtypes.h"
00041 #include "nscore.h"
00042 #include "nsDebug.h"
00043 #include "nsEnumeratorUtils.h"
00044 #include "nsMemory.h"
00045 #include "nsXPIDLString.h"
00046 #include "nsString.h"
00047 #include "nsReadableUtils.h"
00048 
00049 #include "nsIComponentManager.h"
00050 #include "nsIFile.h"
00051 #include "nsILocalFile.h"
00052 #include "nsISeekableStream.h"
00053 #include "nsISerializable.h"
00054 #include "nsIStreamBufferAccess.h"
00055 
00056 #include "nsBinaryStream.h"
00057 #include "nsFastLoadFile.h"
00058 #include "nsInt64.h"
00059 
00060 #ifdef DEBUG_brendan
00061 # define METERING
00062 # define DEBUG_MUX
00063 #endif
00064 
00065 #ifdef METERING
00066 # define METER(x)       x
00067 #else
00068 # define METER(x)       /* nothing */
00069 #endif
00070 
00071 #ifdef DEBUG_MUX
00072 # include <stdio.h>
00073 # include <stdarg.h>
00074 
00075 static void trace_mux(char mode, const char *format, ...)
00076 {
00077     va_list ap;
00078     static FILE *tfp;
00079     if (!tfp) {
00080         char tfn[16];
00081         sprintf(tfn, "/tmp/mux.%ctrace", mode);
00082         tfp = fopen(tfn, "w");
00083         if (!tfp)
00084             return;
00085         setvbuf(tfp, NULL, _IOLBF, 0);
00086     }
00087     va_start(ap, format);
00088     vfprintf(tfp, format, ap);
00089     va_end(ap);
00090 }
00091 
00092 # define TRACE_MUX(args) trace_mux args
00093 #else
00094 # define TRACE_MUX(args) /* nothing */
00095 #endif
00096 
00097 /*
00098  * Fletcher's 16-bit checksum, using 32-bit two's-complement arithmetic.
00099  */
00100 #define FOLD_ONES_COMPLEMENT_CARRY(X)   ((X) = ((X) & 0xffff) + ((X) >> 16))
00101 #define ONES_COMPLEMENT_ACCUMULATE(X,Y) (X) += (Y); if ((X) & 0x80000000)     \
00102                                         FOLD_ONES_COMPLEMENT_CARRY(X)
00103 #define FLETCHER_ACCUMULATE(A,B,U)      ONES_COMPLEMENT_ACCUMULATE(A, U);     \
00104                                         ONES_COMPLEMENT_ACCUMULATE(B, A)
00105 
00106 PR_IMPLEMENT(PRUint32)
00107 NS_AccumulateFastLoadChecksum(PRUint32 *aChecksum,
00108                               const PRUint8* aBuffer,
00109                               PRUint32 aLength,
00110                               PRBool aLastBuffer)
00111 {
00112     PRUint32 C = *aChecksum;
00113     PRUint32 A = C & 0xffff;
00114     PRUint32 B = C >> 16;
00115 
00116     PRUint16 U = 0;
00117     if (aLength >= 4) {
00118         PRBool odd = PRWord(aBuffer) & 1;
00119         switch (PRWord(aBuffer) & 3) {
00120           case 3:
00121             U = (aBuffer[0] << 8) | aBuffer[1];
00122             FLETCHER_ACCUMULATE(A, B, U);
00123             U = aBuffer[2];
00124             aBuffer += 3;
00125             aLength -= 3;
00126             break;
00127 
00128           case 2:
00129             U = (aBuffer[0] << 8) | aBuffer[1];
00130             FLETCHER_ACCUMULATE(A, B, U);
00131             U = 0;
00132             aBuffer += 2;
00133             aLength -= 2;
00134             break;
00135 
00136           case 1:
00137             U = *aBuffer++;
00138             aLength--;
00139             break;
00140         }
00141 
00142         PRUint32 W;
00143         if (odd) {
00144             while (aLength > 3) {
00145                 W = *NS_REINTERPRET_CAST(const PRUint32*, aBuffer);
00146                 U <<= 8;
00147 #ifdef IS_BIG_ENDIAN
00148                 U |= W >> 24;
00149                 FLETCHER_ACCUMULATE(A, B, U);
00150                 U = PRUint16(W >> 8);
00151                 FLETCHER_ACCUMULATE(A, B, U);
00152                 U = W & 0xff;
00153 #else
00154                 U |= W & 0xff;
00155                 FLETCHER_ACCUMULATE(A, B, U);
00156                 U = PRUint16(W >> 8);
00157                 U = NS_SWAP16(U);
00158                 FLETCHER_ACCUMULATE(A, B, U);
00159                 U = W >> 24;
00160 #endif
00161                 aBuffer += 4;
00162                 aLength -= 4;
00163             }
00164             aBuffer--;      // we're odd, we didn't checksum the last byte
00165             aLength++;
00166         } else {
00167             while (aLength > 3) {
00168                 W = *NS_REINTERPRET_CAST(const PRUint32*, aBuffer);
00169 #ifdef IS_BIG_ENDIAN
00170                 U = W >> 16;
00171                 FLETCHER_ACCUMULATE(A, B, U);
00172                 U = PRUint16(W);
00173                 FLETCHER_ACCUMULATE(A, B, U);
00174 #else
00175                 U = NS_SWAP16(W);
00176                 FLETCHER_ACCUMULATE(A, B, U);
00177                 U = W >> 16;
00178                 U = NS_SWAP16(W);
00179                 FLETCHER_ACCUMULATE(A, B, U);
00180 #endif
00181                 aBuffer += 4;
00182                 aLength -= 4;
00183             }
00184         }
00185     }
00186 
00187     if (aLastBuffer) {
00188         NS_ASSERTION(aLength <= 4, "aLength botch");
00189         switch (aLength) {
00190           case 4:
00191             U = (aBuffer[0] << 8) | aBuffer[1];
00192             FLETCHER_ACCUMULATE(A, B, U);
00193             U = (aBuffer[2] << 8) | aBuffer[3];
00194             FLETCHER_ACCUMULATE(A, B, U);
00195             break;
00196 
00197           case 3:
00198             U = (aBuffer[0] << 8) | aBuffer[1];
00199             FLETCHER_ACCUMULATE(A, B, U);
00200             U = aBuffer[2];
00201             FLETCHER_ACCUMULATE(A, B, U);
00202             break;
00203 
00204           case 2:
00205             U = (aBuffer[0] << 8) | aBuffer[1];
00206             FLETCHER_ACCUMULATE(A, B, U);
00207             break;
00208 
00209           case 1:
00210             U = aBuffer[0];
00211             FLETCHER_ACCUMULATE(A, B, U);
00212             break;
00213         }
00214 
00215         aLength = 0;
00216     }
00217 
00218     while (A >> 16)
00219         FOLD_ONES_COMPLEMENT_CARRY(A);
00220     while (B >> 16)
00221         FOLD_ONES_COMPLEMENT_CARRY(B);
00222 
00223     *aChecksum = (B << 16) | A;
00224     return aLength;
00225 }
00226 
00227 PR_IMPLEMENT(PRUint32)
00228 NS_AddFastLoadChecksums(PRUint32 sum1, PRUint32 sum2, PRUint32 sum2ByteCount)
00229 {
00230     PRUint32 A1 = sum1 & 0xffff;
00231     PRUint32 B1 = sum1 >> 16;
00232 
00233     PRUint32 A2 = sum2 & 0xffff;
00234     PRUint32 B2 = sum2 >> 16;
00235 
00236     PRUint32 A = A1 + A2;
00237     while (A >> 16)
00238         FOLD_ONES_COMPLEMENT_CARRY(A);
00239 
00240     PRUint32 B = B2;
00241     for (PRUint32 n = (sum2ByteCount + 1) / 2; n != 0; n--)
00242         ONES_COMPLEMENT_ACCUMULATE(B, B1);
00243     while (B >> 16)
00244         FOLD_ONES_COMPLEMENT_CARRY(B);
00245 
00246     return (B << 16) | A;
00247 }
00248 
00249 #undef FOLD_ONES_COMPLEMENT_CARRY
00250 #undef ONES_COMPLEMENT_ACCUMULATE
00251 #undef FLETCHER_ACCUMULATE
00252 
00253 static const char magic[] = MFL_FILE_MAGIC;
00254 
00255 // -------------------------- nsFastLoadFileReader --------------------------
00256 
00257 nsID nsFastLoadFileReader::nsFastLoadFooter::gDummyID;
00258 nsFastLoadFileReader::nsObjectMapEntry
00259     nsFastLoadFileReader::nsFastLoadFooter::gDummySharpObjectEntry;
00260 
00261 NS_IMPL_ISUPPORTS_INHERITED5(nsFastLoadFileReader,
00262                              nsBinaryInputStream,
00263                              nsIObjectInputStream,
00264                              nsIFastLoadFileControl,
00265                              nsIFastLoadReadControl,
00266                              nsISeekableStream,
00267                              nsIFastLoadFileReader)
00268 
00269 MOZ_DECL_CTOR_COUNTER(nsFastLoadFileReader)
00270 
00271 nsresult
00272 nsFastLoadFileReader::ReadHeader(nsFastLoadHeader *aHeader)
00273 {
00274     nsresult rv;
00275     PRUint32 bytesRead;
00276 
00277     rv = Read(NS_REINTERPRET_CAST(char*, aHeader), sizeof *aHeader, &bytesRead);
00278     if (NS_FAILED(rv))
00279         return rv;
00280 
00281     if (bytesRead != sizeof *aHeader ||
00282         memcmp(aHeader->mMagic, magic, MFL_FILE_MAGIC_SIZE)) {
00283         return NS_ERROR_UNEXPECTED;
00284     }
00285 
00286     aHeader->mChecksum     = NS_SWAP32(aHeader->mChecksum);
00287     aHeader->mVersion      = NS_SWAP32(aHeader->mVersion);
00288     aHeader->mFooterOffset = NS_SWAP32(aHeader->mFooterOffset);
00289     aHeader->mFileSize     = NS_SWAP32(aHeader->mFileSize);
00290 
00291     return NS_OK;
00292 }
00293 
00294 // nsIFastLoadFileControl methods:
00295 
00296 NS_IMETHODIMP
00297 nsFastLoadFileReader::GetChecksum(PRUint32 *aChecksum)
00298 {
00299     *aChecksum = mHeader.mChecksum;
00300     return NS_OK;
00301 }
00302 
00303 NS_IMETHODIMP
00304 nsFastLoadFileReader::SetChecksum(PRUint32 aChecksum)
00305 {
00306     mHeader.mChecksum = aChecksum;
00307     return NS_OK;
00308 }
00309 
00310 struct nsStringMapEntry : public PLDHashEntryHdr {
00311     const char*     mString;            // key, must come first
00312     nsISupports*    mURI;               // for SelectMuxedDocument return value
00313 };
00314 
00315 struct nsDocumentMapEntry : public nsStringMapEntry {
00316     PRUint32    mInitialSegmentOffset;  // offset of URI's first segment in file
00317 };
00318 
00319 struct nsDocumentMapReadEntry : public nsDocumentMapEntry {
00320     PRUint32    mNextSegmentOffset;     // offset of URI's next segment to read
00321     PRUint32    mBytesLeft : 31,        // bytes remaining in current segment
00322                 mNeedToSeek : 1;        // flag to defer Seek from Select to
00323                                         // Read, in case there is no Read before
00324                                         // another entry is Selected (to improve
00325                                         // input stream buffer utilization)
00326     PRInt64     mSaveOffset;            // in case demux schedule differs from
00327                                         // mux schedule
00328 };
00329 
00330 PR_STATIC_CALLBACK(void)
00331 strmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
00332 {
00333     nsStringMapEntry* entry = NS_STATIC_CAST(nsStringMapEntry*, aHdr);
00334 
00335     if (entry->mString)
00336         nsMemory::Free((void*) entry->mString);
00337     NS_IF_RELEASE(entry->mURI);
00338     PL_DHashClearEntryStub(aTable, aHdr);
00339 }
00340 
00341 static const PLDHashTableOps strmap_DHashTableOps = {
00342     PL_DHashAllocTable,
00343     PL_DHashFreeTable,
00344     PL_DHashGetKeyStub,
00345     PL_DHashStringKey,
00346     PL_DHashMatchStringKey,
00347     PL_DHashMoveEntryStub,
00348     strmap_ClearEntry,
00349     PL_DHashFinalizeStub,
00350     NULL
00351 };
00352 
00353 // An nsObjectMapEntry holds a strong reference to an XPCOM object, unless the
00354 // mObject member, when cast to NSFastLoadOID, has its MFL_OBJECT_DEF_TAG bit
00355 // set.  NB: we rely on the fact that an nsISupports* is never an odd pointer.
00356 struct nsObjectMapEntry : public PLDHashEntryHdr {
00357     nsISupports*            mObject;        // key, must come first
00358 };
00359 
00360 // Fast mapping from URI object pointer back to spec-indexed document info.
00361 struct nsURIMapReadEntry : public nsObjectMapEntry {
00362     nsDocumentMapReadEntry* mDocMapEntry;
00363 };
00364 
00365 PR_STATIC_CALLBACK(void)
00366 objmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
00367 {
00368     nsObjectMapEntry* entry = NS_STATIC_CAST(nsObjectMapEntry*, aHdr);
00369 
00370     // Ignore tagged object ids stored as object pointer keys (the updater
00371     // code does this).
00372     if ((NS_PTR_TO_INT32(entry->mObject) & MFL_OBJECT_DEF_TAG) == 0)
00373         NS_IF_RELEASE(entry->mObject);
00374     PL_DHashClearEntryStub(aTable, aHdr);
00375 }
00376 
00377 static const PLDHashTableOps objmap_DHashTableOps = {
00378     PL_DHashAllocTable,
00379     PL_DHashFreeTable,
00380     PL_DHashGetKeyStub,
00381     PL_DHashVoidPtrKeyStub,
00382     PL_DHashMatchEntryStub,
00383     PL_DHashMoveEntryStub,
00384     objmap_ClearEntry,
00385     PL_DHashFinalizeStub,
00386     NULL
00387 };
00388 
00389 NS_IMETHODIMP
00390 nsFastLoadFileReader::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
00391 {
00392     nsDocumentMapReadEntry* docMapEntry =
00393         NS_STATIC_CAST(nsDocumentMapReadEntry*,
00394                        PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
00395                                             PL_DHASH_LOOKUP));
00396 
00397     *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
00398     return NS_OK;
00399 }
00400 
00401 NS_IMETHODIMP
00402 nsFastLoadFileReader::StartMuxedDocument(nsISupports* aURI, const char* aURISpec)
00403 {
00404     nsDocumentMapReadEntry* docMapEntry =
00405         NS_STATIC_CAST(nsDocumentMapReadEntry*,
00406                        PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
00407                                             PL_DHASH_LOOKUP));
00408 
00409     // If the spec isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
00410     // FastLoad service can try for a file update.
00411     if (PL_DHASH_ENTRY_IS_FREE(docMapEntry))
00412         return NS_ERROR_NOT_AVAILABLE;
00413 
00414     nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
00415     nsURIMapReadEntry* uriMapEntry =
00416         NS_STATIC_CAST(nsURIMapReadEntry*,
00417                        PL_DHashTableOperate(&mFooter.mURIMap, key,
00418                                             PL_DHASH_ADD));
00419     if (!uriMapEntry)
00420         return NS_ERROR_OUT_OF_MEMORY;
00421 
00422     NS_ASSERTION(uriMapEntry->mDocMapEntry == nsnull,
00423                  "URI mapped to two different specs?");
00424     if (uriMapEntry->mDocMapEntry)
00425         return NS_ERROR_UNEXPECTED;
00426 
00427     docMapEntry->mURI = aURI;
00428     NS_ADDREF(docMapEntry->mURI);
00429     uriMapEntry->mObject = key;
00430     NS_ADDREF(uriMapEntry->mObject);
00431     uriMapEntry->mDocMapEntry = docMapEntry;
00432     TRACE_MUX(('r', "start %p (%p) %s\n", aURI, key.get(), aURISpec));
00433     return NS_OK;
00434 }
00435 
00436 NS_IMETHODIMP
00437 nsFastLoadFileReader::SelectMuxedDocument(nsISupports* aURI,
00438                                           nsISupports** aResult)
00439 {
00440     nsresult rv;
00441 
00442     // Find the given URI's entry and select it for more reading.
00443     nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
00444     nsURIMapReadEntry* uriMapEntry =
00445         NS_STATIC_CAST(nsURIMapReadEntry*,
00446                        PL_DHashTableOperate(&mFooter.mURIMap, key,
00447                                             PL_DHASH_LOOKUP));
00448 
00449     // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
00450     // FastLoad service can try selecting the file updater.
00451     if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
00452         return NS_ERROR_NOT_AVAILABLE;
00453 
00454     // If we're interrupting another document's segment, save its offset so
00455     // we can seek back when it's reselected.  If prevDocMapEntry->mNeedToSeek
00456     // is set, that means the stream is not positioned for prevDocMapEntry, to
00457     // avoid buffer thrashing.  See below in this function for more.
00458     nsDocumentMapReadEntry* prevDocMapEntry = mCurrentDocumentMapEntry;
00459     if (prevDocMapEntry &&
00460         prevDocMapEntry->mBytesLeft &&
00461         !prevDocMapEntry->mNeedToSeek) {
00462         rv = Tell(&prevDocMapEntry->mSaveOffset);
00463         if (NS_FAILED(rv))
00464             return rv;
00465     }
00466 
00467     // It turns out we get a fair amount of redundant select calls, thanks to
00468     // non-blocking hunks of data from the parser that are devoid of scripts.
00469     // As more data gets FastLoaded, the number of these useless selects will
00470     // decline.
00471     nsDocumentMapReadEntry* docMapEntry = uriMapEntry->mDocMapEntry;
00472     if (docMapEntry == prevDocMapEntry) {
00473         TRACE_MUX(('r', "select prev %s same as current!\n",
00474                    docMapEntry->mString));
00475     }
00476 
00477     // Invariant: docMapEntry->mBytesLeft implies docMapEntry->mSaveOffset has
00478     // been set non-zero by the Tell call above.
00479     else if (docMapEntry->mBytesLeft) {
00480         NS_ASSERTION(docMapEntry->mSaveOffset != 0,
00481                      "reselecting from multiplex at unsaved offset?");
00482 
00483         // Defer Seek till Read, in case of "ping-pong" Selects without any
00484         // intervening Reads, to avoid dumping the underlying mInputStream's
00485         // input buffer for cases where alternate "pongs" fall in the same
00486         // buffer.
00487         docMapEntry->mNeedToSeek = PR_TRUE;
00488     }
00489 
00490     *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
00491     NS_IF_ADDREF(*aResult);
00492 
00493     mCurrentDocumentMapEntry = docMapEntry;
00494 #ifdef DEBUG_MUX
00495     PRInt64 currentSegmentOffset;
00496     Tell(&currentSegmentOffset);
00497     trace_mux('r', "select %p (%p) offset %ld\n",
00498               aURI, key.get(), (long) currentSegmentOffset);
00499 #endif
00500     return NS_OK;
00501 }
00502 
00503 NS_IMETHODIMP
00504 nsFastLoadFileReader::EndMuxedDocument(nsISupports* aURI)
00505 {
00506     nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
00507     nsURIMapReadEntry* uriMapEntry =
00508         NS_STATIC_CAST(nsURIMapReadEntry*,
00509                        PL_DHashTableOperate(&mFooter.mURIMap, key,
00510                                             PL_DHASH_LOOKUP));
00511 
00512     // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
00513     // FastLoad service can try to end a select on its file updater.
00514     if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
00515         return NS_ERROR_NOT_AVAILABLE;
00516 
00517     // Drop our ref to the URI object that was passed to StartMuxedDocument,
00518     // we no longer need it, and we do not want to extend its lifetime.
00519     if (uriMapEntry->mDocMapEntry)
00520         NS_RELEASE(uriMapEntry->mDocMapEntry->mURI);
00521 
00522     // Shrink the table if half the entries are removed sentinels.
00523     PRUint32 size = PL_DHASH_TABLE_SIZE(&mFooter.mURIMap);
00524     if (mFooter.mURIMap.removedCount >= (size >> 2))
00525         PL_DHashTableOperate(&mFooter.mURIMap, key, PL_DHASH_REMOVE);
00526     else
00527         PL_DHashTableRawRemove(&mFooter.mURIMap, uriMapEntry);
00528 
00529     TRACE_MUX(('r', "end %p (%p)\n", aURI, key.get()));
00530     return NS_OK;
00531 }
00532 
00533 NS_IMETHODIMP
00534 nsFastLoadFileReader::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
00535 {
00536     nsresult rv;
00537 
00538     nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
00539     if (entry) {
00540         // Don't call our Seek wrapper, as it clears mCurrentDocumentMapEntry.
00541         if (entry->mNeedToSeek) {
00542             rv = mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
00543                                       entry->mSaveOffset);
00544             if (NS_FAILED(rv))
00545                 return rv;
00546 
00547             entry->mNeedToSeek = PR_FALSE;
00548         }
00549 
00550         // Loop to handle empty segments, which may be generated by the
00551         // writer, given Start A; Start B; Select A; Select B; write B data;
00552         // multiplexing schedules, which do tend to occur given non-blocking
00553         // i/o with LIFO scheduling.  XXXbe investigate LIFO issues
00554         while (entry->mBytesLeft == 0) {
00555             // Check for unexpected end of multiplexed stream.
00556             NS_ASSERTION(entry->mNextSegmentOffset != 0,
00557                          "document demuxed from FastLoad file more than once?");
00558             if (entry->mNextSegmentOffset == 0)
00559                 return NS_ERROR_UNEXPECTED;
00560 
00561             rv = mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
00562                                       entry->mNextSegmentOffset);
00563             if (NS_FAILED(rv))
00564                 return rv;
00565 
00566             // Clear mCurrentDocumentMapEntry temporarily to avoid recursion.
00567             mCurrentDocumentMapEntry = nsnull;
00568 
00569             rv = Read32(&entry->mNextSegmentOffset);
00570             if (NS_SUCCEEDED(rv)) {
00571                 PRUint32 bytesLeft = 0;
00572                 rv = Read32(&bytesLeft);
00573                 entry->mBytesLeft = bytesLeft;
00574             }
00575 
00576             mCurrentDocumentMapEntry = entry;
00577             if (NS_FAILED(rv))
00578                 return rv;
00579 
00580             NS_ASSERTION(entry->mBytesLeft >= 8, "demux segment length botch!");
00581             entry->mBytesLeft -= 8;
00582         }
00583     }
00584 
00585     rv = mInputStream->Read(aBuffer, aCount, aBytesRead);
00586 
00587     if (NS_SUCCEEDED(rv) && entry) {
00588         NS_ASSERTION(entry->mBytesLeft >= *aBytesRead, "demux Read underflow!");
00589         entry->mBytesLeft -= *aBytesRead;
00590 
00591 #ifdef NS_DEBUG
00592         // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
00593         if (entry->mBytesLeft == 0)
00594             entry->mSaveOffset = 0;
00595 #endif
00596     }
00597     return rv;
00598 }
00599 
00600 NS_IMETHODIMP
00601 nsFastLoadFileReader::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
00602                                    PRUint32 aCount, PRUint32 *aResult)
00603 {
00604     nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
00605 
00606     NS_ASSERTION(!entry || (!entry->mNeedToSeek && entry->mBytesLeft != 0),
00607                  "ReadSegments called from above nsFastLoadFileReader layer?!");
00608 
00609     nsresult rv = nsBinaryInputStream::ReadSegments(aWriter, aClosure, aCount,
00610                                                     aResult);
00611     if (NS_SUCCEEDED(rv) && entry) {
00612         NS_ASSERTION(entry->mBytesLeft >= *aResult,
00613                      "demux ReadSegments underflow!");
00614         entry->mBytesLeft -= *aResult;
00615 
00616 #ifdef NS_DEBUG
00617         // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
00618         if (entry->mBytesLeft == 0)
00619             entry->mSaveOffset = 0;
00620 #endif
00621     }
00622     return rv;
00623 }
00624 
00625 NS_IMETHODIMP
00626 nsFastLoadFileReader::SetInputStream(nsIInputStream *aInputStream)
00627 {
00628     nsresult rv = nsBinaryInputStream::SetInputStream(aInputStream);
00629     mSeekableInput = do_QueryInterface(aInputStream);
00630     NS_ASSERTION(!mInputStream || mSeekableInput,
00631                  "FastLoad requires a seekable input stream");
00632     return rv;
00633 }
00634 
00638 #define MFL_CHECKSUM_BUFSIZE    8192
00639 
00640 NS_IMETHODIMP
00641 nsFastLoadFileReader::ComputeChecksum(PRUint32 *aResult)
00642 {
00643     nsCOMPtr<nsIInputStream> stream = mInputStream;
00644     nsCOMPtr<nsISeekableStream> seekable = mSeekableInput;
00645 
00646     PRInt64 saveOffset;
00647     nsresult rv = seekable->Tell(&saveOffset);
00648     if (NS_FAILED(rv))
00649         return rv;
00650 
00651     if (mBufferAccess) {
00652         rv = mBufferAccess->GetUnbufferedStream(getter_AddRefs(stream));
00653         if (NS_FAILED(rv))
00654             return rv;
00655 
00656         seekable = do_QueryInterface(stream);
00657         if (!seekable)
00658             return NS_ERROR_UNEXPECTED;
00659     }
00660 
00661     rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
00662     if (NS_FAILED(rv))
00663         return rv;
00664 
00665     char buf[MFL_CHECKSUM_BUFSIZE];
00666     PRUint32 len, rem;
00667 
00668     rem = offsetof(nsFastLoadHeader, mChecksum);
00669     rv = stream->Read(buf, rem, &len);
00670     if (NS_FAILED(rv))
00671         return rv;
00672     if (len != rem)
00673         return NS_ERROR_UNEXPECTED;
00674 
00675     rv = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, 4);
00676     if (NS_FAILED(rv))
00677         return rv;
00678     memset(buf + rem, 0, 4);
00679     rem += 4;
00680 
00681     PRUint32 checksum = 0;
00682     while (NS_SUCCEEDED(rv = stream->Read(buf + rem, sizeof buf - rem, &len)) &&
00683            len) {
00684         len += rem;
00685         rem = NS_AccumulateFastLoadChecksum(&checksum,
00686                                             NS_REINTERPRET_CAST(PRUint8*, buf),
00687                                             len,
00688                                             PR_FALSE);
00689         if (rem)
00690             memcpy(buf, buf + len - rem, rem);
00691     }
00692     if (NS_FAILED(rv))
00693         return rv;
00694 
00695     if (rem) {
00696         NS_AccumulateFastLoadChecksum(&checksum,
00697                                       NS_REINTERPRET_CAST(PRUint8*, buf),
00698                                       rem,
00699                                       PR_TRUE);
00700     }
00701 
00702     rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
00703     if (NS_FAILED(rv))
00704         return rv;
00705 
00706     *aResult = checksum;
00707     return NS_OK;
00708 }
00709 
00710 NS_IMETHODIMP
00711 nsFastLoadFileReader::GetDependencies(nsISimpleEnumerator* *aDependencies)
00712 {
00713     return NS_NewArrayEnumerator(aDependencies, mFooter.mDependencies);
00714 }
00715 
00716 nsresult
00717 nsFastLoadFileReader::ReadFooter(nsFastLoadFooter *aFooter)
00718 {
00719     nsresult rv;
00720 
00721     rv = ReadFooterPrefix(aFooter);
00722     if (NS_FAILED(rv))
00723         return rv;
00724 
00725     aFooter->mIDMap = new nsID[aFooter->mNumIDs];
00726     if (!aFooter->mIDMap)
00727         return NS_ERROR_OUT_OF_MEMORY;
00728 
00729     PRUint32 i, n;
00730     for (i = 0, n = aFooter->mNumIDs; i < n; i++) {
00731         rv = ReadSlowID(&aFooter->mIDMap[i]);
00732         if (NS_FAILED(rv))
00733             return rv;
00734     }
00735 
00736     aFooter->mObjectMap = new nsObjectMapEntry[aFooter->mNumSharpObjects];
00737     if (!aFooter->mObjectMap)
00738         return NS_ERROR_OUT_OF_MEMORY;
00739 
00740     for (i = 0, n = aFooter->mNumSharpObjects; i < n; i++) {
00741         nsObjectMapEntry* entry = &aFooter->mObjectMap[i];
00742 
00743         rv = ReadSharpObjectInfo(entry);
00744         if (NS_FAILED(rv))
00745             return rv;
00746 
00747         entry->mReadObject = nsnull;
00748         entry->mSkipOffset = 0;
00749         entry->mSaveStrongRefCnt = entry->mStrongRefCnt;
00750         entry->mSaveWeakRefCnt = entry->mWeakRefCnt;
00751     }
00752 
00753     if (!PL_DHashTableInit(&aFooter->mDocumentMap, &strmap_DHashTableOps,
00754                            (void *)this, sizeof(nsDocumentMapReadEntry),
00755                            aFooter->mNumMuxedDocuments)) {
00756         aFooter->mDocumentMap.ops = nsnull;
00757         return NS_ERROR_OUT_OF_MEMORY;
00758     }
00759 
00760     if (!PL_DHashTableInit(&aFooter->mURIMap, &objmap_DHashTableOps,
00761                            (void *)this, sizeof(nsURIMapReadEntry),
00762                            aFooter->mNumMuxedDocuments)) {
00763         aFooter->mURIMap.ops = nsnull;
00764         return NS_ERROR_OUT_OF_MEMORY;
00765     }
00766 
00767     for (i = 0, n = aFooter->mNumMuxedDocuments; i < n; i++) {
00768         nsFastLoadMuxedDocumentInfo info;
00769 
00770         rv = ReadMuxedDocumentInfo(&info);
00771         if (NS_FAILED(rv))
00772             return rv;
00773 
00774         nsDocumentMapReadEntry* entry =
00775             NS_STATIC_CAST(nsDocumentMapReadEntry*,
00776                            PL_DHashTableOperate(&aFooter->mDocumentMap,
00777                                                 info.mURISpec,
00778                                                 PL_DHASH_ADD));
00779         if (!entry) {
00780             nsMemory::Free((void*) info.mURISpec);
00781             return NS_ERROR_OUT_OF_MEMORY;
00782         }
00783 
00784         NS_ASSERTION(!entry->mString, "duplicate URISpec in MuxedDocumentMap");
00785         entry->mString = info.mURISpec;
00786         entry->mURI = nsnull;
00787         entry->mInitialSegmentOffset = info.mInitialSegmentOffset;
00788         entry->mNextSegmentOffset = info.mInitialSegmentOffset;
00789         entry->mBytesLeft = 0;
00790         entry->mNeedToSeek = PR_FALSE;
00791         entry->mSaveOffset = 0;
00792     }
00793 
00794     nsCOMPtr<nsISupportsArray> readDeps;
00795     rv = NS_NewISupportsArray(getter_AddRefs(readDeps));
00796     if (NS_FAILED(rv))
00797         return rv;
00798 
00799     nsCAutoString filename;
00800     for (i = 0, n = aFooter->mNumDependencies; i < n; i++) {
00801         rv = ReadCString(filename);
00802         if (NS_FAILED(rv))
00803             return rv;
00804 
00805         PRInt64 fastLoadMtime;
00806         rv = Read64(NS_REINTERPRET_CAST(PRUint64*, &fastLoadMtime));
00807         if (NS_FAILED(rv))
00808             return rv;
00809 
00810         nsCOMPtr<nsILocalFile> file;
00811         rv = NS_NewNativeLocalFile(filename, PR_TRUE, getter_AddRefs(file));
00812         if (NS_FAILED(rv))
00813             return rv;
00814 
00815         PRInt64 currentMtime;
00816         rv = file->GetLastModifiedTime(&currentMtime);
00817         if (NS_FAILED(rv))
00818             return rv;
00819 
00820         if (LL_NE(fastLoadMtime, currentMtime)) {
00821 #ifdef DEBUG
00822             nsCAutoString path;
00823             file->GetNativePath(path);
00824             printf("%s mtime changed, invalidating FastLoad file\n",
00825                    path.get());
00826 #endif
00827             return NS_ERROR_FAILURE;
00828         }
00829 
00830         rv = readDeps->AppendElement(file);
00831         if (NS_FAILED(rv))
00832             return rv;
00833     }
00834 
00835     aFooter->mDependencies = readDeps;
00836     return NS_OK;
00837 }
00838 
00839 nsresult
00840 nsFastLoadFileReader::ReadFooterPrefix(nsFastLoadFooterPrefix *aFooterPrefix)
00841 {
00842     nsresult rv;
00843 
00844     rv = Read32(&aFooterPrefix->mNumIDs);
00845     if (NS_FAILED(rv))
00846         return rv;
00847 
00848     rv = Read32(&aFooterPrefix->mNumSharpObjects);
00849     if (NS_FAILED(rv))
00850         return rv;
00851 
00852     rv = Read32(&aFooterPrefix->mNumMuxedDocuments);
00853     if (NS_FAILED(rv))
00854         return rv;
00855 
00856     rv = Read32(&aFooterPrefix->mNumDependencies);
00857     if (NS_FAILED(rv))
00858         return rv;
00859 
00860     return NS_OK;
00861 }
00862 
00863 nsresult
00864 nsFastLoadFileReader::ReadSlowID(nsID *aID)
00865 {
00866     nsresult rv;
00867 
00868     rv = Read32(&aID->m0);
00869     if (NS_FAILED(rv))
00870         return rv;
00871 
00872     rv = Read16(&aID->m1);
00873     if (NS_FAILED(rv))
00874         return rv;
00875 
00876     rv = Read16(&aID->m2);
00877     if (NS_FAILED(rv))
00878         return rv;
00879 
00880     PRUint32 bytesRead;
00881     rv = Read(NS_REINTERPRET_CAST(char*, aID->m3), sizeof aID->m3, &bytesRead);
00882     if (NS_FAILED(rv))
00883         return rv;
00884 
00885     if (bytesRead != sizeof aID->m3)
00886         return NS_ERROR_FAILURE;
00887     return NS_OK;
00888 }
00889 
00890 nsresult
00891 nsFastLoadFileReader::ReadFastID(NSFastLoadID *aID)
00892 {
00893     nsresult rv = Read32(aID);
00894     if (NS_SUCCEEDED(rv))
00895         *aID ^= MFL_ID_XOR_KEY;
00896     return rv;
00897 }
00898 
00899 nsresult
00900 nsFastLoadFileReader::ReadSharpObjectInfo(nsFastLoadSharpObjectInfo *aInfo)
00901 {
00902     nsresult rv;
00903 
00904     rv = Read32(&aInfo->mCIDOffset);
00905     if (NS_FAILED(rv))
00906         return rv;
00907 
00908     NS_ASSERTION(aInfo->mCIDOffset != 0,
00909                  "fastload reader: mCIDOffset cannot be zero!");
00910 
00911     rv = Read16(&aInfo->mStrongRefCnt);
00912     if (NS_FAILED(rv))
00913         return rv;
00914 
00915     rv = Read16(&aInfo->mWeakRefCnt);
00916     if (NS_FAILED(rv))
00917         return rv;
00918 
00919     return NS_OK;
00920 }
00921 
00922 nsresult
00923 nsFastLoadFileReader::ReadMuxedDocumentInfo(nsFastLoadMuxedDocumentInfo *aInfo)
00924 {
00925     nsresult rv;
00926 
00927     nsCAutoString spec;
00928     rv = ReadCString(spec);
00929     if (NS_FAILED(rv))
00930         return rv;
00931 
00932     rv = Read32(&aInfo->mInitialSegmentOffset);
00933     if (NS_FAILED(rv))
00934         return rv;
00935 
00936     aInfo->mURISpec = ToNewCString(spec);
00937     return NS_OK;
00938 }
00939 
00940 nsresult
00941 nsFastLoadFileReader::Open()
00942 {
00943     nsresult rv;
00944 
00945     // Don't bother buffering the header, as we immediately seek to EOF.
00946     if (mBufferAccess)
00947         mBufferAccess->DisableBuffering();
00948 
00949     rv = ReadHeader(&mHeader);
00950 
00951     if (mBufferAccess)
00952         mBufferAccess->EnableBuffering();
00953     if (NS_FAILED(rv))
00954         return rv;
00955 
00956     if (mHeader.mVersion != MFL_FILE_VERSION)
00957         return NS_ERROR_UNEXPECTED;
00958     if (mHeader.mFooterOffset == 0)
00959         return NS_ERROR_UNEXPECTED;
00960 
00961     rv = mSeekableInput->Seek(nsISeekableStream::NS_SEEK_END, 0);
00962     if (NS_FAILED(rv))
00963         return rv;
00964 
00965     PRInt64 fileSize;
00966     rv = mSeekableInput->Tell(&fileSize);
00967     if (NS_FAILED(rv))
00968         return rv;
00969 
00970     nsInt64 fileSize64 = fileSize;
00971     const nsInt64 maxUint32 = PR_UINT32_MAX;
00972     NS_ASSERTION(fileSize64 <= maxUint32, "fileSize must fit in 32 bits");
00973     if ((PRUint32) fileSize64 != mHeader.mFileSize)
00974         return NS_ERROR_UNEXPECTED;
00975 
00976     rv = mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
00977                               PRInt32(mHeader.mFooterOffset));
00978     if (NS_FAILED(rv))
00979         return rv;
00980 
00981     rv = ReadFooter(&mFooter);
00982     if (NS_FAILED(rv))
00983         return rv;
00984 
00985     return mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
00986                                 sizeof(nsFastLoadHeader));
00987 }
00988 
00989 NS_IMETHODIMP
00990 nsFastLoadFileReader::Close()
00991 {
00992     // Give up our strong "keepalive" references, in case not all objects that
00993     // were deserialized were fully re-connected.
00994     //
00995     // This happens for sure when an nsFastLoadFileUpdater is created and wraps
00996     // an nsFastLoadFileReader whose data was already deserialized by an earlier
00997     // FastLoad episode.  The reader is useful in the second such episode during
00998     // a session not so much for reading objects as for its footer information,
00999     // which primes the updater's tables so that after the update completes, the
01000     // FastLoad file has a superset footer.
01001 
01002     for (PRUint32 i = 0, n = mFooter.mNumSharpObjects; i < n; i++) {
01003         nsObjectMapEntry* entry = &mFooter.mObjectMap[i];
01004         entry->mReadObject = nsnull;
01005     }
01006 
01007     return mInputStream->Close();
01008 }
01009 
01010 nsresult
01011 nsFastLoadFileReader::DeserializeObject(nsISupports* *aObject)
01012 {
01013     nsresult rv;
01014     NSFastLoadID fastCID;
01015 
01016     rv = ReadFastID(&fastCID);
01017     if (NS_FAILED(rv))
01018         return rv;
01019 
01020     const nsID& slowCID = mFooter.GetID(fastCID);
01021     nsCOMPtr<nsISupports> object(do_CreateInstance(slowCID, &rv));
01022     if (NS_FAILED(rv))
01023         return rv;
01024 
01025     nsCOMPtr<nsISerializable> serializable(do_QueryInterface(object));
01026     if (!serializable)
01027         return NS_ERROR_FAILURE;
01028 
01029     rv = serializable->Read(this);
01030     if (NS_FAILED(rv))
01031         return rv;
01032 
01033     *aObject = object;
01034     NS_ADDREF(*aObject);
01035     return NS_OK;
01036 }
01037 
01038 nsresult
01039 nsFastLoadFileReader::ReadObject(PRBool aIsStrongRef, nsISupports* *aObject)
01040 {
01041     nsresult rv;
01042     NSFastLoadOID oid;
01043 
01044     rv = Read32(&oid);
01045     if (NS_FAILED(rv))
01046         return rv;
01047     oid ^= MFL_OID_XOR_KEY;
01048 
01049     nsCOMPtr<nsISupports> object;
01050 
01051     if (oid == MFL_DULL_OBJECT_OID) {
01052         // A very dull object, defined at point of single (strong) reference.
01053         NS_ASSERTION(aIsStrongRef, "dull object read via weak ref!");
01054 
01055         rv = DeserializeObject(getter_AddRefs(object));
01056         if (NS_FAILED(rv))
01057             return rv;
01058     } else {
01059         NS_ASSERTION((oid & MFL_WEAK_REF_TAG) ==
01060                      (aIsStrongRef ? 0 : MFL_WEAK_REF_TAG),
01061                      "strong vs. weak ref deserialization mismatch!");
01062 
01063         nsObjectMapEntry* entry = &mFooter.GetSharpObjectEntry(oid);
01064 
01065         // Check whether we've already deserialized the object for this OID.
01066         object = entry->mReadObject;
01067         if (!object) {
01068             PRInt64 saveOffset;
01069             nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
01070 
01071             rv = mSeekableInput->Tell(&saveOffset);
01072             if (NS_FAILED(rv))
01073                 return rv;
01074 
01075             PRUint32 saveOffset32 = saveOffset;
01076             if (entry->mCIDOffset != saveOffset32) {
01077                 // We skipped deserialization of this object from its position
01078                 // earlier in the input stream, presumably due to the reference
01079                 // there being an nsFastLoadPtr, or (more likely) because the
01080                 // object was muxed in another document, and deserialization
01081                 // order does not match serialization order.  So we must seek
01082                 // back and read it now.
01083                 NS_ASSERTION(entry->mCIDOffset < saveOffset32,
01084                              "out of order object?!");
01085 
01086                 // Ape our Seek wrapper by clearing mCurrentDocumentMapEntry.
01087                 // This allows for a skipped object to be referenced from two
01088                 // or more multiplexed documents in the FastLoad file.
01089                 saveDocMapEntry = mCurrentDocumentMapEntry;
01090                 mCurrentDocumentMapEntry = nsnull;
01091                 rv = mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
01092                                           entry->mCIDOffset);
01093                 if (NS_FAILED(rv))
01094                     return rv;
01095             }
01096 
01097             rv = DeserializeObject(getter_AddRefs(object));
01098             if (NS_FAILED(rv))
01099                 return rv;
01100 
01101             if (entry->mCIDOffset != saveOffset32) {
01102                 // Save the "skip offset" in case we need to skip this object
01103                 // definition when reading forward, later on.
01104                 rv = mSeekableInput->Tell(&entry->mSkipOffset);
01105                 if (NS_FAILED(rv))
01106                     return rv;
01107 
01108                 // Restore stream offset and mCurrentDocumentMapEntry in case
01109                 // we're still reading forward through a part of the multiplex
01110                 // to get object definitions eagerly.
01111                 rv = mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
01112                                           saveOffset);
01113                 if (NS_FAILED(rv))
01114                     return rv;
01115                 mCurrentDocumentMapEntry = saveDocMapEntry;
01116             }
01117 
01118             // Save object until all refs have been deserialized.
01119             entry->mReadObject = object;
01120         } else {
01121             // What if we are at a definition that's already been read?  This
01122             // case arises when a sharp object's def is serialized before its
01123             // refs, while a non-defining ref is deserialized before the def.
01124             // We must skip over the object definition.
01125             if (oid & MFL_OBJECT_DEF_TAG) {
01126                 NS_ASSERTION(entry->mSkipOffset != 0, "impossible! see above");
01127 
01128                 // Since we are seeking within a muxed segment, we must adjust
01129                 // mBytesLeft, so that Seek called from Read will be triggered
01130                 // when mBytesLeft goes to zero.
01131                 PRInt64 currentOffset;
01132                 rv = mSeekableInput->Tell(&currentOffset);
01133                 if (NS_FAILED(rv))
01134                     return rv;
01135 
01136                 NS_ASSERTION(entry->mSkipOffset > (PRUint32)currentOffset,
01137                              "skipping backwards from object?!");
01138                 NS_ASSERTION(mCurrentDocumentMapEntry->mBytesLeft >=
01139                              entry->mSkipOffset - (PRUint32)currentOffset,
01140                              "skipped object buffer underflow!");
01141 
01142                 mCurrentDocumentMapEntry->mBytesLeft -=
01143                     entry->mSkipOffset - (PRUint32)currentOffset;
01144 
01145                 rv = mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
01146                                           entry->mSkipOffset);
01147                 if (NS_FAILED(rv))
01148                     return rv;
01149             }
01150         }
01151 
01152         if (aIsStrongRef) {
01153             NS_ASSERTION(entry->mStrongRefCnt != 0,
01154                          "mStrongRefCnt underflow!");
01155             --entry->mStrongRefCnt;
01156         } else {
01157             NS_ASSERTION(MFL_GET_WEAK_REFCNT(entry) != 0,
01158                          "mWeakRefCnt underflow!");
01159             MFL_DROP_WEAK_REFCNT(entry);
01160         }
01161 
01162         if (entry->mStrongRefCnt == 0 && MFL_GET_WEAK_REFCNT(entry) == 0)
01163             entry->mReadObject = nsnull;
01164     }
01165 
01166     if (oid & MFL_QUERY_INTERFACE_TAG) {
01167         NSFastLoadID iid;
01168         rv = ReadFastID(&iid);
01169         if (NS_FAILED(rv))
01170             return rv;
01171 
01172         rv = object->QueryInterface(mFooter.GetID(iid),
01173                                     NS_REINTERPRET_CAST(void**, aObject));
01174         if (NS_FAILED(rv))
01175             return rv;
01176     } else {
01177         *aObject = object;
01178         NS_ADDREF(*aObject);
01179     }
01180 
01181     return NS_OK;
01182 }
01183 
01184 NS_IMETHODIMP
01185 nsFastLoadFileReader::ReadID(nsID *aResult)
01186 {
01187     nsresult rv;
01188     NSFastLoadID fastID;
01189 
01190     rv = ReadFastID(&fastID);
01191     if (NS_FAILED(rv))
01192         return rv;
01193 
01194     *aResult = mFooter.GetID(fastID);
01195     return NS_OK;
01196 }
01197 
01198 NS_IMETHODIMP
01199 nsFastLoadFileReader::Seek(PRInt32 aWhence, PRInt64 aOffset)
01200 {
01201     mCurrentDocumentMapEntry = nsnull;
01202     return mSeekableInput->Seek(aWhence, aOffset);
01203 }
01204 
01205 NS_IMETHODIMP
01206 nsFastLoadFileReader::Tell(PRInt64 *aResult)
01207 {
01208     return mSeekableInput->Tell(aResult);
01209 }
01210 
01211 NS_IMETHODIMP
01212 nsFastLoadFileReader::SetEOF()
01213 {
01214     return mSeekableInput->SetEOF();
01215 }
01216 
01217 NS_COM nsresult
01218 NS_NewFastLoadFileReader(nsIObjectInputStream* *aResult,
01219                          nsIInputStream* aSrcStream)
01220 {
01221     nsFastLoadFileReader* reader = new nsFastLoadFileReader(aSrcStream);
01222     if (!reader)
01223         return NS_ERROR_OUT_OF_MEMORY;
01224 
01225     // Stabilize reader's refcnt.
01226     nsCOMPtr<nsIObjectInputStream> stream(reader);
01227 
01228     nsresult rv = reader->Open();
01229     if (NS_FAILED(rv))
01230         return rv;
01231 
01232     *aResult = stream;
01233     NS_ADDREF(*aResult);
01234     return NS_OK;
01235 }
01236 
01237 // -------------------------- nsFastLoadFileWriter --------------------------
01238 
01239 NS_IMPL_ISUPPORTS_INHERITED4(nsFastLoadFileWriter,
01240                              nsBinaryOutputStream,
01241                              nsIObjectOutputStream,
01242                              nsIFastLoadFileControl,
01243                              nsIFastLoadWriteControl,
01244                              nsISeekableStream)
01245 
01246 MOZ_DECL_CTOR_COUNTER(nsFastLoadFileWriter)
01247 
01248 struct nsIDMapEntry : public PLDHashEntryHdr {
01249     NSFastLoadID    mFastID;            // 1 + nsFastLoadFooter::mIDMap index
01250     nsID            mSlowID;            // key, used by PLDHashTableOps below
01251 };
01252 
01253 PR_STATIC_CALLBACK(const void *)
01254 idmap_GetKey(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
01255 {
01256     nsIDMapEntry* entry = NS_STATIC_CAST(nsIDMapEntry*, aHdr);
01257 
01258     return &entry->mSlowID;
01259 }
01260 
01261 PR_STATIC_CALLBACK(PLDHashNumber)
01262 idmap_HashKey(PLDHashTable *aTable, const void *aKey)
01263 {
01264     const nsID *idp = NS_REINTERPRET_CAST(const nsID*, aKey);
01265 
01266     return idp->m0;
01267 }
01268 
01269 PR_STATIC_CALLBACK(PRBool)
01270 idmap_MatchEntry(PLDHashTable *aTable,
01271                 const PLDHashEntryHdr *aHdr,
01272                 const void *aKey)
01273 {
01274     const nsIDMapEntry* entry = NS_STATIC_CAST(const nsIDMapEntry*, aHdr);
01275     const nsID *idp = NS_REINTERPRET_CAST(const nsID*, aKey);
01276 
01277     return memcmp(&entry->mSlowID, idp, sizeof(nsID)) == 0;
01278 }
01279 
01280 static const PLDHashTableOps idmap_DHashTableOps = {
01281     PL_DHashAllocTable,
01282     PL_DHashFreeTable,
01283     idmap_GetKey,
01284     idmap_HashKey,
01285     idmap_MatchEntry,
01286     PL_DHashMoveEntryStub,
01287     PL_DHashClearEntryStub,
01288     PL_DHashFinalizeStub,
01289     NULL
01290 };
01291 
01292 nsresult
01293 nsFastLoadFileWriter::MapID(const nsID& aSlowID, NSFastLoadID *aResult)
01294 {
01295     nsIDMapEntry* entry =
01296         NS_STATIC_CAST(nsIDMapEntry*,
01297                        PL_DHashTableOperate(&mIDMap, &aSlowID, PL_DHASH_ADD));
01298     if (!entry)
01299         return NS_ERROR_OUT_OF_MEMORY;
01300 
01301     if (entry->mFastID == 0) {
01302         entry->mFastID = mIDMap.entryCount;
01303         entry->mSlowID = aSlowID;
01304     }
01305 
01306     *aResult = entry->mFastID;
01307     return NS_OK;
01308 }
01309 
01310 nsresult
01311 nsFastLoadFileWriter::WriteHeader(nsFastLoadHeader *aHeader)
01312 {
01313     nsresult rv;
01314     PRUint32 bytesWritten;
01315 
01316     rv = Write(aHeader->mMagic, MFL_FILE_MAGIC_SIZE, &bytesWritten);
01317     if (NS_FAILED(rv))
01318         return rv;
01319 
01320     if (bytesWritten != MFL_FILE_MAGIC_SIZE)
01321         return NS_ERROR_FAILURE;
01322 
01323     rv = Write32(aHeader->mChecksum);
01324     if (NS_FAILED(rv))
01325         return rv;
01326 
01327     rv = Write32(aHeader->mVersion);
01328     if (NS_FAILED(rv))
01329         return rv;
01330 
01331     rv = Write32(aHeader->mFooterOffset);
01332     if (NS_FAILED(rv))
01333         return rv;
01334 
01335     rv = Write32(aHeader->mFileSize);
01336     if (NS_FAILED(rv))
01337         return rv;
01338 
01339     return NS_OK;
01340 }
01341 
01342 // nsIFastLoadFileControl methods:
01343 
01344 NS_IMETHODIMP
01345 nsFastLoadFileWriter::GetChecksum(PRUint32 *aChecksum)
01346 {
01347     if (mHeader.mChecksum == 0)
01348         return NS_ERROR_NOT_AVAILABLE;
01349     *aChecksum = mHeader.mChecksum;
01350     return NS_OK;
01351 }
01352 
01353 NS_IMETHODIMP
01354 nsFastLoadFileWriter::SetChecksum(PRUint32 aChecksum)
01355 {
01356     mHeader.mChecksum = aChecksum;
01357     return NS_OK;
01358 }
01359 
01360 struct nsDocumentMapWriteEntry : public nsDocumentMapEntry {
01361     PRUint32    mCurrentSegmentOffset;      // last written segment's offset
01362 };
01363 
01364 // Fast mapping from URI object pointer back to spec-indexed document info.
01365 // We also may need the slow mapping from mURISpec to nsDocumentMapWriteEntry,
01366 // because the writer's mDocumentMap double hash table may grow "behind the
01367 // back of" each mURIMap entry's mDocMapEntry member.
01368 struct nsURIMapWriteEntry : public nsObjectMapEntry {
01369     nsDocumentMapWriteEntry* mDocMapEntry;
01370     PRUint32                 mGeneration;
01371     const char*              mURISpec;
01372 };
01373 
01374 NS_IMETHODIMP
01375 nsFastLoadFileWriter::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
01376 {
01377     nsDocumentMapWriteEntry* docMapEntry =
01378         NS_STATIC_CAST(nsDocumentMapWriteEntry*,
01379                        PL_DHashTableOperate(&mDocumentMap, aURISpec,
01380                                             PL_DHASH_LOOKUP));
01381 
01382     *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
01383     return NS_OK;
01384 }
01385 
01386 NS_IMETHODIMP
01387 nsFastLoadFileWriter::StartMuxedDocument(nsISupports* aURI,
01388                                          const char* aURISpec)
01389 {
01390     // Save mDocumentMap table generation and mCurrentDocumentMapEntry key in
01391     // case the hash table grows during the PL_DHASH_ADD operation.
01392     PRUint32 saveGeneration = mDocumentMap.generation;
01393     const char* saveURISpec = mCurrentDocumentMapEntry
01394                               ? mCurrentDocumentMapEntry->mString
01395                               : nsnull;
01396 
01397     nsDocumentMapWriteEntry* docMapEntry =
01398         NS_STATIC_CAST(nsDocumentMapWriteEntry*,
01399                        PL_DHashTableOperate(&mDocumentMap, aURISpec,
01400                                             PL_DHASH_ADD));
01401     if (!docMapEntry)
01402         return NS_ERROR_OUT_OF_MEMORY;
01403 
01404     // If the generation number changed, refresh mCurrentDocumentMapEntry.
01405     if (mCurrentDocumentMapEntry && mDocumentMap.generation != saveGeneration) {
01406         mCurrentDocumentMapEntry =
01407             NS_STATIC_CAST(nsDocumentMapWriteEntry*,
01408                            PL_DHashTableOperate(&mDocumentMap, saveURISpec,
01409                                                 PL_DHASH_LOOKUP));
01410         NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(mCurrentDocumentMapEntry),
01411                      "mCurrentDocumentMapEntry lost during table growth?!");
01412 
01413         // Refresh saveGeneration for use below when initializing uriMapEntry.
01414         saveGeneration = mDocumentMap.generation;
01415     }
01416 
01417     NS_ASSERTION(docMapEntry->mString == nsnull,
01418                  "redundant multiplexed document?");
01419     if (docMapEntry->mString)
01420         return NS_ERROR_UNEXPECTED;
01421 
01422     void* spec = nsMemory::Clone(aURISpec, strlen(aURISpec) + 1);
01423     if (!spec)
01424         return NS_ERROR_OUT_OF_MEMORY;
01425     docMapEntry->mString = NS_REINTERPRET_CAST(const char*, spec);
01426     docMapEntry->mURI = aURI;
01427     NS_ADDREF(docMapEntry->mURI);
01428 
01429     nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
01430     nsURIMapWriteEntry* uriMapEntry =
01431         NS_STATIC_CAST(nsURIMapWriteEntry*,
01432                        PL_DHashTableOperate(&mURIMap, key, PL_DHASH_ADD));
01433     if (!uriMapEntry)
01434         return NS_ERROR_OUT_OF_MEMORY;
01435 
01436     NS_ASSERTION(uriMapEntry->mDocMapEntry == nsnull,
01437                  "URI mapped to two different specs?");
01438     if (uriMapEntry->mDocMapEntry)
01439         return NS_ERROR_UNEXPECTED;
01440 
01441     uriMapEntry->mObject = key;
01442     NS_ADDREF(uriMapEntry->mObject);
01443     uriMapEntry->mDocMapEntry = docMapEntry;
01444     uriMapEntry->mGeneration = saveGeneration;
01445     uriMapEntry->mURISpec = NS_REINTERPRET_CAST(const char*, spec);
01446     TRACE_MUX(('w', "start %p (%p) %s\n", aURI, key.get(), aURISpec));
01447     return NS_OK;
01448 }
01449 
01450 NS_IMETHODIMP
01451 nsFastLoadFileWriter::SelectMuxedDocument(nsISupports* aURI,
01452                                           nsISupports** aResult)
01453 {
01454     // Capture the current file offset (XXXbe maintain our own via Write?)
01455     nsresult rv;
01456     PRInt64 currentSegmentOffset;
01457     rv = mSeekableOutput->Tell(&currentSegmentOffset);
01458     if (NS_FAILED(rv))
01459         return rv;
01460 
01461     PRUint32 currentSegmentOffset32 = currentSegmentOffset;
01462     // Look for an existing entry keyed by aURI, added by StartMuxedDocument.
01463     nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
01464     nsURIMapWriteEntry* uriMapEntry =
01465         NS_STATIC_CAST(nsURIMapWriteEntry*,
01466                        PL_DHashTableOperate(&mURIMap, key, PL_DHASH_LOOKUP));
01467     NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(uriMapEntry),
01468                  "SelectMuxedDocument without prior StartMuxedDocument?");
01469     if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
01470         return NS_ERROR_UNEXPECTED;
01471 
01472     // Beware that uriMapEntry->mDocMapEntry may be stale, if an mDocumentMap
01473     // addition caused that table to grow.  We save the mDocumentMap generation
01474     // in each uriMapEntry and compare it to the current generation, rehashing
01475     // uriMapEntry->mURISpec if necessary.
01476 
01477     nsDocumentMapWriteEntry* docMapEntry = uriMapEntry->mDocMapEntry;
01478     if (uriMapEntry->mGeneration != mDocumentMap.generation) {
01479         docMapEntry =
01480             NS_STATIC_CAST(nsDocumentMapWriteEntry*,
01481                            PL_DHashTableOperate(&mDocumentMap,
01482                                                 uriMapEntry->mURISpec,
01483                                                 PL_DHASH_LOOKUP));
01484         NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(docMapEntry), "lost mDocMapEntry!?");
01485         uriMapEntry->mDocMapEntry = docMapEntry;
01486         uriMapEntry->mGeneration = mDocumentMap.generation;
01487     }
01488 
01489     // If there is a muxed document segment open, close it now by setting its
01490     // length, stored in the second PRUint32 of the segment.
01491     nsDocumentMapWriteEntry* prevDocMapEntry = mCurrentDocumentMapEntry;
01492     if (prevDocMapEntry) {
01493         if (prevDocMapEntry == docMapEntry) {
01494             TRACE_MUX(('w', "select prev %s same as current!\n",
01495                        prevDocMapEntry->mString));
01496             *aResult = docMapEntry->mURI;
01497             NS_ADDREF(*aResult);
01498             return NS_OK;
01499         }
01500 
01501         PRUint32 prevSegmentOffset = prevDocMapEntry->mCurrentSegmentOffset;
01502         TRACE_MUX(('w', "select prev %s offset %lu\n",
01503                    prevDocMapEntry->mString, prevSegmentOffset));
01504 
01505         rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
01506                                    prevSegmentOffset + 4);
01507         if (NS_FAILED(rv))
01508             return rv;
01509 
01510         // The length counts all bytes in the segment, including the header
01511         // that contains [nextSegmentOffset, length].
01512         rv = Write32(currentSegmentOffset32 - prevSegmentOffset);
01513         if (NS_FAILED(rv))
01514             return rv;
01515 
01516         // Seek back to the current offset only if we are not going to seek
01517         // back to *this* entry's last "current" segment offset and write its
01518         // next segment offset at the first PRUint32 of the segment.
01519         if (!docMapEntry->mInitialSegmentOffset) {
01520             rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
01521                                        currentSegmentOffset);
01522             if (NS_FAILED(rv))
01523                 return rv;
01524         }
01525     }
01526 
01527     // If this entry was newly added, set its key and initial segment offset.
01528     // Otherwise, seek back to write the next segment offset of the previous
01529     // segment for this document in the multiplex.
01530     if (!docMapEntry->mInitialSegmentOffset) {
01531         docMapEntry->mInitialSegmentOffset = currentSegmentOffset32;
01532     } else {
01533         rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
01534                                    docMapEntry->mCurrentSegmentOffset);
01535         if (NS_FAILED(rv))
01536             return rv;
01537 
01538         rv = Write32(currentSegmentOffset32);
01539         if (NS_FAILED(rv))
01540             return rv;
01541 
01542         rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
01543                                    currentSegmentOffset);
01544         if (NS_FAILED(rv))
01545             return rv;
01546     }
01547 
01548     // Update this document's current segment offset so we can later fix its
01549     // next segment offset (unless it is last, in which case we leave the zero
01550     // placeholder as a terminator).
01551     docMapEntry->mCurrentSegmentOffset = currentSegmentOffset32;
01552 
01553     rv = Write32(0);    // nextSegmentOffset placeholder
01554     if (NS_FAILED(rv))
01555         return rv;
01556 
01557     rv = Write32(0);    // length placeholder
01558     if (NS_FAILED(rv))
01559         return rv;
01560 
01561     *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
01562     NS_IF_ADDREF(*aResult);
01563 
01564     mCurrentDocumentMapEntry = docMapEntry;
01565     TRACE_MUX(('w', "select %p (%p) offset %lu\n",
01566                aURI, key.get(), currentSegmentOffset));
01567     return NS_OK;
01568 }
01569 
01570 NS_IMETHODIMP
01571 nsFastLoadFileWriter::EndMuxedDocument(nsISupports* aURI)
01572 {
01573     nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
01574     nsURIMapWriteEntry* uriMapEntry =
01575         NS_STATIC_CAST(nsURIMapWriteEntry*,
01576                        PL_DHashTableOperate(&mURIMap, key, PL_DHASH_LOOKUP));
01577 
01578     // If the URI isn't in the map, nsFastLoadFileWriter::StartMuxedDocument
01579     // must have been called with a redundant URI, *and* its caller must have
01580     // ignored the NS_ERROR_UNEXPECTED it returned in that case.
01581     if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry)) {
01582         TRACE_MUX(('w', "bad end %p (%p)\n", aURI, key.get()));
01583         return NS_ERROR_UNEXPECTED;
01584     }
01585 
01586     // Drop our ref to the URI object that was passed to StartMuxedDocument,
01587     // we no longer need it, and we do not want to extend its lifetime.
01588     if (uriMapEntry->mDocMapEntry)
01589         NS_RELEASE(uriMapEntry->mDocMapEntry->mURI);
01590 
01591     // Shrink the table if half the entries are removed sentinels.
01592     PRUint32 size = PL_DHASH_TABLE_SIZE(&mURIMap);
01593     if (mURIMap.removedCount >= (size >> 2))
01594         PL_DHashTableOperate(&mURIMap, key, PL_DHASH_REMOVE);
01595     else
01596         PL_DHashTableRawRemove(&mURIMap, uriMapEntry);
01597 
01598     TRACE_MUX(('w', "end %p (%p)\n", aURI, key.get()));
01599     return NS_OK;
01600 }
01601 
01602 struct nsDependencyMapEntry : public nsStringMapEntry {
01603     PRInt64 mLastModified;
01604 };
01605 
01606 NS_IMETHODIMP
01607 nsFastLoadFileWriter::AddDependency(nsIFile* aFile)
01608 {
01609     nsCAutoString path;
01610     nsresult rv = aFile->GetNativePath(path);
01611     if (NS_FAILED(rv))
01612         return rv;
01613 
01614     nsDependencyMapEntry* entry =
01615         NS_STATIC_CAST(nsDependencyMapEntry*,
01616                        PL_DHashTableOperate(&mDependencyMap, path.get(),
01617                                             PL_DHASH_ADD));
01618     if (!entry)
01619         return NS_ERROR_OUT_OF_MEMORY;
01620 
01621     if (!entry->mString) {
01622         const char *tmp = ToNewCString(path);
01623         if (!tmp)
01624             return NS_ERROR_OUT_OF_MEMORY;
01625         entry->mString = tmp;
01626 
01627         // If we can't get the last modified time from aFile, assume it does
01628         // not exist, or is otherwise inaccessible to us (due to permissions),
01629         // remove the dependency, and suppress the failure.
01630         //
01631         // Otherwise, we would end up aborting the fastload process due to a
01632         // missing .js or .xul or other file on every startup.
01633 
01634         rv = aFile->GetLastModifiedTime(&entry->mLastModified);
01635         if (NS_FAILED(rv)) {
01636             PL_DHashTableOperate(&mDependencyMap, path.get(), PL_DHASH_REMOVE);
01637             rv = NS_OK;
01638         }
01639     }
01640     return rv;
01641 }
01642 
01643 nsresult
01644 nsFastLoadFileWriter::WriteFooterPrefix(const nsFastLoadFooterPrefix& aFooterPrefix)
01645 {
01646     nsresult rv;
01647 
01648     rv = Write32(aFooterPrefix.mNumIDs);
01649     if (NS_FAILED(rv))
01650         return rv;
01651 
01652     rv = Write32(aFooterPrefix.mNumSharpObjects);
01653     if (NS_FAILED(rv))
01654         return rv;
01655 
01656     rv = Write32(aFooterPrefix.mNumMuxedDocuments);
01657     if (NS_FAILED(rv))
01658         return rv;
01659 
01660     rv = Write32(aFooterPrefix.mNumDependencies);
01661     if (NS_FAILED(rv))
01662         return rv;
01663 
01664     return NS_OK;
01665 }
01666 
01667 nsresult
01668 nsFastLoadFileWriter::WriteSlowID(const nsID& aID)
01669 {
01670     nsresult rv;
01671 
01672     rv = Write32(aID.m0);
01673     if (NS_FAILED(rv))
01674         return rv;
01675 
01676     rv = Write16(aID.m1);
01677     if (NS_FAILED(rv))
01678         return rv;
01679 
01680     rv = Write16(aID.m2);
01681     if (NS_FAILED(rv))
01682         return rv;
01683 
01684     PRUint32 bytesWritten;
01685     rv = Write(NS_REINTERPRET_CAST(const char*, aID.m3), sizeof aID.m3,
01686                &bytesWritten);
01687     if (NS_FAILED(rv))
01688         return rv;
01689 
01690     if (bytesWritten != sizeof aID.m3)
01691         return NS_ERROR_FAILURE;
01692     return NS_OK;
01693 }
01694 
01695 nsresult
01696 nsFastLoadFileWriter::WriteFastID(NSFastLoadID aID)
01697 {
01698     return Write32(aID ^ MFL_ID_XOR_KEY);
01699 }
01700 
01701 nsresult
01702 nsFastLoadFileWriter::WriteSharpObjectInfo(const nsFastLoadSharpObjectInfo& aInfo)
01703 {
01704     nsresult rv;
01705 
01706     NS_ASSERTION(aInfo.mCIDOffset != 0,
01707                  "fastload writer: mCIDOffset cannot be zero!");
01708 
01709     rv = Write32(aInfo.mCIDOffset);
01710     if (NS_FAILED(rv))
01711         return rv;
01712 
01713     rv = Write16(aInfo.mStrongRefCnt);
01714     if (NS_FAILED(rv))
01715         return rv;
01716 
01717     rv = Write16(aInfo.mWeakRefCnt);
01718     if (NS_FAILED(rv))
01719         return rv;
01720 
01721     return NS_OK;
01722 }
01723 
01724 nsresult
01725 nsFastLoadFileWriter::WriteMuxedDocumentInfo(const nsFastLoadMuxedDocumentInfo& aInfo)
01726 {
01727     nsresult rv;
01728 
01729     rv = WriteStringZ(aInfo.mURISpec);
01730     if (NS_FAILED(rv))
01731         return rv;
01732 
01733     rv = Write32(aInfo.mInitialSegmentOffset);
01734     if (NS_FAILED(rv))
01735         return rv;
01736 
01737     return NS_OK;
01738 }
01739 
01740 PLDHashOperator PR_CALLBACK
01741 nsFastLoadFileWriter::IDMapEnumerate(PLDHashTable *aTable,
01742                                      PLDHashEntryHdr *aHdr,
01743                                      PRUint32 aNumber,
01744                                      void *aData)
01745 {
01746     nsIDMapEntry* entry = NS_STATIC_CAST(nsIDMapEntry*, aHdr);
01747     PRUint32 index = entry->mFastID - 1;
01748     nsID* vector = NS_REINTERPRET_CAST(nsID*, aData);
01749 
01750     NS_ASSERTION(index < aTable->entryCount, "bad nsIDMap index!");
01751     vector[index] = entry->mSlowID;
01752     return PL_DHASH_NEXT;
01753 }
01754 
01755 struct nsSharpObjectMapEntry : public nsObjectMapEntry {
01756     NSFastLoadOID               mOID;
01757     nsFastLoadSharpObjectInfo   mInfo;
01758 };
01759 
01760 PLDHashOperator PR_CALLBACK
01761 nsFastLoadFileWriter::ObjectMapEnumerate(PLDHashTable *aTable,
01762                                          PLDHashEntryHdr *aHdr,
01763                                          PRUint32 aNumber,
01764                                          void *aData)
01765 {
01766     nsSharpObjectMapEntry* entry = NS_STATIC_CAST(nsSharpObjectMapEntry*, aHdr);
01767     PRUint32 index = MFL_OID_TO_SHARP_INDEX(entry->mOID);
01768     nsFastLoadSharpObjectInfo* vector =
01769         NS_REINTERPRET_CAST(nsFastLoadSharpObjectInfo*, aData);
01770 
01771     NS_ASSERTION(index < aTable->entryCount, "bad nsObjectMap index!");
01772     vector[index] = entry->mInfo;
01773 
01774     NS_ASSERTION(entry->mInfo.mStrongRefCnt, "no strong ref in serialization!");
01775 
01776     // Ignore tagged object ids stored as object pointer keys (the updater
01777     // code does this).
01778     if ((NS_PTR_TO_INT32(entry->mObject) & MFL_OBJECT_DEF_TAG) == 0)
01779         NS_RELEASE(entry->mObject);
01780 
01781     return PL_DHASH_NEXT;
01782 }
01783 
01784 PLDHashOperator PR_CALLBACK
01785 nsFastLoadFileWriter::DocumentMapEnumerate(PLDHashTable *aTable,
01786                                            PLDHashEntryHdr *aHdr,
01787                                            PRUint32 aNumber,
01788                                            void *aData)
01789 {
01790     nsFastLoadFileWriter* writer =
01791         NS_REINTERPRET_CAST(nsFastLoadFileWriter*, aTable->data);
01792     nsDocumentMapWriteEntry* entry =
01793         NS_STATIC_CAST(nsDocumentMapWriteEntry*, aHdr);
01794     nsresult* rvp = NS_REINTERPRET_CAST(nsresult*, aData);
01795 
01796     nsFastLoadMuxedDocumentInfo info;
01797     info.mURISpec = entry->mString;
01798     info.mInitialSegmentOffset = entry->mInitialSegmentOffset;
01799     *rvp = writer->WriteMuxedDocumentInfo(info);
01800 
01801     return NS_FAILED(*rvp) ? PL_DHASH_STOP : PL_DHASH_NEXT;
01802 }
01803 
01804 PLDHashOperator PR_CALLBACK
01805 nsFastLoadFileWriter::DependencyMapEnumerate(PLDHashTable *aTable,
01806                                              PLDHashEntryHdr *aHdr,
01807                                              PRUint32 aNumber,
01808                                              void *aData)
01809 {
01810     nsFastLoadFileWriter* writer =
01811         NS_REINTERPRET_CAST(nsFastLoadFileWriter*, aTable->data);
01812     nsDependencyMapEntry* entry = NS_STATIC_CAST(nsDependencyMapEntry*, aHdr);
01813     nsresult* rvp = NS_REINTERPRET_CAST(nsresult*, aData);
01814 
01815     *rvp = writer->WriteStringZ(entry->mString);
01816     if (NS_SUCCEEDED(*rvp))
01817         *rvp = writer->Write64(entry->mLastModified);
01818 
01819     return NS_FAILED(*rvp) ? PL_DHASH_STOP :PL_DHASH_NEXT;
01820 }
01821 
01822 nsresult
01823 nsFastLoadFileWriter::WriteFooter()
01824 {
01825     nsresult rv;
01826     PRUint32 i, count;
01827 
01828     nsFastLoadFooterPrefix footerPrefix;
01829     footerPrefix.mNumIDs = mIDMap.entryCount;
01830     footerPrefix.mNumSharpObjects = mObjectMap.entryCount;
01831     footerPrefix.mNumMuxedDocuments = mDocumentMap.entryCount;
01832     footerPrefix.mNumDependencies = mDependencyMap.entryCount;
01833 
01834     rv = WriteFooterPrefix(footerPrefix);
01835     if (NS_FAILED(rv))
01836         return rv;
01837 
01838     // Enumerate mIDMap into a vector indexed by mFastID and write it.
01839     nsID* idvec = new nsID[footerPrefix.mNumIDs];
01840     if (!idvec)
01841         return NS_ERROR_OUT_OF_MEMORY;
01842 
01843     count = PL_DHashTableEnumerate(&mIDMap, IDMapEnumerate, idvec);
01844     NS_ASSERTION(count == footerPrefix.mNumIDs, "bad mIDMap enumeration!");
01845     for (i = 0; i < count; i++) {
01846         rv = WriteSlowID(idvec[i]);
01847         if (NS_FAILED(rv)) break;
01848     }
01849 
01850     delete[] idvec;
01851     if (NS_FAILED(rv))
01852         return rv;
01853 
01854     // Enumerate mObjectMap into a vector indexed by mOID and write it.
01855     nsFastLoadSharpObjectInfo* objvec =
01856         new nsFastLoadSharpObjectInfo[footerPrefix.mNumSharpObjects];
01857     if (!objvec)
01858         return NS_ERROR_OUT_OF_MEMORY;
01859 #ifdef NS_DEBUG
01860     memset(objvec, 0, footerPrefix.mNumSharpObjects *
01861                       sizeof(nsFastLoadSharpObjectInfo));
01862 #endif
01863 
01864     count = PL_DHashTableEnumerate(&mObjectMap, ObjectMapEnumerate, objvec);
01865     NS_ASSERTION(count == footerPrefix.mNumSharpObjects,
01866                  "bad mObjectMap enumeration!");
01867     for (i = 0; i < count; i++) {
01868         rv = WriteSharpObjectInfo(objvec[i]);
01869         if (NS_FAILED(rv)) break;
01870     }
01871 
01872     delete[] objvec;
01873     if (NS_FAILED(rv))
01874         return rv;
01875 
01876     // Enumerate mDocumentMap, writing nsFastLoadMuxedDocumentInfo records
01877     count = PL_DHashTableEnumerate(&mDocumentMap, DocumentMapEnumerate, &rv);
01878     if (NS_FAILED(rv))
01879         return rv;
01880 
01881     NS_ASSERTION(count == footerPrefix.mNumMuxedDocuments,
01882                  "bad mDocumentMap enumeration!");
01883 
01884     // Write out make-like file dependencies.
01885     count = PL_DHashTableEnumerate(&mDependencyMap, DependencyMapEnumerate, &rv);
01886     if (NS_FAILED(rv))
01887         return rv;
01888 
01889     return NS_OK;
01890 }
01891 
01892 nsresult
01893 nsFastLoadFileWriter::Init()
01894 {
01895     if (!PL_DHashTableInit(&mIDMap, &idmap_DHashTableOps, (void *)this,
01896                            sizeof(nsIDMapEntry), PL_DHASH_MIN_SIZE)) {
01897         mIDMap.ops = nsnull;
01898         return NS_ERROR_OUT_OF_MEMORY;
01899     }
01900 
01901     if (!PL_DHashTableInit(&mObjectMap, &objmap_DHashTableOps, (void *)this,
01902                            sizeof(nsSharpObjectMapEntry), PL_DHASH_MIN_SIZE)) {
01903         mObjectMap.ops = nsnull;
01904         return NS_ERROR_OUT_OF_MEMORY;
01905     }
01906 
01907     if (!PL_DHashTableInit(&mDocumentMap, &strmap_DHashTableOps, (void *)this,
01908                            sizeof(nsDocumentMapWriteEntry),
01909                            PL_DHASH_MIN_SIZE)) {
01910         mDocumentMap.ops = nsnull;
01911         return NS_ERROR_OUT_OF_MEMORY;
01912     }
01913 
01914     if (!PL_DHashTableInit(&mURIMap, &objmap_DHashTableOps, (void *)this,
01915                            sizeof(nsURIMapWriteEntry), PL_DHASH_MIN_SIZE)) {
01916         mURIMap.ops = nsnull;
01917         return NS_ERROR_OUT_OF_MEMORY;
01918     }
01919 
01920     if (!PL_DHashTableInit(&mDependencyMap, &strmap_DHashTableOps, (void *)this,
01921                            sizeof(nsDependencyMapEntry), PL_DHASH_MIN_SIZE)) {
01922         mDependencyMap.ops = nsnull;
01923         return NS_ERROR_OUT_OF_MEMORY;
01924     }
01925 
01926     return NS_OK;
01927 }
01928 
01929 nsresult
01930 nsFastLoadFileWriter::Open()
01931 {
01932     nsresult rv;
01933 
01934     rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
01935                                sizeof(nsFastLoadHeader));
01936     if (NS_FAILED(rv))
01937         return rv;
01938 
01939     return Init();
01940 }
01941 
01942 NS_IMETHODIMP
01943 nsFastLoadFileWriter::Close()
01944 {
01945     nsresult rv;
01946 
01947     memcpy(mHeader.mMagic, magic, MFL_FILE_MAGIC_SIZE);
01948     mHeader.mChecksum = 0;
01949     mHeader.mVersion = MFL_FILE_VERSION;
01950 
01951     PRInt64 footerOffset;
01952     rv = mSeekableOutput->Tell(&footerOffset);
01953 
01954     LL_L2UI(mHeader.mFooterOffset, footerOffset);
01955     if (NS_FAILED(rv))
01956         return rv;
01957 
01958     // If there is a muxed document segment open, close it now by setting its
01959     // length, stored in the second PRUint32 of the segment.
01960     if (mCurrentDocumentMapEntry) {
01961         PRUint32 currentSegmentOffset =
01962             mCurrentDocumentMapEntry->mCurrentSegmentOffset;
01963         rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
01964                                    currentSegmentOffset + 4);
01965         if (NS_FAILED(rv))
01966             return rv;
01967 
01968         rv = Write32(mHeader.mFooterOffset - currentSegmentOffset);
01969         if (NS_FAILED(rv))
01970             return rv;
01971 
01972         // Seek back to the current offset to write the footer.
01973         rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
01974                                    mHeader.mFooterOffset);
01975         if (NS_FAILED(rv))
01976             return rv;
01977 
01978         mCurrentDocumentMapEntry = nsnull;
01979     }
01980 
01981     rv = WriteFooter();
01982     if (NS_FAILED(rv))
01983         return rv;
01984     PRInt64 fileSize;
01985     rv = mSeekableOutput->Tell(&fileSize);
01986     LL_L2UI(mHeader.mFileSize, fileSize);
01987     if (NS_FAILED(rv))
01988         return rv;
01989 
01990     rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET, 0);
01991     if (NS_FAILED(rv))
01992         return rv;
01993 
01994     rv = WriteHeader(&mHeader);
01995     if (NS_FAILED(rv))
01996         return rv;
01997 
01998     // Now compute the checksum, using mFileIO to get an input stream on the
01999     // underlying FastLoad file.
02000     if (mFileIO) {
02001         // Get the unbuffered output stream, which flushes the buffered header
02002         // so we can read and checksum it along with the rest of the file, and
02003         // which allows us to write the checksum directly.
02004         nsCOMPtr<nsIOutputStream> output;
02005         rv = mBufferAccess->GetUnbufferedStream(getter_AddRefs(output));
02006         if (NS_FAILED(rv) || !output)
02007             return NS_ERROR_UNEXPECTED;
02008 
02009         nsCOMPtr<nsIInputStream> input;
02010         rv = mFileIO->GetInputStream(getter_AddRefs(input));
02011         if (NS_FAILED(rv))
02012             return rv;
02013 
02014         // Get the unbuffered input stream, to avoid copying overhead and to
02015         // keep our view of the file coherent with the writer -- we don't want
02016         // to hit a stale buffer in the reader's underlying stream.
02017         nsCOMPtr<nsIStreamBufferAccess> bufferAccess =
02018             do_QueryInterface(input);
02019         rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(input));
02020         if (NS_FAILED(rv) || !input)
02021             return NS_ERROR_UNEXPECTED;
02022 
02023         // Seek the input stream to offset 0, in case it's a reader who has
02024         // already been used to consume some of the FastLoad file.
02025         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input);
02026         rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
02027         if (NS_FAILED(rv))
02028             return rv;
02029 
02030         char buf[MFL_CHECKSUM_BUFSIZE];
02031         PRUint32 len, rem = 0;
02032         PRUint32 checksum = 0;
02033 
02034         // Ok, we're finally ready to checksum the FastLoad file we just wrote!
02035         while (NS_SUCCEEDED(rv =
02036                             input->Read(buf + rem, sizeof buf - rem, &len)) &&
02037                len) {
02038             len += rem;
02039             rem = NS_AccumulateFastLoadChecksum(&checksum,
02040                                                 NS_REINTERPRET_CAST(PRUint8*,
02041                                                                     buf),
02042                                                 len,
02043                                                 PR_FALSE);
02044             if (rem)
02045                 memcpy(buf, buf + len - rem, rem);
02046         }
02047         if (NS_FAILED(rv))
02048             return rv;
02049 
02050         if (rem) {
02051             NS_AccumulateFastLoadChecksum(&checksum,
02052                                           NS_REINTERPRET_CAST(PRUint8*, buf),
02053                                           rem,
02054                                           PR_TRUE);
02055         }
02056 
02057         // Store the checksum in the FastLoad file header and remember it via
02058         // mHeader.mChecksum, for GetChecksum.
02059         seekable = do_QueryInterface(output);
02060         rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
02061                             offsetof(nsFastLoadHeader, mChecksum));
02062         if (NS_FAILED(rv))
02063             return rv;
02064 
02065         mHeader.mChecksum = checksum;
02066         checksum = NS_SWAP32(checksum);
02067         PRUint32 bytesWritten;
02068         rv = output->Write(NS_REINTERPRET_CAST(char*, &checksum),
02069                            sizeof checksum,
02070                            &bytesWritten);
02071         if (NS_FAILED(rv))
02072             return rv;
02073         if (bytesWritten != sizeof checksum)
02074             return NS_ERROR_FAILURE;
02075     }
02076 
02077     return mOutputStream->Close();
02078 }
02079 
02080 // Pseudo-tag used as flag between WriteSingleRefObject and WriteObjectCommon.
02081 #define MFL_SINGLE_REF_PSEUDO_TAG       PR_BIT(MFL_OBJECT_TAG_BITS)
02082 
02083 nsresult
02084 nsFastLoadFileWriter::WriteObjectCommon(nsISupports* aObject,
02085                                         PRBool aIsStrongRef,
02086                                         PRUint32 aTags)
02087 {
02088     nsrefcnt rc;
02089     nsresult rv;
02090 
02091     NS_ASSERTION((NS_PTR_TO_INT32(aObject) & MFL_OBJECT_DEF_TAG) == 0,
02092                  "odd nsISupports*, oh no!");
02093 
02094     // Here be manual refcounting dragons!
02095     rc = aObject->AddRef();
02096     NS_ASSERTION(rc != 0, "bad refcnt when writing aObject!");
02097 
02098     NSFastLoadOID oid;
02099     nsCOMPtr<nsIClassInfo> classInfo;
02100 
02101     if (rc == 2 && (aTags & MFL_SINGLE_REF_PSEUDO_TAG)) {
02102         // Dull object: only one strong ref and no weak refs in serialization.
02103         // Conservative: we don't trust the caller if there are more than two
02104         // refs (one from the AddRef above, one from the data structure that's
02105         // being serialized).
02106         oid = MFL_DULL_OBJECT_OID;
02107         aObject->Release();
02108     } else {
02109         // Object is presumed to be multiply connected through some combo of
02110         // strong and weak refs.  Hold onto it via mObjectMap.
02111         nsSharpObjectMapEntry* entry =
02112             NS_STATIC_CAST(nsSharpObjectMapEntry*,
02113                            PL_DHashTableOperate(&mObjectMap, aObject,
02114                                                 PL_DHASH_ADD));
02115         if (!entry) {
02116             aObject->Release();
02117             return NS_ERROR_OUT_OF_MEMORY;
02118         }
02119 
02120         if (!entry->mObject) {
02121             // First time we've seen this object address: add it to mObjectMap
02122             // and serialize the object at the current stream offset.
02123             PRInt64 thisOffset;
02124             rv = Tell(&thisOffset);
02125             if (NS_FAILED(rv)) {
02126                 aObject->Release();
02127                 return rv;
02128             }
02129 
02130             // NB: aObject was already held, and mObject is a raw nsISupports*.
02131             entry->mObject = aObject;
02132 
02133             oid = (mObjectMap.entryCount << MFL_OBJECT_TAG_BITS);
02134             entry->mOID = oid;
02135 
02136             // NB: the (32-bit, fast) CID and object data follow the OID.
02137             entry->mInfo.mCIDOffset = thisOffset + sizeof(oid);
02138             entry->mInfo.mStrongRefCnt = aIsStrongRef ? 1 : 0;
02139             entry->mInfo.mWeakRefCnt   = aIsStrongRef ? 0 : 1;
02140 
02141             // Record in oid the fact that we're defining this object in the
02142             // stream, and get the object's class info here, so we can take
02143             // note of singletons in order to avoid reserializing them when
02144             // updating after reading.
02145             oid |= MFL_OBJECT_DEF_TAG;
02146             classInfo = do_QueryInterface(aObject);
02147             if (!classInfo) {
02148                 NS_NOTREACHED("aObject must implement nsIClassInfo");
02149                 return NS_ERROR_FAILURE;
02150             }
02151 
02152             PRUint32 flags;
02153             if (NS_SUCCEEDED(classInfo->GetFlags(&flags)) &&
02154                 (flags & nsIClassInfo::SINGLETON)) {
02155                 MFL_SET_SINGLETON_FLAG(&entry->mInfo);
02156             }
02157         } else {
02158             // Already serialized, recover oid and update the desired refcnt.
02159             oid = entry->mOID;
02160             if (aIsStrongRef) {
02161                 ++entry->mInfo.mStrongRefCnt;
02162                 NS_ASSERTION(entry->mInfo.mStrongRefCnt != 0,
02163                              "mStrongRefCnt overflow");
02164             } else {
02165                 MFL_BUMP_WEAK_REFCNT(&entry->mInfo);
02166                 NS_ASSERTION(MFL_GET_WEAK_REFCNT(&entry->mInfo) != 0,
02167                              "mWeakRefCnt overflow");
02168             }
02169 
02170             aObject->Release();
02171         }
02172     }
02173 
02174     if (!aIsStrongRef)
02175         oid |= MFL_WEAK_REF_TAG;
02176     oid |= (aTags & MFL_QUERY_INTERFACE_TAG);
02177 
02178     rv = Write32(oid ^ MFL_OID_XOR_KEY);
02179     if (NS_FAILED(rv))
02180         return rv;
02181 
02182     if (oid & MFL_OBJECT_DEF_TAG) {
02183         nsCOMPtr<nsISerializable> serializable(do_QueryInterface(aObject));
02184         if (!serializable) {
02185             NS_NOTREACHED("aObject must implement nsISerializable");
02186             return NS_ERROR_FAILURE;
02187         }
02188 
02189         nsCID slowCID;
02190         rv = classInfo->GetClassIDNoAlloc(&slowCID);
02191         if (NS_FAILED(rv))
02192             return rv;
02193 
02194         NSFastLoadID fastCID;
02195         rv = MapID(slowCID, &fastCID);
02196         if (NS_FAILED(rv))
02197             return rv;
02198 
02199         rv = WriteFastID(fastCID);
02200         if (NS_FAILED(rv))
02201             return rv;
02202 
02203         rv = serializable->Write(this);
02204         if (NS_FAILED(rv))
02205             return rv;
02206     }
02207 
02208     return NS_OK;
02209 }
02210 
02211 NS_IMETHODIMP
02212 nsFastLoadFileWriter::WriteObject(nsISupports* aObject, PRBool aIsStrongRef)
02213 {
02214 #ifdef NS_DEBUG
02215     nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
02216 
02217     NS_ASSERTION(rootObject.get() == aObject,
02218                  "bad call to WriteObject -- call WriteCompoundObject!");
02219 #endif
02220 
02221     return WriteObjectCommon(aObject, aIsStrongRef, 0);
02222 }
02223 
02224 NS_IMETHODIMP
02225 nsFastLoadFileWriter::WriteSingleRefObject(nsISupports* aObject)
02226 {
02227 #ifdef NS_DEBUG
02228     nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
02229 
02230     NS_ASSERTION(rootObject.get() == aObject,
02231                  "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
02232 #endif
02233 
02234     return WriteObjectCommon(aObject, PR_TRUE, MFL_SINGLE_REF_PSEUDO_TAG);
02235 }
02236 
02237 NS_IMETHODIMP
02238 nsFastLoadFileWriter::WriteCompoundObject(nsISupports* aObject,
02239                                           const nsIID& aIID,
02240                                           PRBool aIsStrongRef)
02241 {
02242     nsresult rv;
02243     nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
02244     
02245     // We could assert that |rootObject != aObject|, but that would prevent
02246     // callers who don't know whether they're dealing with the primary
02247     // nsISupports pointer (e.g., they don't know which implementation of
02248     // nsIURI they have) from using this function.
02249 
02250 #ifdef NS_DEBUG
02251     nsCOMPtr<nsISupports> roundtrip;
02252     rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
02253     NS_ASSERTION(roundtrip.get() == aObject,
02254                  "bad aggregation or multiple inheritance detected by call to "
02255                  "WriteCompoundObject!");
02256 #endif
02257 
02258     rv = WriteObjectCommon(rootObject, aIsStrongRef, MFL_QUERY_INTERFACE_TAG);
02259     if (NS_FAILED(rv))
02260         return rv;
02261 
02262     NSFastLoadID iid;
02263     rv = MapID(aIID, &iid);
02264     if (NS_FAILED(rv))
02265         return rv;
02266 
02267     return WriteFastID(iid);
02268 }
02269 
02270 NS_IMETHODIMP
02271 nsFastLoadFileWriter::WriteID(const nsID& aID)
02272 {
02273     nsresult rv;
02274     NSFastLoadID fastID;
02275 
02276     rv = MapID(aID, &fastID);
02277     if (NS_FAILED(rv))
02278         return rv;
02279 
02280     return WriteFastID(fastID);
02281 }
02282 
02283 NS_IMETHODIMP
02284 nsFastLoadFileWriter::Seek(PRInt32 aWhence, PRInt64 aOffset)
02285 {
02286     mCurrentDocumentMapEntry = nsnull;
02287     return mSeekableOutput->Seek(aWhence, aOffset);
02288 }
02289 
02290 NS_IMETHODIMP
02291 nsFastLoadFileWriter::Tell(PRInt64 *aResult)
02292 {
02293     return mSeekableOutput->Tell(aResult);
02294 }
02295 
02296 NS_IMETHODIMP
02297 nsFastLoadFileWriter::SetEOF()
02298 {
02299     return mSeekableOutput->SetEOF();
02300 }
02301 
02302 NS_IMETHODIMP
02303 nsFastLoadFileWriter::SetOutputStream(nsIOutputStream *aStream)
02304 {
02305     nsresult rv = nsBinaryOutputStream::SetOutputStream(aStream);
02306     mSeekableOutput = do_QueryInterface(mOutputStream);
02307     return rv;
02308 }
02309 
02310 NS_COM nsresult
02311 NS_NewFastLoadFileWriter(nsIObjectOutputStream* *aResult,
02312                          nsIOutputStream* aDestStream,
02313                          nsIFastLoadFileIO* aFileIO)
02314 {
02315     nsFastLoadFileWriter* writer =
02316         new nsFastLoadFileWriter(aDestStream, aFileIO);
02317     if (!writer)
02318         return NS_ERROR_OUT_OF_MEMORY;
02319 
02320     // Stabilize writer's refcnt.
02321     nsCOMPtr<nsIObjectOutputStream> stream(writer);
02322 
02323     nsresult rv = writer->Open();
02324     if (NS_FAILED(rv))
02325         return rv;
02326 
02327     *aResult = stream;
02328     NS_ADDREF(*aResult);
02329     return NS_OK;
02330 }
02331 
02332 // -------------------------- nsFastLoadFileUpdater --------------------------
02333 
02334 NS_IMPL_ISUPPORTS_INHERITED1(nsFastLoadFileUpdater,
02335                              nsFastLoadFileWriter,
02336                              nsIFastLoadFileIO)
02337 
02338 NS_IMETHODIMP
02339 nsFastLoadFileUpdater::GetInputStream(nsIInputStream** aResult)
02340 {
02341     *aResult = mInputStream;
02342     NS_IF_ADDREF(*aResult);
02343     return NS_OK;
02344 }
02345 
02346 NS_IMETHODIMP
02347 nsFastLoadFileUpdater::GetOutputStream(nsIOutputStream** aResult)
02348 {
02349     *aResult = nsnull;
02350     return NS_OK;
02351 }
02352 
02353 PLDHashOperator PR_CALLBACK
02354 nsFastLoadFileUpdater::CopyReadDocumentMapEntryToUpdater(PLDHashTable *aTable,
02355                                                          PLDHashEntryHdr *aHdr,
02356                                                          PRUint32 aNumber,
02357                                                          void *aData)
02358 {
02359     nsDocumentMapReadEntry* readEntry =
02360         NS_STATIC_CAST(nsDocumentMapReadEntry*, aHdr);
02361     nsFastLoadFileUpdater* updater =
02362         NS_REINTERPRET_CAST(nsFastLoadFileUpdater*, aData);
02363 
02364     void* spec = nsMemory::Clone(readEntry->mString,
02365                                  strlen(readEntry->mString) + 1);
02366     if (!spec)
02367         return PL_DHASH_STOP;
02368 
02369     nsDocumentMapWriteEntry* writeEntry =
02370         NS_STATIC_CAST(nsDocumentMapWriteEntry*,
02371                        PL_DHashTableOperate(&updater->mDocumentMap, spec,
02372                                             PL_DHASH_ADD));
02373     if (!writeEntry) {
02374         nsMemory::Free(spec);
02375         return PL_DHASH_STOP;
02376     }
02377 
02378     writeEntry->mString = NS_REINTERPRET_CAST(const char*, spec);
02379     writeEntry->mURI = nsnull;
02380     writeEntry->mInitialSegmentOffset = readEntry->mInitialSegmentOffset;
02381     writeEntry->mCurrentSegmentOffset = 0;
02382     return PL_DHASH_NEXT;
02383 }
02384 
02385 nsresult
02386 nsFastLoadFileUpdater::Open(nsFastLoadFileReader* aReader)
02387 {
02388     nsresult rv;
02389     rv = nsFastLoadFileWriter::Init();
02390     if (NS_FAILED(rv))
02391         return rv;
02392 
02393     PRUint32 i, n;
02394 
02395     // Map from dense, zero-based, uint32 NSFastLoadID in reader to 16-byte
02396     // nsID in updater.
02397     nsID* readIDMap = aReader->mFooter.mIDMap;
02398     for (i = 0, n = aReader->mFooter.mNumIDs; i < n; i++) {
02399         NSFastLoadID fastID;
02400         rv = MapID(readIDMap[i], &fastID);
02401         NS_ASSERTION(fastID == i + 1, "huh?");
02402         if (NS_FAILED(rv))
02403             return rv;
02404     }
02405 
02406     // Map from reader dense, zero-based MFL_OID_TO_SHARP_INDEX(oid) to sharp
02407     // object offset and refcnt information in updater.
02408     nsFastLoadFileReader::nsObjectMapEntry* readObjectMap =
02409         aReader->mFooter.mObjectMap;
02410 
02411     // Prepare to save aReader state in case we need to seek back and read a
02412     // singleton object that might otherwise get written by this updater.
02413     nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
02414     nsISeekableStream* inputSeekable = nsnull;
02415     PRInt64 saveOffset = 0;
02416 
02417     for (i = 0, n = aReader->mFooter.mNumSharpObjects; i < n; i++) {
02418         nsFastLoadFileReader::nsObjectMapEntry* readEntry = &readObjectMap[i];
02419 
02420         NS_ASSERTION(readEntry->mCIDOffset != 0,
02421                      "fastload updater: mCIDOffset cannot be zero!");
02422 
02423         // If the reader didn't read this object but it's a singleton, we must
02424         // "deserialize" it now, to discover its one and only root nsISupports
02425         // address.  The object already exists in memory if it was created at
02426         // startup without resort to the FastLoad file.  The canonical example
02427         // is the system principal object held by all XUL JS scripts.
02428 
02429         nsISupports* obj = readEntry->mReadObject;
02430         if (!obj && MFL_GET_SINGLETON_FLAG(readEntry)) {
02431             if (!saveDocMapEntry) {
02432                 inputSeekable = aReader->mSeekableInput;
02433                 rv = inputSeekable->Tell(&saveOffset);
02434                 if (NS_FAILED(rv))
02435                     return rv;
02436 
02437                 saveDocMapEntry = aReader->mCurrentDocumentMapEntry;
02438                 aReader->mCurrentDocumentMapEntry = nsnull;
02439             }
02440 
02441             rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET,
02442                                      readEntry->mCIDOffset);
02443             if (NS_FAILED(rv))
02444                 return rv;
02445 
02446             rv = aReader
02447                  ->DeserializeObject(getter_AddRefs(readEntry->mReadObject));
02448             if (NS_FAILED(rv))
02449                 return rv;
02450             obj = readEntry->mReadObject;
02451 
02452             // Don't forget to set mSkipOffset in case someone calls the reader
02453             // to "deserialize" (yet again) the object we just read.
02454             //
02455             // Say the singleton is the system principal, and the FastLoad file
02456             // contains data for navigator.xul including scripts and functions.
02457             // If we update the FastLoad file to contain data for messenger.xul
02458             // in a separate session started via mozilla -mail, *and during the
02459             // same FastLoad episode in this session* race to open a navigator
02460             // window, we will attempt to read all objects serialized in the
02461             // navigator.xul portion of the FastLoad file.
02462             //
02463             // mSkipOffset must be set in such a case so the reader can skip
02464             // the system principal's serialized data, because the updater for
02465             // messenger.xul being opened here has already read it.
02466 
02467             rv = inputSeekable->Tell(&readEntry->mSkipOffset);
02468             if (NS_FAILED(rv))
02469                 return rv;
02470         }
02471 
02472         NSFastLoadOID oid = MFL_SHARP_INDEX_TO_OID(i);
02473         void* key = obj
02474                     ? NS_REINTERPRET_CAST(void*, obj)
02475                     : NS_REINTERPRET_CAST(void*, (oid | MFL_OBJECT_DEF_TAG));
02476 
02477         nsSharpObjectMapEntry* writeEntry =
02478             NS_STATIC_CAST(nsSharpObjectMapEntry*,
02479                            PL_DHashTableOperate(&mObjectMap, key,
02480                                                 PL_DHASH_ADD));
02481         if (!writeEntry)
02482             return NS_ERROR_OUT_OF_MEMORY;
02483 
02484         // Hold the object if there is one, so that objmap_ClearEntry can
02485         // release the reference.
02486         NS_IF_ADDREF(obj);
02487         writeEntry->mObject = NS_REINTERPRET_CAST(nsISupports*, key);
02488         writeEntry->mOID = oid;
02489         writeEntry->mInfo.mCIDOffset = readEntry->mCIDOffset;
02490         writeEntry->mInfo.mStrongRefCnt = readEntry->mSaveStrongRefCnt;
02491         writeEntry->mInfo.mWeakRefCnt = readEntry->mSaveWeakRefCnt;
02492     }
02493 
02494     // If we had to read any singletons, restore aReader's saved state.
02495     if (saveDocMapEntry) {
02496         rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
02497         if (NS_FAILED(rv))
02498             return rv;
02499 
02500         aReader->mCurrentDocumentMapEntry = saveDocMapEntry;
02501     }
02502 
02503     // Copy URI spec string and initial segment offset in FastLoad file from
02504     // nsDocumentMapReadEntry in reader to nsDocumentMapWriteEntry in updater.
02505     // If we didn't enumerate all entries, we ran out of memory.
02506     n = PL_DHashTableEnumerate(&aReader->mFooter.mDocumentMap,
02507                                CopyReadDocumentMapEntryToUpdater,
02508                                this);
02509     if (n != aReader->mFooter.mDocumentMap.entryCount)
02510         return NS_ERROR_OUT_OF_MEMORY;
02511 
02512     // Copy source filename dependencies from reader to updater.
02513     nsISupportsArray* readDeps = aReader->mFooter.mDependencies;
02514     rv = readDeps->Count(&n);
02515     if (NS_FAILED(rv))
02516         return rv;
02517 
02518     for (i = 0; i < n; i++) {
02519         nsCOMPtr<nsIFile> file;
02520         rv = readDeps->GetElementAt(i, getter_AddRefs(file));
02521         if (NS_FAILED(rv))
02522             return rv;
02523 
02524         rv = AddDependency(file);
02525         if (NS_FAILED(rv))
02526             return rv;
02527     }
02528 
02529     // Seek to the reader's footer offset so we overwrite the footer.  First,
02530     // update the header to have a zero mFooterOffset, which will invalidate
02531     // the FastLoad file on next startup read attempt, should we crash before
02532     // completing this update.
02533     rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
02534                                offsetof(nsFastLoadHeader, mFooterOffset));
02535     if (NS_FAILED(rv))
02536         return rv;
02537 
02538     rv = Write32(0);
02539     if (NS_FAILED(rv))
02540         return rv;
02541 
02542     rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
02543                                aReader->mHeader.mFooterOffset);
02544     if (NS_FAILED(rv))
02545         return rv;
02546 
02547     // Avoid creating yet another object by implementing nsIFastLoadFileIO on
02548     // this updater, and save aReader's input stream so it can be returned by
02549     // GetInputStream called from nsFastLoadFileWriter::Close.  This requires
02550     // that we override Close to break the resulting zero-length cycle.
02551     mFileIO = this;
02552     mInputStream = aReader->mInputStream;
02553     mSeekableInput = aReader->mSeekableInput;
02554     return NS_OK;
02555 }
02556 
02557 NS_IMETHODIMP
02558 nsFastLoadFileUpdater::Close()
02559 {
02560     // Call base-class Close implementation, which uses mFileIO.
02561     nsresult rv = nsFastLoadFileWriter::Close();
02562 
02563     // Break degenerate cycle from this->mFileIO to this.
02564     mFileIO = nsnull;
02565     return rv;
02566 }
02567 
02568 NS_COM nsresult
02569 NS_NewFastLoadFileUpdater(nsIObjectOutputStream* *aResult,
02570                           nsIOutputStream* aOutputStream,
02571                           nsIObjectInputStream* aReaderAsStream)
02572 {
02573     // Make sure that aReaderAsStream is an nsFastLoadFileReader.
02574     nsCOMPtr<nsIFastLoadFileReader> reader(do_QueryInterface(aReaderAsStream));
02575     if (!reader)
02576         return NS_ERROR_UNEXPECTED;
02577 
02578     nsFastLoadFileUpdater* updater = new nsFastLoadFileUpdater(aOutputStream);
02579     if (!updater)
02580         return NS_ERROR_OUT_OF_MEMORY;
02581 
02582     // Stabilize updater's refcnt.
02583     nsCOMPtr<nsIObjectOutputStream> stream(updater);
02584 
02585     nsresult rv = updater->Open(NS_STATIC_CAST(nsFastLoadFileReader*,
02586                                                aReaderAsStream));
02587     if (NS_FAILED(rv))
02588         return rv;
02589 
02590     *aResult = stream;
02591     NS_ADDREF(*aResult);
02592     return NS_OK;
02593 }