summaryrefslogtreecommitdiffstats
path: root/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp2581
1 files changed, 2581 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp b/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp
new file mode 100644
index 00000000..56b09d22
--- /dev/null
+++ b/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp
@@ -0,0 +1,2581 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla FastLoad code.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2001
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Brendan Eich <brendan@mozilla.org> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <string.h>
+#include "prtypes.h"
+#include "nscore.h"
+#include "nsDebug.h"
+#include "nsEnumeratorUtils.h"
+#include "nsMemory.h"
+#include "nsXPIDLString.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+
+#include "nsIComponentManager.h"
+#include "nsIFile.h"
+#include "nsILocalFile.h"
+#include "nsISeekableStream.h"
+#include "nsISerializable.h"
+#include "nsIStreamBufferAccess.h"
+
+#include "nsBinaryStream.h"
+#include "nsFastLoadFile.h"
+#include "nsInt64.h"
+
+#ifdef DEBUG_brendan
+# define METERING
+# define DEBUG_MUX
+#endif
+
+#ifdef METERING
+# define METER(x) x
+#else
+# define METER(x) /* nothing */
+#endif
+
+#ifdef DEBUG_MUX
+# include <stdio.h>
+# include <stdarg.h>
+
+static void trace_mux(char mode, const char *format, ...)
+{
+ va_list ap;
+ static FILE *tfp;
+ if (!tfp) {
+ char tfn[16];
+ sprintf(tfn, "/tmp/mux.%ctrace", mode);
+ tfp = fopen(tfn, "w");
+ if (!tfp)
+ return;
+ setvbuf(tfp, NULL, _IOLBF, 0);
+ }
+ va_start(ap, format);
+ vfprintf(tfp, format, ap);
+ va_end(ap);
+}
+
+# define TRACE_MUX(args) trace_mux args
+#else
+# define TRACE_MUX(args) /* nothing */
+#endif
+
+/*
+ * Fletcher's 16-bit checksum, using 32-bit two's-complement arithmetic.
+ */
+#define FOLD_ONES_COMPLEMENT_CARRY(X) ((X) = ((X) & 0xffff) + ((X) >> 16))
+#define ONES_COMPLEMENT_ACCUMULATE(X,Y) (X) += (Y); if ((X) & 0x80000000) \
+ FOLD_ONES_COMPLEMENT_CARRY(X)
+#define FLETCHER_ACCUMULATE(A,B,U) ONES_COMPLEMENT_ACCUMULATE(A, U); \
+ ONES_COMPLEMENT_ACCUMULATE(B, A)
+
+PR_IMPLEMENT(PRUint32)
+NS_AccumulateFastLoadChecksum(PRUint32 *aChecksum,
+ const PRUint8* aBuffer,
+ PRUint32 aLength,
+ PRBool aLastBuffer)
+{
+ PRUint32 C = *aChecksum;
+ PRUint32 A = C & 0xffff;
+ PRUint32 B = C >> 16;
+
+ PRUint16 U = 0;
+ if (aLength >= 4) {
+ PRBool odd = PRWord(aBuffer) & 1;
+ switch (PRWord(aBuffer) & 3) {
+ case 3:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = aBuffer[2];
+ aBuffer += 3;
+ aLength -= 3;
+ break;
+
+ case 2:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = 0;
+ aBuffer += 2;
+ aLength -= 2;
+ break;
+
+ case 1:
+ U = *aBuffer++;
+ aLength--;
+ break;
+ }
+
+ PRUint32 W;
+ if (odd) {
+ while (aLength > 3) {
+ W = *NS_REINTERPRET_CAST(const PRUint32*, aBuffer);
+ U <<= 8;
+#ifdef IS_BIG_ENDIAN
+ U |= W >> 24;
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = PRUint16(W >> 8);
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = W & 0xff;
+#else
+ U |= W & 0xff;
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = PRUint16(W >> 8);
+ U = NS_SWAP16(U);
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = W >> 24;
+#endif
+ aBuffer += 4;
+ aLength -= 4;
+ }
+ aBuffer--; // we're odd, we didn't checksum the last byte
+ aLength++;
+ } else {
+ while (aLength > 3) {
+ W = *NS_REINTERPRET_CAST(const PRUint32*, aBuffer);
+#ifdef IS_BIG_ENDIAN
+ U = W >> 16;
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = PRUint16(W);
+ FLETCHER_ACCUMULATE(A, B, U);
+#else
+ U = NS_SWAP16(W);
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = W >> 16;
+ U = NS_SWAP16(W);
+ FLETCHER_ACCUMULATE(A, B, U);
+#endif
+ aBuffer += 4;
+ aLength -= 4;
+ }
+ }
+ }
+
+ if (aLastBuffer) {
+ NS_ASSERTION(aLength <= 4, "aLength botch");
+ switch (aLength) {
+ case 4:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = (aBuffer[2] << 8) | aBuffer[3];
+ FLETCHER_ACCUMULATE(A, B, U);
+ break;
+
+ case 3:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ U = aBuffer[2];
+ FLETCHER_ACCUMULATE(A, B, U);
+ break;
+
+ case 2:
+ U = (aBuffer[0] << 8) | aBuffer[1];
+ FLETCHER_ACCUMULATE(A, B, U);
+ break;
+
+ case 1:
+ U = aBuffer[0];
+ FLETCHER_ACCUMULATE(A, B, U);
+ break;
+ }
+
+ aLength = 0;
+ }
+
+ while (A >> 16)
+ FOLD_ONES_COMPLEMENT_CARRY(A);
+ while (B >> 16)
+ FOLD_ONES_COMPLEMENT_CARRY(B);
+
+ *aChecksum = (B << 16) | A;
+ return aLength;
+}
+
+PR_IMPLEMENT(PRUint32)
+NS_AddFastLoadChecksums(PRUint32 sum1, PRUint32 sum2, PRUint32 sum2ByteCount)
+{
+ PRUint32 A1 = sum1 & 0xffff;
+ PRUint32 B1 = sum1 >> 16;
+
+ PRUint32 A2 = sum2 & 0xffff;
+ PRUint32 B2 = sum2 >> 16;
+
+ PRUint32 A = A1 + A2;
+ while (A >> 16)
+ FOLD_ONES_COMPLEMENT_CARRY(A);
+
+ PRUint32 B = B2;
+ for (PRUint32 n = (sum2ByteCount + 1) / 2; n != 0; n--)
+ ONES_COMPLEMENT_ACCUMULATE(B, B1);
+ while (B >> 16)
+ FOLD_ONES_COMPLEMENT_CARRY(B);
+
+ return (B << 16) | A;
+}
+
+#undef FOLD_ONES_COMPLEMENT_CARRY
+#undef ONES_COMPLEMENT_ACCUMULATE
+#undef FLETCHER_ACCUMULATE
+
+static const char magic[] = MFL_FILE_MAGIC;
+
+// -------------------------- nsFastLoadFileReader --------------------------
+
+nsID nsFastLoadFileReader::nsFastLoadFooter::gDummyID;
+nsFastLoadFileReader::nsObjectMapEntry
+ nsFastLoadFileReader::nsFastLoadFooter::gDummySharpObjectEntry;
+
+NS_IMPL_ISUPPORTS_INHERITED5(nsFastLoadFileReader,
+ nsBinaryInputStream,
+ nsIObjectInputStream,
+ nsIFastLoadFileControl,
+ nsIFastLoadReadControl,
+ nsISeekableStream,
+ nsIFastLoadFileReader)
+
+MOZ_DECL_CTOR_COUNTER(nsFastLoadFileReader)
+
+nsresult
+nsFastLoadFileReader::ReadHeader(nsFastLoadHeader *aHeader)
+{
+ nsresult rv;
+ PRUint32 bytesRead;
+
+ rv = Read(NS_REINTERPRET_CAST(char*, aHeader), sizeof *aHeader, &bytesRead);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (bytesRead != sizeof *aHeader ||
+ memcmp(aHeader->mMagic, magic, MFL_FILE_MAGIC_SIZE)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aHeader->mChecksum = NS_SWAP32(aHeader->mChecksum);
+ aHeader->mVersion = NS_SWAP32(aHeader->mVersion);
+ aHeader->mFooterOffset = NS_SWAP32(aHeader->mFooterOffset);
+ aHeader->mFileSize = NS_SWAP32(aHeader->mFileSize);
+
+ return NS_OK;
+}
+
+// nsIFastLoadFileControl methods:
+
+NS_IMETHODIMP
+nsFastLoadFileReader::GetChecksum(PRUint32 *aChecksum)
+{
+ *aChecksum = mHeader.mChecksum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::SetChecksum(PRUint32 aChecksum)
+{
+ mHeader.mChecksum = aChecksum;
+ return NS_OK;
+}
+
+struct nsStringMapEntry : public PLDHashEntryHdr {
+ const char* mString; // key, must come first
+ nsISupports* mURI; // for SelectMuxedDocument return value
+};
+
+struct nsDocumentMapEntry : public nsStringMapEntry {
+ PRUint32 mInitialSegmentOffset; // offset of URI's first segment in file
+};
+
+struct nsDocumentMapReadEntry : public nsDocumentMapEntry {
+ PRUint32 mNextSegmentOffset; // offset of URI's next segment to read
+ PRUint32 mBytesLeft : 31, // bytes remaining in current segment
+ mNeedToSeek : 1; // flag to defer Seek from Select to
+ // Read, in case there is no Read before
+ // another entry is Selected (to improve
+ // input stream buffer utilization)
+ PRInt64 mSaveOffset; // in case demux schedule differs from
+ // mux schedule
+};
+
+PR_STATIC_CALLBACK(void)
+strmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
+{
+ nsStringMapEntry* entry = NS_STATIC_CAST(nsStringMapEntry*, aHdr);
+
+ if (entry->mString)
+ nsMemory::Free((void*) entry->mString);
+ NS_IF_RELEASE(entry->mURI);
+ PL_DHashClearEntryStub(aTable, aHdr);
+}
+
+static const PLDHashTableOps strmap_DHashTableOps = {
+ PL_DHashAllocTable,
+ PL_DHashFreeTable,
+ PL_DHashGetKeyStub,
+ PL_DHashStringKey,
+ PL_DHashMatchStringKey,
+ PL_DHashMoveEntryStub,
+ strmap_ClearEntry,
+ PL_DHashFinalizeStub,
+ NULL
+};
+
+// An nsObjectMapEntry holds a strong reference to an XPCOM object, unless the
+// mObject member, when cast to NSFastLoadOID, has its MFL_OBJECT_DEF_TAG bit
+// set. NB: we rely on the fact that an nsISupports* is never an odd pointer.
+struct nsObjectMapEntry : public PLDHashEntryHdr {
+ nsISupports* mObject; // key, must come first
+};
+
+// Fast mapping from URI object pointer back to spec-indexed document info.
+struct nsURIMapReadEntry : public nsObjectMapEntry {
+ nsDocumentMapReadEntry* mDocMapEntry;
+};
+
+PR_STATIC_CALLBACK(void)
+objmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
+{
+ nsObjectMapEntry* entry = NS_STATIC_CAST(nsObjectMapEntry*, aHdr);
+
+ // Ignore tagged object ids stored as object pointer keys (the updater
+ // code does this).
+ if ((NS_PTR_TO_INT32(entry->mObject) & MFL_OBJECT_DEF_TAG) == 0)
+ NS_IF_RELEASE(entry->mObject);
+ PL_DHashClearEntryStub(aTable, aHdr);
+}
+
+static const PLDHashTableOps objmap_DHashTableOps = {
+ PL_DHashAllocTable,
+ PL_DHashFreeTable,
+ PL_DHashGetKeyStub,
+ PL_DHashVoidPtrKeyStub,
+ PL_DHashMatchEntryStub,
+ PL_DHashMoveEntryStub,
+ objmap_ClearEntry,
+ PL_DHashFinalizeStub,
+ NULL
+};
+
+NS_IMETHODIMP
+nsFastLoadFileReader::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
+{
+ nsDocumentMapReadEntry* docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
+ PL_DHASH_LOOKUP));
+
+ *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::StartMuxedDocument(nsISupports* aURI, const char* aURISpec)
+{
+ nsDocumentMapReadEntry* docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
+ PL_DHASH_LOOKUP));
+
+ // If the spec isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
+ // FastLoad service can try for a file update.
+ if (PL_DHASH_ENTRY_IS_FREE(docMapEntry))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapReadEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mURIMap, key,
+ PL_DHASH_ADD));
+ if (!uriMapEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ASSERTION(uriMapEntry->mDocMapEntry == nsnull,
+ "URI mapped to two different specs?");
+ if (uriMapEntry->mDocMapEntry)
+ return NS_ERROR_UNEXPECTED;
+
+ docMapEntry->mURI = aURI;
+ NS_ADDREF(docMapEntry->mURI);
+ uriMapEntry->mObject = key;
+ NS_ADDREF(uriMapEntry->mObject);
+ uriMapEntry->mDocMapEntry = docMapEntry;
+ TRACE_MUX(('r', "start %p (%p) %s\n", aURI, key.get(), aURISpec));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::SelectMuxedDocument(nsISupports* aURI,
+ nsISupports** aResult)
+{
+ nsresult rv;
+
+ // Find the given URI's entry and select it for more reading.
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapReadEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mURIMap, key,
+ PL_DHASH_LOOKUP));
+
+ // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
+ // FastLoad service can try selecting the file updater.
+ if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ // If we're interrupting another document's segment, save its offset so
+ // we can seek back when it's reselected. If prevDocMapEntry->mNeedToSeek
+ // is set, that means the stream is not positioned for prevDocMapEntry, to
+ // avoid buffer thrashing. See below in this function for more.
+ nsDocumentMapReadEntry* prevDocMapEntry = mCurrentDocumentMapEntry;
+ if (prevDocMapEntry &&
+ prevDocMapEntry->mBytesLeft &&
+ !prevDocMapEntry->mNeedToSeek) {
+ rv = Tell(&prevDocMapEntry->mSaveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // It turns out we get a fair amount of redundant select calls, thanks to
+ // non-blocking hunks of data from the parser that are devoid of scripts.
+ // As more data gets FastLoaded, the number of these useless selects will
+ // decline.
+ nsDocumentMapReadEntry* docMapEntry = uriMapEntry->mDocMapEntry;
+ if (docMapEntry == prevDocMapEntry) {
+ TRACE_MUX(('r', "select prev %s same as current!\n",
+ docMapEntry->mString));
+ }
+
+ // Invariant: docMapEntry->mBytesLeft implies docMapEntry->mSaveOffset has
+ // been set non-zero by the Tell call above.
+ else if (docMapEntry->mBytesLeft) {
+ NS_ASSERTION(docMapEntry->mSaveOffset != 0,
+ "reselecting from multiplex at unsaved offset?");
+
+ // Defer Seek till Read, in case of "ping-pong" Selects without any
+ // intervening Reads, to avoid dumping the underlying mInputStream's
+ // input buffer for cases where alternate "pongs" fall in the same
+ // buffer.
+ docMapEntry->mNeedToSeek = PR_TRUE;
+ }
+
+ *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
+ NS_IF_ADDREF(*aResult);
+
+ mCurrentDocumentMapEntry = docMapEntry;
+#ifdef DEBUG_MUX
+ PRInt64 currentSegmentOffset;
+ Tell(&currentSegmentOffset);
+ trace_mux('r', "select %p (%p) offset %ld\n",
+ aURI, key.get(), (long) currentSegmentOffset);
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::EndMuxedDocument(nsISupports* aURI)
+{
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapReadEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapReadEntry*,
+ PL_DHashTableOperate(&mFooter.mURIMap, key,
+ PL_DHASH_LOOKUP));
+
+ // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
+ // FastLoad service can try to end a select on its file updater.
+ if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ // Drop our ref to the URI object that was passed to StartMuxedDocument,
+ // we no longer need it, and we do not want to extend its lifetime.
+ if (uriMapEntry->mDocMapEntry)
+ NS_RELEASE(uriMapEntry->mDocMapEntry->mURI);
+
+ // Shrink the table if half the entries are removed sentinels.
+ PRUint32 size = PL_DHASH_TABLE_SIZE(&mFooter.mURIMap);
+ if (mFooter.mURIMap.removedCount >= (size >> 2))
+ PL_DHashTableOperate(&mFooter.mURIMap, key, PL_DHASH_REMOVE);
+ else
+ PL_DHashTableRawRemove(&mFooter.mURIMap, uriMapEntry);
+
+ TRACE_MUX(('r', "end %p (%p)\n", aURI, key.get()));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
+{
+ nsresult rv;
+
+ nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
+ if (entry) {
+ // Don't call our Seek wrapper, as it clears mCurrentDocumentMapEntry.
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ if (entry->mNeedToSeek) {
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ entry->mSaveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ entry->mNeedToSeek = PR_FALSE;
+ }
+
+ // Loop to handle empty segments, which may be generated by the
+ // writer, given Start A; Start B; Select A; Select B; write B data;
+ // multiplexing schedules, which do tend to occur given non-blocking
+ // i/o with LIFO scheduling. XXXbe investigate LIFO issues
+ while (entry->mBytesLeft == 0) {
+ // Check for unexpected end of multiplexed stream.
+ NS_ASSERTION(entry->mNextSegmentOffset != 0,
+ "document demuxed from FastLoad file more than once?");
+ if (entry->mNextSegmentOffset == 0)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ entry->mNextSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Clear mCurrentDocumentMapEntry temporarily to avoid recursion.
+ mCurrentDocumentMapEntry = nsnull;
+
+ rv = Read32(&entry->mNextSegmentOffset);
+ if (NS_SUCCEEDED(rv)) {
+ PRUint32 bytesLeft = 0;
+ rv = Read32(&bytesLeft);
+ entry->mBytesLeft = bytesLeft;
+ }
+
+ mCurrentDocumentMapEntry = entry;
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_ASSERTION(entry->mBytesLeft >= 8, "demux segment length botch!");
+ entry->mBytesLeft -= 8;
+ }
+ }
+
+ rv = mInputStream->Read(aBuffer, aCount, aBytesRead);
+
+ if (NS_SUCCEEDED(rv) && entry) {
+ NS_ASSERTION(entry->mBytesLeft >= *aBytesRead, "demux Read underflow!");
+ entry->mBytesLeft -= *aBytesRead;
+
+#ifdef NS_DEBUG
+ // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
+ if (entry->mBytesLeft == 0)
+ entry->mSaveOffset = 0;
+#endif
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+ PRUint32 aCount, PRUint32 *aResult)
+{
+ nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
+
+ NS_ASSERTION(!entry || (!entry->mNeedToSeek && entry->mBytesLeft != 0),
+ "ReadSegments called from above nsFastLoadFileReader layer?!");
+
+ nsresult rv = nsBinaryInputStream::ReadSegments(aWriter, aClosure, aCount,
+ aResult);
+ if (NS_SUCCEEDED(rv) && entry) {
+ NS_ASSERTION(entry->mBytesLeft >= *aResult,
+ "demux ReadSegments underflow!");
+ entry->mBytesLeft -= *aResult;
+
+#ifdef NS_DEBUG
+ // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
+ if (entry->mBytesLeft == 0)
+ entry->mSaveOffset = 0;
+#endif
+ }
+ return rv;
+}
+
+/**
+ * XXX tuneme
+ */
+#define MFL_CHECKSUM_BUFSIZE 8192
+
+NS_IMETHODIMP
+nsFastLoadFileReader::ComputeChecksum(PRUint32 *aResult)
+{
+ nsCOMPtr<nsIInputStream> stream = mInputStream;
+
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(stream));
+ PRInt64 saveOffset;
+ nsresult rv = seekable->Tell(&saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIStreamBufferAccess> bufferAccess(do_QueryInterface(stream));
+ if (bufferAccess) {
+ rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(stream));
+ if (NS_FAILED(rv))
+ return rv;
+
+ seekable = do_QueryInterface(stream);
+ if (!seekable)
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ char buf[MFL_CHECKSUM_BUFSIZE];
+ PRUint32 len, rem;
+
+ rem = offsetof(nsFastLoadHeader, mChecksum);
+ rv = stream->Read(buf, rem, &len);
+ if (NS_FAILED(rv))
+ return rv;
+ if (len != rem)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, 4);
+ if (NS_FAILED(rv))
+ return rv;
+ memset(buf + rem, 0, 4);
+ rem += 4;
+
+ PRUint32 checksum = 0;
+ while (NS_SUCCEEDED(rv = stream->Read(buf + rem, sizeof buf - rem, &len)) &&
+ len) {
+ len += rem;
+ rem = NS_AccumulateFastLoadChecksum(&checksum,
+ NS_REINTERPRET_CAST(PRUint8*, buf),
+ len,
+ PR_FALSE);
+ if (rem)
+ memcpy(buf, buf + len - rem, rem);
+ }
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (rem) {
+ NS_AccumulateFastLoadChecksum(&checksum,
+ NS_REINTERPRET_CAST(PRUint8*, buf),
+ rem,
+ PR_TRUE);
+ }
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = checksum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::GetDependencies(nsISimpleEnumerator* *aDependencies)
+{
+ return NS_NewArrayEnumerator(aDependencies, mFooter.mDependencies);
+}
+
+nsresult
+nsFastLoadFileReader::ReadFooter(nsFastLoadFooter *aFooter)
+{
+ nsresult rv;
+
+ rv = ReadFooterPrefix(aFooter);
+ if (NS_FAILED(rv))
+ return rv;
+
+ aFooter->mIDMap = new nsID[aFooter->mNumIDs];
+ if (!aFooter->mIDMap)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ PRUint32 i, n;
+ for (i = 0, n = aFooter->mNumIDs; i < n; i++) {
+ rv = ReadSlowID(&aFooter->mIDMap[i]);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ aFooter->mObjectMap = new nsObjectMapEntry[aFooter->mNumSharpObjects];
+ if (!aFooter->mObjectMap)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ for (i = 0, n = aFooter->mNumSharpObjects; i < n; i++) {
+ nsObjectMapEntry* entry = &aFooter->mObjectMap[i];
+
+ rv = ReadSharpObjectInfo(entry);
+ if (NS_FAILED(rv))
+ return rv;
+
+ entry->mReadObject = nsnull;
+ entry->mSkipOffset = 0;
+ entry->mSaveStrongRefCnt = entry->mStrongRefCnt;
+ entry->mSaveWeakRefCnt = entry->mWeakRefCnt;
+ }
+
+ if (!PL_DHashTableInit(&aFooter->mDocumentMap, &strmap_DHashTableOps,
+ (void *)this, sizeof(nsDocumentMapReadEntry),
+ aFooter->mNumMuxedDocuments)) {
+ aFooter->mDocumentMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&aFooter->mURIMap, &objmap_DHashTableOps,
+ (void *)this, sizeof(nsURIMapReadEntry),
+ aFooter->mNumMuxedDocuments)) {
+ aFooter->mURIMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ for (i = 0, n = aFooter->mNumMuxedDocuments; i < n; i++) {
+ nsFastLoadMuxedDocumentInfo info;
+
+ rv = ReadMuxedDocumentInfo(&info);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsDocumentMapReadEntry* entry =
+ NS_STATIC_CAST(nsDocumentMapReadEntry*,
+ PL_DHashTableOperate(&aFooter->mDocumentMap,
+ info.mURISpec,
+ PL_DHASH_ADD));
+ if (!entry) {
+ nsMemory::Free((void*) info.mURISpec);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ASSERTION(!entry->mString, "duplicate URISpec in MuxedDocumentMap");
+ entry->mString = info.mURISpec;
+ entry->mURI = nsnull;
+ entry->mInitialSegmentOffset = info.mInitialSegmentOffset;
+ entry->mNextSegmentOffset = info.mInitialSegmentOffset;
+ entry->mBytesLeft = 0;
+ entry->mNeedToSeek = PR_FALSE;
+ entry->mSaveOffset = 0;
+ }
+
+ nsCOMPtr<nsISupportsArray> readDeps;
+ rv = NS_NewISupportsArray(getter_AddRefs(readDeps));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCAutoString filename;
+ for (i = 0, n = aFooter->mNumDependencies; i < n; i++) {
+ rv = ReadCString(filename);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRInt64 fastLoadMtime;
+ rv = Read64(NS_REINTERPRET_CAST(PRUint64*, &fastLoadMtime));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsILocalFile> file;
+ rv = NS_NewNativeLocalFile(filename, PR_TRUE, getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRInt64 currentMtime;
+ rv = file->GetLastModifiedTime(&currentMtime);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (LL_NE(fastLoadMtime, currentMtime)) {
+#ifdef DEBUG
+ nsCAutoString path;
+ file->GetNativePath(path);
+ printf("%s mtime changed, invalidating FastLoad file\n",
+ path.get());
+#endif
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = readDeps->AppendElement(file);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ aFooter->mDependencies = readDeps;
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadFooterPrefix(nsFastLoadFooterPrefix *aFooterPrefix)
+{
+ nsresult rv;
+
+ rv = Read32(&aFooterPrefix->mNumIDs);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read32(&aFooterPrefix->mNumSharpObjects);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read32(&aFooterPrefix->mNumMuxedDocuments);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read32(&aFooterPrefix->mNumDependencies);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadSlowID(nsID *aID)
+{
+ nsresult rv;
+
+ rv = Read32(&aID->m0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read16(&aID->m1);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read16(&aID->m2);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 bytesRead;
+ rv = Read(NS_REINTERPRET_CAST(char*, aID->m3), sizeof aID->m3, &bytesRead);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (bytesRead != sizeof aID->m3)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadFastID(NSFastLoadID *aID)
+{
+ nsresult rv = Read32(aID);
+ if (NS_SUCCEEDED(rv))
+ *aID ^= MFL_ID_XOR_KEY;
+ return rv;
+}
+
+nsresult
+nsFastLoadFileReader::ReadSharpObjectInfo(nsFastLoadSharpObjectInfo *aInfo)
+{
+ nsresult rv;
+
+ rv = Read32(&aInfo->mCIDOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_ASSERTION(aInfo->mCIDOffset != 0,
+ "fastload reader: mCIDOffset cannot be zero!");
+
+ rv = Read16(&aInfo->mStrongRefCnt);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read16(&aInfo->mWeakRefCnt);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadMuxedDocumentInfo(nsFastLoadMuxedDocumentInfo *aInfo)
+{
+ nsresult rv;
+
+ nsCAutoString spec;
+ rv = ReadCString(spec);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Read32(&aInfo->mInitialSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ aInfo->mURISpec = ToNewCString(spec);
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::Open()
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ if (!seekable)
+ return NS_ERROR_UNEXPECTED;
+
+ nsresult rv;
+
+ // Don't bother buffering the header, as we immediately seek to EOF.
+ nsCOMPtr<nsIStreamBufferAccess>
+ bufferAccess(do_QueryInterface(mInputStream));
+ if (bufferAccess)
+ bufferAccess->DisableBuffering();
+
+ rv = ReadHeader(&mHeader);
+
+ if (bufferAccess)
+ bufferAccess->EnableBuffering();
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (mHeader.mVersion != MFL_FILE_VERSION)
+ return NS_ERROR_UNEXPECTED;
+ if (mHeader.mFooterOffset == 0)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRInt64 fileSize;
+ rv = seekable->Tell(&fileSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsInt64 fileSize64 = fileSize;
+ const nsInt64 maxUint32 = PR_UINT32_MAX;
+ NS_ASSERTION(fileSize64 <= maxUint32, "fileSize must fit in 32 bits");
+ if ((PRUint32) fileSize64 != mHeader.mFileSize)
+ return NS_ERROR_UNEXPECTED;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ PRInt32(mHeader.mFooterOffset));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = ReadFooter(&mFooter);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ sizeof(nsFastLoadHeader));
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::Close()
+{
+ // Give up our strong "keepalive" references, in case not all objects that
+ // were deserialized were fully re-connected.
+ //
+ // This happens for sure when an nsFastLoadFileUpdater is created and wraps
+ // an nsFastLoadFileReader whose data was already deserialized by an earlier
+ // FastLoad episode. The reader is useful in the second such episode during
+ // a session not so much for reading objects as for its footer information,
+ // which primes the updater's tables so that after the update completes, the
+ // FastLoad file has a superset footer.
+
+ for (PRUint32 i = 0, n = mFooter.mNumSharpObjects; i < n; i++) {
+ nsObjectMapEntry* entry = &mFooter.mObjectMap[i];
+ entry->mReadObject = nsnull;
+ }
+
+ return mInputStream->Close();
+}
+
+nsresult
+nsFastLoadFileReader::DeserializeObject(nsISupports* *aObject)
+{
+ nsresult rv;
+ NSFastLoadID fastCID;
+
+ rv = ReadFastID(&fastCID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ const nsID& slowCID = mFooter.GetID(fastCID);
+ nsCOMPtr<nsISupports> object(do_CreateInstance(slowCID, &rv));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsISerializable> serializable(do_QueryInterface(object));
+ if (!serializable)
+ return NS_ERROR_FAILURE;
+
+ rv = serializable->Read(this);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aObject = object;
+ NS_ADDREF(*aObject);
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileReader::ReadObject(PRBool aIsStrongRef, nsISupports* *aObject)
+{
+ nsresult rv;
+ NSFastLoadOID oid;
+
+ rv = Read32(&oid);
+ if (NS_FAILED(rv))
+ return rv;
+ oid ^= MFL_OID_XOR_KEY;
+
+ nsCOMPtr<nsISupports> object;
+
+ if (oid == MFL_DULL_OBJECT_OID) {
+ // A very dull object, defined at point of single (strong) reference.
+ NS_ASSERTION(aIsStrongRef, "dull object read via weak ref!");
+
+ rv = DeserializeObject(getter_AddRefs(object));
+ if (NS_FAILED(rv))
+ return rv;
+ } else {
+ NS_ASSERTION((oid & MFL_WEAK_REF_TAG) ==
+ (aIsStrongRef ? 0 : MFL_WEAK_REF_TAG),
+ "strong vs. weak ref deserialization mismatch!");
+
+ nsObjectMapEntry* entry = &mFooter.GetSharpObjectEntry(oid);
+
+ // Check whether we've already deserialized the object for this OID.
+ object = entry->mReadObject;
+ if (!object) {
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ PRInt64 saveOffset;
+ nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
+
+ rv = seekable->Tell(&saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 saveOffset32 = saveOffset;
+ if (entry->mCIDOffset != saveOffset32) {
+ // We skipped deserialization of this object from its position
+ // earlier in the input stream, presumably due to the reference
+ // there being an nsFastLoadPtr, or (more likely) because the
+ // object was muxed in another document, and deserialization
+ // order does not match serialization order. So we must seek
+ // back and read it now.
+ NS_ASSERTION(entry->mCIDOffset < saveOffset32,
+ "out of order object?!");
+
+ // Ape our Seek wrapper by clearing mCurrentDocumentMapEntry.
+ // This allows for a skipped object to be referenced from two
+ // or more multiplexed documents in the FastLoad file.
+ saveDocMapEntry = mCurrentDocumentMapEntry;
+ mCurrentDocumentMapEntry = nsnull;
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ entry->mCIDOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ rv = DeserializeObject(getter_AddRefs(object));
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (entry->mCIDOffset != saveOffset32) {
+ // Save the "skip offset" in case we need to skip this object
+ // definition when reading forward, later on.
+ rv = seekable->Tell(&entry->mSkipOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Restore stream offset and mCurrentDocumentMapEntry in case
+ // we're still reading forward through a part of the multiplex
+ // to get object definitions eagerly.
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ mCurrentDocumentMapEntry = saveDocMapEntry;
+ }
+
+ // Save object until all refs have been deserialized.
+ entry->mReadObject = object;
+ } else {
+ // What if we are at a definition that's already been read? This
+ // case arises when a sharp object's def is serialized before its
+ // refs, while a non-defining ref is deserialized before the def.
+ // We must skip over the object definition.
+ if (oid & MFL_OBJECT_DEF_TAG) {
+ NS_ASSERTION(entry->mSkipOffset != 0, "impossible! see above");
+ nsCOMPtr<nsISeekableStream>
+ seekable(do_QueryInterface(mInputStream));
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ entry->mSkipOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ if (aIsStrongRef) {
+ NS_ASSERTION(entry->mStrongRefCnt != 0,
+ "mStrongRefCnt underflow!");
+ --entry->mStrongRefCnt;
+ } else {
+ NS_ASSERTION(MFL_GET_WEAK_REFCNT(entry) != 0,
+ "mWeakRefCnt underflow!");
+ MFL_DROP_WEAK_REFCNT(entry);
+ }
+
+ if (entry->mStrongRefCnt == 0 && MFL_GET_WEAK_REFCNT(entry) == 0)
+ entry->mReadObject = nsnull;
+ }
+
+ if (oid & MFL_QUERY_INTERFACE_TAG) {
+ NSFastLoadID iid;
+ rv = ReadFastID(&iid);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = object->QueryInterface(mFooter.GetID(iid),
+ NS_REINTERPRET_CAST(void**, aObject));
+ if (NS_FAILED(rv))
+ return rv;
+ } else {
+ *aObject = object;
+ NS_ADDREF(*aObject);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::ReadID(nsID *aResult)
+{
+ nsresult rv;
+ NSFastLoadID fastID;
+
+ rv = ReadFastID(&fastID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = mFooter.GetID(fastID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+ mCurrentDocumentMapEntry = nsnull;
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ return seekable->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::Tell(PRInt64 *aResult)
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ return seekable->Tell(aResult);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileReader::SetEOF()
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
+ return seekable->SetEOF();
+}
+
+NS_COM nsresult
+NS_NewFastLoadFileReader(nsIObjectInputStream* *aResult,
+ nsIInputStream* aSrcStream)
+{
+ nsFastLoadFileReader* reader = new nsFastLoadFileReader(aSrcStream);
+ if (!reader)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Stabilize reader's refcnt.
+ nsCOMPtr<nsIObjectInputStream> stream(reader);
+
+ nsresult rv = reader->Open();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = stream;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+// -------------------------- nsFastLoadFileWriter --------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED4(nsFastLoadFileWriter,
+ nsBinaryOutputStream,
+ nsIObjectOutputStream,
+ nsIFastLoadFileControl,
+ nsIFastLoadWriteControl,
+ nsISeekableStream)
+
+MOZ_DECL_CTOR_COUNTER(nsFastLoadFileWriter)
+
+struct nsIDMapEntry : public PLDHashEntryHdr {
+ NSFastLoadID mFastID; // 1 + nsFastLoadFooter::mIDMap index
+ nsID mSlowID; // key, used by PLDHashTableOps below
+};
+
+PR_STATIC_CALLBACK(const void *)
+idmap_GetKey(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
+{
+ nsIDMapEntry* entry = NS_STATIC_CAST(nsIDMapEntry*, aHdr);
+
+ return &entry->mSlowID;
+}
+
+PR_STATIC_CALLBACK(PLDHashNumber)
+idmap_HashKey(PLDHashTable *aTable, const void *aKey)
+{
+ const nsID *idp = NS_REINTERPRET_CAST(const nsID*, aKey);
+
+ return idp->m0;
+}
+
+PR_STATIC_CALLBACK(PRBool)
+idmap_MatchEntry(PLDHashTable *aTable,
+ const PLDHashEntryHdr *aHdr,
+ const void *aKey)
+{
+ const nsIDMapEntry* entry = NS_STATIC_CAST(const nsIDMapEntry*, aHdr);
+ const nsID *idp = NS_REINTERPRET_CAST(const nsID*, aKey);
+
+ return memcmp(&entry->mSlowID, idp, sizeof(nsID)) == 0;
+}
+
+static const PLDHashTableOps idmap_DHashTableOps = {
+ PL_DHashAllocTable,
+ PL_DHashFreeTable,
+ idmap_GetKey,
+ idmap_HashKey,
+ idmap_MatchEntry,
+ PL_DHashMoveEntryStub,
+ PL_DHashClearEntryStub,
+ PL_DHashFinalizeStub,
+ NULL
+};
+
+nsresult
+nsFastLoadFileWriter::MapID(const nsID& aSlowID, NSFastLoadID *aResult)
+{
+ nsIDMapEntry* entry =
+ NS_STATIC_CAST(nsIDMapEntry*,
+ PL_DHashTableOperate(&mIDMap, &aSlowID, PL_DHASH_ADD));
+ if (!entry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (entry->mFastID == 0) {
+ entry->mFastID = mIDMap.entryCount;
+ entry->mSlowID = aSlowID;
+ }
+
+ *aResult = entry->mFastID;
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteHeader(nsFastLoadHeader *aHeader)
+{
+ nsresult rv;
+ PRUint32 bytesWritten;
+
+ rv = Write(aHeader->mMagic, MFL_FILE_MAGIC_SIZE, &bytesWritten);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (bytesWritten != MFL_FILE_MAGIC_SIZE)
+ return NS_ERROR_FAILURE;
+
+ rv = Write32(aHeader->mChecksum);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aHeader->mVersion);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aHeader->mFooterOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aHeader->mFileSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+// nsIFastLoadFileControl methods:
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::GetChecksum(PRUint32 *aChecksum)
+{
+ if (mHeader.mChecksum == 0)
+ return NS_ERROR_NOT_AVAILABLE;
+ *aChecksum = mHeader.mChecksum;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::SetChecksum(PRUint32 aChecksum)
+{
+ mHeader.mChecksum = aChecksum;
+ return NS_OK;
+}
+
+struct nsDocumentMapWriteEntry : public nsDocumentMapEntry {
+ PRUint32 mCurrentSegmentOffset; // last written segment's offset
+};
+
+// Fast mapping from URI object pointer back to spec-indexed document info.
+// We also may need the slow mapping from mURISpec to nsDocumentMapWriteEntry,
+// because the writer's mDocumentMap double hash table may grow "behind the
+// back of" each mURIMap entry's mDocMapEntry member.
+struct nsURIMapWriteEntry : public nsObjectMapEntry {
+ nsDocumentMapWriteEntry* mDocMapEntry;
+ PRUint32 mGeneration;
+ const char* mURISpec;
+};
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
+{
+ nsDocumentMapWriteEntry* docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&mDocumentMap, aURISpec,
+ PL_DHASH_LOOKUP));
+
+ *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::StartMuxedDocument(nsISupports* aURI,
+ const char* aURISpec)
+{
+ // Save mDocumentMap table generation and mCurrentDocumentMapEntry key in
+ // case the hash table grows during the PL_DHASH_ADD operation.
+ PRUint32 saveGeneration = mDocumentMap.generation;
+ const char* saveURISpec = mCurrentDocumentMapEntry
+ ? mCurrentDocumentMapEntry->mString
+ : nsnull;
+
+ nsDocumentMapWriteEntry* docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&mDocumentMap, aURISpec,
+ PL_DHASH_ADD));
+ if (!docMapEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // If the generation number changed, refresh mCurrentDocumentMapEntry.
+ if (mCurrentDocumentMapEntry && mDocumentMap.generation != saveGeneration) {
+ mCurrentDocumentMapEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&mDocumentMap, saveURISpec,
+ PL_DHASH_LOOKUP));
+ NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(mCurrentDocumentMapEntry),
+ "mCurrentDocumentMapEntry lost during table growth?!");
+
+ // Refresh saveGeneration for use below when initializing uriMapEntry.
+ saveGeneration = mDocumentMap.generation;
+ }
+
+ NS_ASSERTION(docMapEntry->mString == nsnull,
+ "redundant multiplexed document?");
+ if (docMapEntry->mString)
+ return NS_ERROR_UNEXPECTED;
+
+ void* spec = nsMemory::Clone(aURISpec, strlen(aURISpec) + 1);
+ if (!spec)
+ return NS_ERROR_OUT_OF_MEMORY;
+ docMapEntry->mString = NS_REINTERPRET_CAST(const char*, spec);
+ docMapEntry->mURI = aURI;
+ NS_ADDREF(docMapEntry->mURI);
+
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapWriteEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapWriteEntry*,
+ PL_DHashTableOperate(&mURIMap, key, PL_DHASH_ADD));
+ if (!uriMapEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ASSERTION(uriMapEntry->mDocMapEntry == nsnull,
+ "URI mapped to two different specs?");
+ if (uriMapEntry->mDocMapEntry)
+ return NS_ERROR_UNEXPECTED;
+
+ uriMapEntry->mObject = key;
+ NS_ADDREF(uriMapEntry->mObject);
+ uriMapEntry->mDocMapEntry = docMapEntry;
+ uriMapEntry->mGeneration = saveGeneration;
+ uriMapEntry->mURISpec = NS_REINTERPRET_CAST(const char*, spec);
+ TRACE_MUX(('w', "start %p (%p) %s\n", aURI, key.get(), aURISpec));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::SelectMuxedDocument(nsISupports* aURI,
+ nsISupports** aResult)
+{
+ // Avoid repeatedly QI'ing to nsISeekableStream as we tell and seek.
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+
+ // Capture the current file offset (XXXbe maintain our own via Write?)
+ nsresult rv;
+ PRInt64 currentSegmentOffset;
+ rv = seekable->Tell(&currentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 currentSegmentOffset32 = currentSegmentOffset;
+ // Look for an existing entry keyed by aURI, added by StartMuxedDocument.
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapWriteEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapWriteEntry*,
+ PL_DHashTableOperate(&mURIMap, key, PL_DHASH_LOOKUP));
+ NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(uriMapEntry),
+ "SelectMuxedDocument without prior StartMuxedDocument?");
+ if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
+ return NS_ERROR_UNEXPECTED;
+
+ // Beware that uriMapEntry->mDocMapEntry may be stale, if an mDocumentMap
+ // addition caused that table to grow. We save the mDocumentMap generation
+ // in each uriMapEntry and compare it to the current generation, rehashing
+ // uriMapEntry->mURISpec if necessary.
+
+ nsDocumentMapWriteEntry* docMapEntry = uriMapEntry->mDocMapEntry;
+ if (uriMapEntry->mGeneration != mDocumentMap.generation) {
+ docMapEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&mDocumentMap,
+ uriMapEntry->mURISpec,
+ PL_DHASH_LOOKUP));
+ NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(docMapEntry), "lost mDocMapEntry!?");
+ uriMapEntry->mDocMapEntry = docMapEntry;
+ uriMapEntry->mGeneration = mDocumentMap.generation;
+ }
+ docMapEntry = uriMapEntry->mDocMapEntry;
+
+ // If there is a muxed document segment open, close it now by setting its
+ // length, stored in the second PRUint32 of the segment.
+ nsDocumentMapWriteEntry* prevDocMapEntry = mCurrentDocumentMapEntry;
+ if (prevDocMapEntry) {
+ if (prevDocMapEntry == docMapEntry) {
+ TRACE_MUX(('w', "select prev %s same as current!\n",
+ prevDocMapEntry->mString));
+ *aResult = docMapEntry->mURI;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+ }
+
+ PRUint32 prevSegmentOffset = prevDocMapEntry->mCurrentSegmentOffset;
+ TRACE_MUX(('w', "select prev %s offset %lu\n",
+ prevDocMapEntry->mString, prevSegmentOffset));
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ prevSegmentOffset + 4);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // The length counts all bytes in the segment, including the header
+ // that contains [nextSegmentOffset, length].
+ rv = Write32(currentSegmentOffset32 - prevSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Seek back to the current offset only if we are not going to seek
+ // back to *this* entry's last "current" segment offset and write its
+ // next segment offset at the first PRUint32 of the segment.
+ if (!docMapEntry->mInitialSegmentOffset) {
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ currentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ // If this entry was newly added, set its key and initial segment offset.
+ // Otherwise, seek back to write the next segment offset of the previous
+ // segment for this document in the multiplex.
+ if (!docMapEntry->mInitialSegmentOffset) {
+ docMapEntry->mInitialSegmentOffset = currentSegmentOffset32;
+ } else {
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ docMapEntry->mCurrentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(currentSegmentOffset32);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ currentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // Update this document's current segment offset so we can later fix its
+ // next segment offset (unless it is last, in which case we leave the zero
+ // placeholder as a terminator).
+ docMapEntry->mCurrentSegmentOffset = currentSegmentOffset32;
+
+ rv = Write32(0); // nextSegmentOffset placeholder
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(0); // length placeholder
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
+ NS_IF_ADDREF(*aResult);
+
+ mCurrentDocumentMapEntry = docMapEntry;
+ TRACE_MUX(('w', "select %p (%p) offset %lu\n",
+ aURI, key.get(), currentSegmentOffset));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::EndMuxedDocument(nsISupports* aURI)
+{
+ nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
+ nsURIMapWriteEntry* uriMapEntry =
+ NS_STATIC_CAST(nsURIMapWriteEntry*,
+ PL_DHashTableOperate(&mURIMap, key, PL_DHASH_LOOKUP));
+
+ // If the URI isn't in the map, nsFastLoadFileWriter::StartMuxedDocument
+ // must have been called with a redundant URI, *and* its caller must have
+ // ignored the NS_ERROR_UNEXPECTED it returned in that case.
+ if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry)) {
+ TRACE_MUX(('w', "bad end %p (%p)\n", aURI, key.get()));
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Drop our ref to the URI object that was passed to StartMuxedDocument,
+ // we no longer need it, and we do not want to extend its lifetime.
+ if (uriMapEntry->mDocMapEntry)
+ NS_RELEASE(uriMapEntry->mDocMapEntry->mURI);
+
+ // Shrink the table if half the entries are removed sentinels.
+ PRUint32 size = PL_DHASH_TABLE_SIZE(&mURIMap);
+ if (mURIMap.removedCount >= (size >> 2))
+ PL_DHashTableOperate(&mURIMap, key, PL_DHASH_REMOVE);
+ else
+ PL_DHashTableRawRemove(&mURIMap, uriMapEntry);
+
+ TRACE_MUX(('w', "end %p (%p)\n", aURI, key.get()));
+ return NS_OK;
+}
+
+struct nsDependencyMapEntry : public nsStringMapEntry {
+ PRInt64 mLastModified;
+};
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::AddDependency(nsIFile* aFile)
+{
+ nsCAutoString path;
+ nsresult rv = aFile->GetNativePath(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsDependencyMapEntry* entry =
+ NS_STATIC_CAST(nsDependencyMapEntry*,
+ PL_DHashTableOperate(&mDependencyMap, path.get(),
+ PL_DHASH_ADD));
+ if (!entry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (!entry->mString) {
+ const char *tmp = ToNewCString(path);
+ if (!tmp)
+ return NS_ERROR_OUT_OF_MEMORY;
+ entry->mString = tmp;
+
+ // If we can't get the last modified time from aFile, assume it does
+ // not exist, or is otherwise inaccessible to us (due to permissions),
+ // remove the dependency, and suppress the failure.
+ //
+ // Otherwise, we would end up aborting the fastload process due to a
+ // missing .js or .xul or other file on every startup.
+
+ rv = aFile->GetLastModifiedTime(&entry->mLastModified);
+ if (NS_FAILED(rv)) {
+ PL_DHashTableOperate(&mDependencyMap, path.get(), PL_DHASH_REMOVE);
+ rv = NS_OK;
+ }
+ }
+ return rv;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteFooterPrefix(const nsFastLoadFooterPrefix& aFooterPrefix)
+{
+ nsresult rv;
+
+ rv = Write32(aFooterPrefix.mNumIDs);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aFooterPrefix.mNumSharpObjects);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aFooterPrefix.mNumMuxedDocuments);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aFooterPrefix.mNumDependencies);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteSlowID(const nsID& aID)
+{
+ nsresult rv;
+
+ rv = Write32(aID.m0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write16(aID.m1);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write16(aID.m2);
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 bytesWritten;
+ rv = Write(NS_REINTERPRET_CAST(const char*, aID.m3), sizeof aID.m3,
+ &bytesWritten);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (bytesWritten != sizeof aID.m3)
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteFastID(NSFastLoadID aID)
+{
+ return Write32(aID ^ MFL_ID_XOR_KEY);
+}
+
+nsresult
+nsFastLoadFileWriter::WriteSharpObjectInfo(const nsFastLoadSharpObjectInfo& aInfo)
+{
+ nsresult rv;
+
+ NS_ASSERTION(aInfo.mCIDOffset != 0,
+ "fastload writer: mCIDOffset cannot be zero!");
+
+ rv = Write32(aInfo.mCIDOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write16(aInfo.mStrongRefCnt);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write16(aInfo.mWeakRefCnt);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteMuxedDocumentInfo(const nsFastLoadMuxedDocumentInfo& aInfo)
+{
+ nsresult rv;
+
+ rv = WriteStringZ(aInfo.mURISpec);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(aInfo.mInitialSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileWriter::IDMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsIDMapEntry* entry = NS_STATIC_CAST(nsIDMapEntry*, aHdr);
+ PRUint32 index = entry->mFastID - 1;
+ nsID* vector = NS_REINTERPRET_CAST(nsID*, aData);
+
+ NS_ASSERTION(index < aTable->entryCount, "bad nsIDMap index!");
+ vector[index] = entry->mSlowID;
+ return PL_DHASH_NEXT;
+}
+
+struct nsSharpObjectMapEntry : public nsObjectMapEntry {
+ NSFastLoadOID mOID;
+ nsFastLoadSharpObjectInfo mInfo;
+};
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileWriter::ObjectMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsSharpObjectMapEntry* entry = NS_STATIC_CAST(nsSharpObjectMapEntry*, aHdr);
+ PRUint32 index = MFL_OID_TO_SHARP_INDEX(entry->mOID);
+ nsFastLoadSharpObjectInfo* vector =
+ NS_REINTERPRET_CAST(nsFastLoadSharpObjectInfo*, aData);
+
+ NS_ASSERTION(index < aTable->entryCount, "bad nsObjectMap index!");
+ vector[index] = entry->mInfo;
+
+ NS_ASSERTION(entry->mInfo.mStrongRefCnt, "no strong ref in serialization!");
+
+ // Ignore tagged object ids stored as object pointer keys (the updater
+ // code does this).
+ if ((NS_PTR_TO_INT32(entry->mObject) & MFL_OBJECT_DEF_TAG) == 0)
+ NS_RELEASE(entry->mObject);
+
+ return PL_DHASH_NEXT;
+}
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileWriter::DocumentMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsFastLoadFileWriter* writer =
+ NS_REINTERPRET_CAST(nsFastLoadFileWriter*, aTable->data);
+ nsDocumentMapWriteEntry* entry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*, aHdr);
+ nsresult* rvp = NS_REINTERPRET_CAST(nsresult*, aData);
+
+ nsFastLoadMuxedDocumentInfo info;
+ info.mURISpec = entry->mString;
+ info.mInitialSegmentOffset = entry->mInitialSegmentOffset;
+ *rvp = writer->WriteMuxedDocumentInfo(info);
+
+ return NS_FAILED(*rvp) ? PL_DHASH_STOP : PL_DHASH_NEXT;
+}
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileWriter::DependencyMapEnumerate(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsFastLoadFileWriter* writer =
+ NS_REINTERPRET_CAST(nsFastLoadFileWriter*, aTable->data);
+ nsDependencyMapEntry* entry = NS_STATIC_CAST(nsDependencyMapEntry*, aHdr);
+ nsresult* rvp = NS_REINTERPRET_CAST(nsresult*, aData);
+
+ *rvp = writer->WriteStringZ(entry->mString);
+ if (NS_SUCCEEDED(*rvp))
+ *rvp = writer->Write64(entry->mLastModified);
+
+ return NS_FAILED(*rvp) ? PL_DHASH_STOP :PL_DHASH_NEXT;
+}
+
+nsresult
+nsFastLoadFileWriter::WriteFooter()
+{
+ nsresult rv;
+ PRUint32 i, count;
+
+ nsFastLoadFooterPrefix footerPrefix;
+ footerPrefix.mNumIDs = mIDMap.entryCount;
+ footerPrefix.mNumSharpObjects = mObjectMap.entryCount;
+ footerPrefix.mNumMuxedDocuments = mDocumentMap.entryCount;
+ footerPrefix.mNumDependencies = mDependencyMap.entryCount;
+
+ rv = WriteFooterPrefix(footerPrefix);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Enumerate mIDMap into a vector indexed by mFastID and write it.
+ nsID* idvec = new nsID[footerPrefix.mNumIDs];
+ if (!idvec)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ count = PL_DHashTableEnumerate(&mIDMap, IDMapEnumerate, idvec);
+ NS_ASSERTION(count == footerPrefix.mNumIDs, "bad mIDMap enumeration!");
+ for (i = 0; i < count; i++) {
+ rv = WriteSlowID(idvec[i]);
+ if (NS_FAILED(rv)) break;
+ }
+
+ delete[] idvec;
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Enumerate mObjectMap into a vector indexed by mOID and write it.
+ nsFastLoadSharpObjectInfo* objvec =
+ new nsFastLoadSharpObjectInfo[footerPrefix.mNumSharpObjects];
+ if (!objvec)
+ return NS_ERROR_OUT_OF_MEMORY;
+#ifdef NS_DEBUG
+ memset(objvec, 0, footerPrefix.mNumSharpObjects *
+ sizeof(nsFastLoadSharpObjectInfo));
+#endif
+
+ count = PL_DHashTableEnumerate(&mObjectMap, ObjectMapEnumerate, objvec);
+ NS_ASSERTION(count == footerPrefix.mNumSharpObjects,
+ "bad mObjectMap enumeration!");
+ for (i = 0; i < count; i++) {
+ rv = WriteSharpObjectInfo(objvec[i]);
+ if (NS_FAILED(rv)) break;
+ }
+
+ delete[] objvec;
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Enumerate mDocumentMap, writing nsFastLoadMuxedDocumentInfo records
+ count = PL_DHashTableEnumerate(&mDocumentMap, DocumentMapEnumerate, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NS_ASSERTION(count == footerPrefix.mNumMuxedDocuments,
+ "bad mDocumentMap enumeration!");
+
+ // Write out make-like file dependencies.
+ count = PL_DHashTableEnumerate(&mDependencyMap, DependencyMapEnumerate, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::Init()
+{
+ if (!PL_DHashTableInit(&mIDMap, &idmap_DHashTableOps, (void *)this,
+ sizeof(nsIDMapEntry), PL_DHASH_MIN_SIZE)) {
+ mIDMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&mObjectMap, &objmap_DHashTableOps, (void *)this,
+ sizeof(nsSharpObjectMapEntry), PL_DHASH_MIN_SIZE)) {
+ mObjectMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&mDocumentMap, &strmap_DHashTableOps, (void *)this,
+ sizeof(nsDocumentMapWriteEntry),
+ PL_DHASH_MIN_SIZE)) {
+ mDocumentMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&mURIMap, &objmap_DHashTableOps, (void *)this,
+ sizeof(nsURIMapWriteEntry), PL_DHASH_MIN_SIZE)) {
+ mURIMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!PL_DHashTableInit(&mDependencyMap, &strmap_DHashTableOps, (void *)this,
+ sizeof(nsDependencyMapEntry), PL_DHASH_MIN_SIZE)) {
+ mDependencyMap.ops = nsnull;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsFastLoadFileWriter::Open()
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ if (!seekable)
+ return NS_ERROR_UNEXPECTED;
+
+ nsresult rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ sizeof(nsFastLoadHeader));
+ if (NS_FAILED(rv))
+ return rv;
+
+ return Init();
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::Close()
+{
+ nsresult rv;
+
+ memcpy(mHeader.mMagic, magic, MFL_FILE_MAGIC_SIZE);
+ mHeader.mChecksum = 0;
+ mHeader.mVersion = MFL_FILE_VERSION;
+
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+
+ PRInt64 footerOffset;
+ rv = seekable->Tell(&footerOffset);
+
+ LL_L2UI(mHeader.mFooterOffset, footerOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // If there is a muxed document segment open, close it now by setting its
+ // length, stored in the second PRUint32 of the segment.
+ if (mCurrentDocumentMapEntry) {
+ PRUint32 currentSegmentOffset =
+ mCurrentDocumentMapEntry->mCurrentSegmentOffset;
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ currentSegmentOffset + 4);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(mHeader.mFooterOffset - currentSegmentOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Seek back to the current offset to write the footer.
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ mHeader.mFooterOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mCurrentDocumentMapEntry = nsnull;
+ }
+
+ rv = WriteFooter();
+ if (NS_FAILED(rv))
+ return rv;
+ PRInt64 fileSize;
+ rv = seekable->Tell(&fileSize);
+ LL_L2UI(mHeader.mFileSize, fileSize);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = WriteHeader(&mHeader);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Now compute the checksum, using mFileIO to get an input stream on the
+ // underlying FastLoad file.
+ if (mFileIO) {
+ // Get the unbuffered output stream, which flushes the buffered header
+ // so we can read and checksum it along with the rest of the file, and
+ // which allows us to write the checksum directly.
+ nsCOMPtr<nsIStreamBufferAccess>
+ bufferAccess(do_QueryInterface(mOutputStream));
+ nsCOMPtr<nsIOutputStream> output;
+ rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(output));
+ if (NS_FAILED(rv) || !output)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIInputStream> input;
+ rv = mFileIO->GetInputStream(getter_AddRefs(input));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Get the unbuffered input stream, to avoid copying overhead and to
+ // keep our view of the file coherent with the writer -- we don't want
+ // to hit a stale buffer in the reader's underlying stream.
+ bufferAccess = do_QueryInterface(input);
+ rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(input));
+ if (NS_FAILED(rv) || !input)
+ return NS_ERROR_UNEXPECTED;
+
+ // Seek the input stream to offset 0, in case it's a reader who has
+ // already been used to consume some of the FastLoad file.
+ seekable = do_QueryInterface(input);
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ char buf[MFL_CHECKSUM_BUFSIZE];
+ PRUint32 len, rem = 0;
+ PRUint32 checksum = 0;
+
+ // Ok, we're finally ready to checksum the FastLoad file we just wrote!
+ while (NS_SUCCEEDED(rv =
+ input->Read(buf + rem, sizeof buf - rem, &len)) &&
+ len) {
+ len += rem;
+ rem = NS_AccumulateFastLoadChecksum(&checksum,
+ NS_REINTERPRET_CAST(PRUint8*,
+ buf),
+ len,
+ PR_FALSE);
+ if (rem)
+ memcpy(buf, buf + len - rem, rem);
+ }
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (rem) {
+ NS_AccumulateFastLoadChecksum(&checksum,
+ NS_REINTERPRET_CAST(PRUint8*, buf),
+ rem,
+ PR_TRUE);
+ }
+
+ // Store the checksum in the FastLoad file header and remember it via
+ // mHeader.mChecksum, for GetChecksum.
+ seekable = do_QueryInterface(output);
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ offsetof(nsFastLoadHeader, mChecksum));
+ if (NS_FAILED(rv))
+ return rv;
+
+ mHeader.mChecksum = checksum;
+ checksum = NS_SWAP32(checksum);
+ PRUint32 bytesWritten;
+ rv = output->Write(NS_REINTERPRET_CAST(char*, &checksum),
+ sizeof checksum,
+ &bytesWritten);
+ if (NS_FAILED(rv))
+ return rv;
+ if (bytesWritten != sizeof checksum)
+ return NS_ERROR_FAILURE;
+ }
+
+ return mOutputStream->Close();
+}
+
+// Psuedo-tag used as flag between WriteSingleRefObject and WriteObjectCommon.
+#define MFL_SINGLE_REF_PSEUDO_TAG PR_BIT(MFL_OBJECT_TAG_BITS)
+
+nsresult
+nsFastLoadFileWriter::WriteObjectCommon(nsISupports* aObject,
+ PRBool aIsStrongRef,
+ PRUint32 aTags)
+{
+ nsrefcnt rc;
+ nsresult rv;
+
+ NS_ASSERTION((NS_PTR_TO_INT32(aObject) & MFL_OBJECT_DEF_TAG) == 0,
+ "odd nsISupports*, oh no!");
+
+ // Here be manual refcounting dragons!
+ rc = aObject->AddRef();
+ NS_ASSERTION(rc != 0, "bad refcnt when writing aObject!");
+
+ NSFastLoadOID oid;
+ nsCOMPtr<nsIClassInfo> classInfo;
+
+ if (rc == 2 && (aTags & MFL_SINGLE_REF_PSEUDO_TAG)) {
+ // Dull object: only one strong ref and no weak refs in serialization.
+ // Conservative: we don't trust the caller if there are more than two
+ // refs (one from the AddRef above, one from the data structure that's
+ // being serialized).
+ oid = MFL_DULL_OBJECT_OID;
+ aObject->Release();
+ } else {
+ // Object is presumed to be multiply connected through some combo of
+ // strong and weak refs. Hold onto it via mObjectMap.
+ nsSharpObjectMapEntry* entry =
+ NS_STATIC_CAST(nsSharpObjectMapEntry*,
+ PL_DHashTableOperate(&mObjectMap, aObject,
+ PL_DHASH_ADD));
+ if (!entry) {
+ aObject->Release();
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!entry->mObject) {
+ // First time we've seen this object address: add it to mObjectMap
+ // and serialize the object at the current stream offset.
+ PRInt64 thisOffset;
+ rv = Tell(&thisOffset);
+ if (NS_FAILED(rv)) {
+ aObject->Release();
+ return rv;
+ }
+
+ // NB: aObject was already held, and mObject is a raw nsISupports*.
+ entry->mObject = aObject;
+
+ oid = (mObjectMap.entryCount << MFL_OBJECT_TAG_BITS);
+ entry->mOID = oid;
+
+ // NB: the (32-bit, fast) CID and object data follow the OID.
+ entry->mInfo.mCIDOffset = thisOffset + sizeof(oid);
+ entry->mInfo.mStrongRefCnt = aIsStrongRef ? 1 : 0;
+ entry->mInfo.mWeakRefCnt = aIsStrongRef ? 0 : 1;
+
+ // Record in oid the fact that we're defining this object in the
+ // stream, and get the object's class info here, so we can take
+ // note of singletons in order to avoid reserializing them when
+ // updating after reading.
+ oid |= MFL_OBJECT_DEF_TAG;
+ classInfo = do_QueryInterface(aObject);
+ if (!classInfo)
+ return NS_ERROR_FAILURE;
+
+ PRUint32 flags;
+ if (NS_SUCCEEDED(classInfo->GetFlags(&flags)) &&
+ (flags & nsIClassInfo::SINGLETON)) {
+ MFL_SET_SINGLETON_FLAG(&entry->mInfo);
+ }
+ } else {
+ // Already serialized, recover oid and update the desired refcnt.
+ oid = entry->mOID;
+ if (aIsStrongRef) {
+ ++entry->mInfo.mStrongRefCnt;
+ NS_ASSERTION(entry->mInfo.mStrongRefCnt != 0,
+ "mStrongRefCnt overflow");
+ } else {
+ MFL_BUMP_WEAK_REFCNT(&entry->mInfo);
+ NS_ASSERTION(MFL_GET_WEAK_REFCNT(&entry->mInfo) != 0,
+ "mWeakRefCnt overflow");
+ }
+
+ aObject->Release();
+ }
+ }
+
+ if (!aIsStrongRef)
+ oid |= MFL_WEAK_REF_TAG;
+ oid |= (aTags & MFL_QUERY_INTERFACE_TAG);
+
+ rv = Write32(oid ^ MFL_OID_XOR_KEY);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (oid & MFL_OBJECT_DEF_TAG) {
+ nsCOMPtr<nsISerializable> serializable(do_QueryInterface(aObject));
+ if (!serializable)
+ return NS_ERROR_FAILURE;
+
+ nsCID slowCID;
+ rv = classInfo->GetClassIDNoAlloc(&slowCID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NSFastLoadID fastCID;
+ rv = MapID(slowCID, &fastCID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = WriteFastID(fastCID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = serializable->Write(this);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::WriteObject(nsISupports* aObject, PRBool aIsStrongRef)
+{
+#ifdef NS_DEBUG
+ nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
+
+ NS_ASSERTION(rootObject.get() == aObject,
+ "bad call to WriteObject -- call WriteCompoundObject!");
+#endif
+
+ return WriteObjectCommon(aObject, aIsStrongRef, 0);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::WriteSingleRefObject(nsISupports* aObject)
+{
+#ifdef NS_DEBUG
+ nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
+
+ NS_ASSERTION(rootObject.get() == aObject,
+ "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
+#endif
+
+ return WriteObjectCommon(aObject, PR_TRUE, MFL_SINGLE_REF_PSEUDO_TAG);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::WriteCompoundObject(nsISupports* aObject,
+ const nsIID& aIID,
+ PRBool aIsStrongRef)
+{
+ nsresult rv;
+ nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
+
+#ifdef NS_DEBUG
+ nsCOMPtr<nsISupports> roundtrip;
+ rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
+
+ NS_ASSERTION(rootObject.get() != aObject,
+ "wasteful call to WriteCompoundObject -- call WriteObject!");
+ NS_ASSERTION(roundtrip.get() == aObject,
+ "bad aggregation or multiple inheritance detected by call to "
+ "WriteCompoundObject!");
+#endif
+
+ rv = WriteObjectCommon(rootObject, aIsStrongRef, MFL_QUERY_INTERFACE_TAG);
+ if (NS_FAILED(rv))
+ return rv;
+
+ NSFastLoadID iid;
+ rv = MapID(aIID, &iid);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return WriteFastID(iid);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::WriteID(const nsID& aID)
+{
+ nsresult rv;
+ NSFastLoadID fastID;
+
+ rv = MapID(aID, &fastID);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return WriteFastID(fastID);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::Seek(PRInt32 aWhence, PRInt64 aOffset)
+{
+ mCurrentDocumentMapEntry = nsnull;
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ return seekable->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::Tell(PRInt64 *aResult)
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ return seekable->Tell(aResult);
+}
+
+NS_IMETHODIMP
+nsFastLoadFileWriter::SetEOF()
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ return seekable->SetEOF();
+}
+
+NS_COM nsresult
+NS_NewFastLoadFileWriter(nsIObjectOutputStream* *aResult,
+ nsIOutputStream* aDestStream,
+ nsIFastLoadFileIO* aFileIO)
+{
+ nsFastLoadFileWriter* writer =
+ new nsFastLoadFileWriter(aDestStream, aFileIO);
+ if (!writer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Stabilize writer's refcnt.
+ nsCOMPtr<nsIObjectOutputStream> stream(writer);
+
+ nsresult rv = writer->Open();
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = stream;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
+
+// -------------------------- nsFastLoadFileUpdater --------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED1(nsFastLoadFileUpdater,
+ nsFastLoadFileWriter,
+ nsIFastLoadFileIO)
+
+NS_IMETHODIMP
+nsFastLoadFileUpdater::GetInputStream(nsIInputStream** aResult)
+{
+ *aResult = mInputStream;
+ NS_IF_ADDREF(*aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileUpdater::GetOutputStream(nsIOutputStream** aResult)
+{
+ *aResult = nsnull;
+ return NS_OK;
+}
+
+PLDHashOperator PR_CALLBACK
+nsFastLoadFileUpdater::CopyReadDocumentMapEntryToUpdater(PLDHashTable *aTable,
+ PLDHashEntryHdr *aHdr,
+ PRUint32 aNumber,
+ void *aData)
+{
+ nsDocumentMapReadEntry* readEntry =
+ NS_STATIC_CAST(nsDocumentMapReadEntry*, aHdr);
+ nsFastLoadFileUpdater* updater =
+ NS_REINTERPRET_CAST(nsFastLoadFileUpdater*, aData);
+
+ void* spec = nsMemory::Clone(readEntry->mString,
+ strlen(readEntry->mString) + 1);
+ if (!spec)
+ return PL_DHASH_STOP;
+
+ nsDocumentMapWriteEntry* writeEntry =
+ NS_STATIC_CAST(nsDocumentMapWriteEntry*,
+ PL_DHashTableOperate(&updater->mDocumentMap, spec,
+ PL_DHASH_ADD));
+ if (!writeEntry) {
+ nsMemory::Free(spec);
+ return PL_DHASH_STOP;
+ }
+
+ writeEntry->mString = NS_REINTERPRET_CAST(const char*, spec);
+ writeEntry->mURI = nsnull;
+ writeEntry->mInitialSegmentOffset = readEntry->mInitialSegmentOffset;
+ writeEntry->mCurrentSegmentOffset = 0;
+ return PL_DHASH_NEXT;
+}
+
+nsresult
+nsFastLoadFileUpdater::Open(nsFastLoadFileReader* aReader)
+{
+ nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
+ if (!seekable)
+ return NS_ERROR_UNEXPECTED;
+
+ nsresult rv;
+ rv = nsFastLoadFileWriter::Init();
+ if (NS_FAILED(rv))
+ return rv;
+
+ PRUint32 i, n;
+
+ // Map from dense, zero-based, uint32 NSFastLoadID in reader to 16-byte
+ // nsID in updater.
+ nsID* readIDMap = aReader->mFooter.mIDMap;
+ for (i = 0, n = aReader->mFooter.mNumIDs; i < n; i++) {
+ NSFastLoadID fastID;
+ rv = MapID(readIDMap[i], &fastID);
+ NS_ASSERTION(fastID == i + 1, "huh?");
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // Map from reader dense, zero-based MFL_OID_TO_SHARP_INDEX(oid) to sharp
+ // object offset and refcnt information in updater.
+ nsFastLoadFileReader::nsObjectMapEntry* readObjectMap =
+ aReader->mFooter.mObjectMap;
+
+ // Prepare to save aReader state in case we need to seek back and read a
+ // singleton object that might otherwise get written by this updater.
+ nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
+ nsCOMPtr<nsISeekableStream> inputSeekable;
+ PRInt64 saveOffset = 0;
+
+ for (i = 0, n = aReader->mFooter.mNumSharpObjects; i < n; i++) {
+ nsFastLoadFileReader::nsObjectMapEntry* readEntry = &readObjectMap[i];
+
+ NS_ASSERTION(readEntry->mCIDOffset != 0,
+ "fastload updater: mCIDOffset cannot be zero!");
+
+ // If the reader didn't read this object but it's a singleton, we must
+ // "deserialize" it now, to discover its one and only root nsISupports
+ // address. The object already exists in memory if it was created at
+ // startup without resort to the FastLoad file. The canonical example
+ // is the system principal object held by all XUL JS scripts.
+
+ nsISupports* obj = readEntry->mReadObject;
+ if (!obj && MFL_GET_SINGLETON_FLAG(readEntry)) {
+ if (!saveDocMapEntry) {
+ inputSeekable = do_QueryInterface(aReader->mInputStream);
+ rv = inputSeekable->Tell(&saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ saveDocMapEntry = aReader->mCurrentDocumentMapEntry;
+ aReader->mCurrentDocumentMapEntry = nsnull;
+ }
+
+ rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ readEntry->mCIDOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = aReader
+ ->DeserializeObject(getter_AddRefs(readEntry->mReadObject));
+ if (NS_FAILED(rv))
+ return rv;
+ obj = readEntry->mReadObject;
+
+ // Don't forget to set mSkipOffset in case someone calls the reader
+ // to "deserialize" (yet again) the object we just read.
+ //
+ // Say the singleton is the system principal, and the FastLoad file
+ // contains data for navigator.xul including scripts and functions.
+ // If we update the FastLoad file to contain data for messenger.xul
+ // in a separate session started via mozilla -mail, *and during the
+ // same FastLoad episode in this session* race to open a navigator
+ // window, we will attempt to read all objects serialized in the
+ // navigator.xul portion of the FastLoad file.
+ //
+ // mSkipOffset must be set in such a case so the reader can skip
+ // the system principal's serialized data, because the updater for
+ // messenger.xul being opened here has already read it.
+
+ rv = inputSeekable->Tell(&readEntry->mSkipOffset);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ NSFastLoadOID oid = MFL_SHARP_INDEX_TO_OID(i);
+ void* key = obj
+ ? NS_REINTERPRET_CAST(void*, obj)
+ : NS_REINTERPRET_CAST(void*, (oid | MFL_OBJECT_DEF_TAG));
+
+ nsSharpObjectMapEntry* writeEntry =
+ NS_STATIC_CAST(nsSharpObjectMapEntry*,
+ PL_DHashTableOperate(&mObjectMap, key,
+ PL_DHASH_ADD));
+ if (!writeEntry)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Hold the object if there is one, so that objmap_ClearEntry can
+ // release the reference.
+ NS_IF_ADDREF(obj);
+ writeEntry->mObject = NS_REINTERPRET_CAST(nsISupports*, key);
+ writeEntry->mOID = oid;
+ writeEntry->mInfo.mCIDOffset = readEntry->mCIDOffset;
+ writeEntry->mInfo.mStrongRefCnt = readEntry->mSaveStrongRefCnt;
+ writeEntry->mInfo.mWeakRefCnt = readEntry->mSaveWeakRefCnt;
+ }
+
+ // If we had to read any singletons, restore aReader's saved state.
+ if (saveDocMapEntry) {
+ rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ aReader->mCurrentDocumentMapEntry = saveDocMapEntry;
+ }
+
+ // Copy URI spec string and initial segment offset in FastLoad file from
+ // nsDocumentMapReadEntry in reader to nsDocumentMapWriteEntry in updater.
+ // If we didn't enumerate all entries, we ran out of memory.
+ n = PL_DHashTableEnumerate(&aReader->mFooter.mDocumentMap,
+ CopyReadDocumentMapEntryToUpdater,
+ this);
+ if (n != aReader->mFooter.mDocumentMap.entryCount)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Copy source filename dependencies from reader to updater.
+ nsISupportsArray* readDeps = aReader->mFooter.mDependencies;
+ rv = readDeps->Count(&n);
+ if (NS_FAILED(rv))
+ return rv;
+
+ for (i = 0; i < n; i++) {
+ nsCOMPtr<nsIFile> file;
+ rv = readDeps->GetElementAt(i, getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = AddDependency(file);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ // Seek to the reader's footer offset so we overwrite the footer. First,
+ // update the header to have a zero mFooterOffset, which will invalidate
+ // the FastLoad file on next startup read attempt, should we crash before
+ // completing this update.
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ offsetof(nsFastLoadHeader, mFooterOffset));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = Write32(0);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
+ aReader->mHeader.mFooterOffset);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Avoid creating yet another object by implementing nsIFastLoadFileIO on
+ // this updater, and save aReader's input stream so it can be returned by
+ // GetInputStream called from nsFastLoadFileWriter::Close. This requires
+ // that we override Close to break the resulting zero-length cycle.
+ mFileIO = this;
+ mInputStream = aReader->mInputStream;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFastLoadFileUpdater::Close()
+{
+ // Call base-class Close implementation, which uses mFileIO.
+ nsresult rv = nsFastLoadFileWriter::Close();
+
+ // Break degenerate cycle from this->mFileIO to this.
+ mFileIO = nsnull;
+ return rv;
+}
+
+NS_COM nsresult
+NS_NewFastLoadFileUpdater(nsIObjectOutputStream* *aResult,
+ nsIOutputStream* aOutputStream,
+ nsIObjectInputStream* aReaderAsStream)
+{
+ // Make sure that aReaderAsStream is an nsFastLoadFileReader.
+ nsCOMPtr<nsIFastLoadFileReader> reader(do_QueryInterface(aReaderAsStream));
+ if (!reader)
+ return NS_ERROR_UNEXPECTED;
+
+ nsFastLoadFileUpdater* updater = new nsFastLoadFileUpdater(aOutputStream);
+ if (!updater)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Stabilize updater's refcnt.
+ nsCOMPtr<nsIObjectOutputStream> stream(updater);
+
+ nsresult rv = updater->Open(NS_STATIC_CAST(nsFastLoadFileReader*,
+ aReaderAsStream));
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = stream;
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}