nsFastLoadFile.cpp revision 677833bc953b6cb418c701facbdcf4aa18d6c44e
/* -*- 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
*
* 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>
{
if (!tfp) {
char tfn[16];
if (!tfp)
return;
}
}
#else
#endif
/*
* Fletcher's 16-bit checksum, using 32-bit two's-complement arithmetic.
*/
#define ONES_COMPLEMENT_ACCUMULATE(X,Y) (X) += (Y); if ((X) & 0x80000000) \
#define FLETCHER_ACCUMULATE(A,B,U) ONES_COMPLEMENT_ACCUMULATE(A, U); \
{
PRUint32 A = C & 0xffff;
PRUint32 B = C >> 16;
PRUint16 U = 0;
if (aLength >= 4) {
case 3:
FLETCHER_ACCUMULATE(A, B, U);
U = aBuffer[2];
aBuffer += 3;
aLength -= 3;
break;
case 2:
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) {
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) {
#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) {
switch (aLength) {
case 4:
FLETCHER_ACCUMULATE(A, B, U);
FLETCHER_ACCUMULATE(A, B, U);
break;
case 3:
FLETCHER_ACCUMULATE(A, B, U);
U = aBuffer[2];
FLETCHER_ACCUMULATE(A, B, U);
break;
case 2:
FLETCHER_ACCUMULATE(A, B, U);
break;
case 1:
U = aBuffer[0];
FLETCHER_ACCUMULATE(A, B, U);
break;
}
aLength = 0;
}
while (A >> 16)
while (B >> 16)
*aChecksum = (B << 16) | A;
return aLength;
}
{
while (A >> 16)
while (B >> 16)
return (B << 16) | A;
}
static const char magic[] = MFL_FILE_MAGIC;
// -------------------------- nsFastLoadFileReader --------------------------
{
return rv;
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
// nsIFastLoadFileControl methods:
{
return NS_OK;
}
{
return NS_OK;
}
struct nsStringMapEntry : public PLDHashEntryHdr {
const char* mString; // key, must come first
};
struct nsDocumentMapEntry : public nsStringMapEntry {
};
struct nsDocumentMapReadEntry : public nsDocumentMapEntry {
// Read, in case there is no Read before
// another entry is Selected (to improve
// input stream buffer utilization)
// mux schedule
};
PR_STATIC_CALLBACK(void)
{
}
static const PLDHashTableOps strmap_DHashTableOps = {
};
// 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 {
};
// Fast mapping from URI object pointer back to spec-indexed document info.
struct nsURIMapReadEntry : public nsObjectMapEntry {
};
PR_STATIC_CALLBACK(void)
{
// Ignore tagged object ids stored as object pointer keys (the updater
// code does this).
}
static const PLDHashTableOps objmap_DHashTableOps = {
};
{
return NS_OK;
}
{
// If the spec isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
// FastLoad service can try for a file update.
return NS_ERROR_NOT_AVAILABLE;
PL_DHASH_ADD));
if (!uriMapEntry)
return NS_ERROR_OUT_OF_MEMORY;
"URI mapped to two different specs?");
if (uriMapEntry->mDocMapEntry)
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
{
// Find the given URI's entry and select it for more reading.
// If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
// FastLoad service can try selecting the file updater.
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.
if (prevDocMapEntry &&
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.
if (docMapEntry == prevDocMapEntry) {
docMapEntry->mString));
}
// Invariant: docMapEntry->mBytesLeft implies docMapEntry->mSaveOffset has
// been set non-zero by the Tell call above.
else if (docMapEntry->mBytesLeft) {
"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.
}
#ifdef DEBUG_MUX
#endif
return NS_OK;
}
{
// 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.
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)
// Shrink the table if half the entries are removed sentinels.
else
return NS_OK;
}
{
if (entry) {
// Don't call our Seek wrapper, as it clears mCurrentDocumentMapEntry.
if (entry->mNeedToSeek) {
entry->mSaveOffset);
return rv;
}
// 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.
"document demuxed from FastLoad file more than once?");
if (entry->mNextSegmentOffset == 0)
return NS_ERROR_UNEXPECTED;
return rv;
// Clear mCurrentDocumentMapEntry temporarily to avoid recursion.
if (NS_SUCCEEDED(rv)) {
}
return rv;
}
}
#ifdef NS_DEBUG
// Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
if (entry->mBytesLeft == 0)
entry->mSaveOffset = 0;
#endif
}
return rv;
}
{
"ReadSegments called from above nsFastLoadFileReader layer?!");
aResult);
"demux ReadSegments underflow!");
#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
{
return rv;
if (bufferAccess) {
return rv;
if (!seekable)
return NS_ERROR_UNEXPECTED;
}
return rv;
char buf[MFL_CHECKSUM_BUFSIZE];
return rv;
return NS_ERROR_UNEXPECTED;
return rv;
rem += 4;
len) {
len,
PR_FALSE);
if (rem)
}
return rv;
if (rem) {
rem,
PR_TRUE);
}
return rv;
return NS_OK;
}
{
}
{
return rv;
return NS_ERROR_OUT_OF_MEMORY;
PRUint32 i, n;
return rv;
}
if (!aFooter->mObjectMap)
return NS_ERROR_OUT_OF_MEMORY;
for (i = 0, n = aFooter->mNumSharpObjects; i < n; i++) {
return rv;
entry->mSkipOffset = 0;
}
(void *)this, sizeof(nsDocumentMapReadEntry),
return NS_ERROR_OUT_OF_MEMORY;
}
(void *)this, sizeof(nsURIMapReadEntry),
return NS_ERROR_OUT_OF_MEMORY;
}
for (i = 0, n = aFooter->mNumMuxedDocuments; i < n; i++) {
return rv;
PL_DHASH_ADD));
if (!entry) {
return NS_ERROR_OUT_OF_MEMORY;
}
entry->mBytesLeft = 0;
entry->mSaveOffset = 0;
}
return rv;
for (i = 0, n = aFooter->mNumDependencies; i < n; i++) {
return rv;
return rv;
return rv;
return rv;
#ifdef DEBUG
printf("%s mtime changed, invalidating FastLoad file\n",
#endif
return NS_ERROR_FAILURE;
}
return rv;
}
return NS_OK;
}
{
return rv;
return rv;
return rv;
return rv;
return NS_OK;
}
{
return rv;
return rv;
return rv;
return rv;
return NS_ERROR_FAILURE;
return NS_OK;
}
{
if (NS_SUCCEEDED(rv))
*aID ^= MFL_ID_XOR_KEY;
return rv;
}
{
return rv;
"fastload reader: mCIDOffset cannot be zero!");
return rv;
return rv;
return NS_OK;
}
{
return rv;
return rv;
return NS_OK;
}
{
if (!seekable)
return NS_ERROR_UNEXPECTED;
// Don't bother buffering the header, as we immediately seek to EOF.
if (bufferAccess)
if (bufferAccess)
return rv;
return NS_ERROR_UNEXPECTED;
if (mHeader.mFooterOffset == 0)
return NS_ERROR_UNEXPECTED;
return rv;
return rv;
return NS_ERROR_UNEXPECTED;
return rv;
return rv;
sizeof(nsFastLoadHeader));
}
{
// 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.
}
return mInputStream->Close();
}
{
return rv;
return rv;
if (!serializable)
return NS_ERROR_FAILURE;
return rv;
return NS_OK;
}
{
return rv;
oid ^= MFL_OID_XOR_KEY;
if (oid == MFL_DULL_OBJECT_OID) {
// A very dull object, defined at point of single (strong) reference.
return rv;
} else {
(aIsStrongRef ? 0 : MFL_WEAK_REF_TAG),
"strong vs. weak ref deserialization mismatch!");
// Check whether we've already deserialized the object for this OID.
if (!object) {
return rv;
// 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.
"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.
entry->mCIDOffset);
return rv;
}
return rv;
// Save the "skip offset" in case we need to skip this object
// definition when reading forward, later on.
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.
return rv;
}
// Save object until all refs have been deserialized.
} 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) {
entry->mSkipOffset);
return rv;
}
}
if (aIsStrongRef) {
"mStrongRefCnt underflow!");
--entry->mStrongRefCnt;
} else {
"mWeakRefCnt underflow!");
}
}
if (oid & MFL_QUERY_INTERFACE_TAG) {
return rv;
NS_REINTERPRET_CAST(void**, aObject));
return rv;
} else {
}
return NS_OK;
}
{
return rv;
return NS_OK;
}
{
}
{
}
{
}
{
if (!reader)
return NS_ERROR_OUT_OF_MEMORY;
// Stabilize reader's refcnt.
return rv;
return NS_OK;
}
// -------------------------- nsFastLoadFileWriter --------------------------
struct nsIDMapEntry : public PLDHashEntryHdr {
};
PR_STATIC_CALLBACK(const void *)
{
}
{
}
const PLDHashEntryHdr *aHdr,
const void *aKey)
{
}
static const PLDHashTableOps idmap_DHashTableOps = {
};
{
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
{
return rv;
if (bytesWritten != MFL_FILE_MAGIC_SIZE)
return NS_ERROR_FAILURE;
return rv;
return rv;
return rv;
return rv;
return NS_OK;
}
// nsIFastLoadFileControl methods:
{
return NS_ERROR_NOT_AVAILABLE;
return NS_OK;
}
{
return NS_OK;
}
struct nsDocumentMapWriteEntry : public nsDocumentMapEntry {
};
// 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 {
const char* mURISpec;
};
{
return NS_OK;
}
const char* aURISpec)
{
// Save mDocumentMap table generation and mCurrentDocumentMapEntry key in
// case the hash table grows during the PL_DHASH_ADD operation.
const char* saveURISpec = mCurrentDocumentMapEntry
: nsnull;
PL_DHASH_ADD));
if (!docMapEntry)
return NS_ERROR_OUT_OF_MEMORY;
// If the generation number changed, refresh mCurrentDocumentMapEntry.
"mCurrentDocumentMapEntry lost during table growth?!");
// Refresh saveGeneration for use below when initializing uriMapEntry.
}
"redundant multiplexed document?");
if (docMapEntry->mString)
return NS_ERROR_UNEXPECTED;
if (!spec)
return NS_ERROR_OUT_OF_MEMORY;
if (!uriMapEntry)
return NS_ERROR_OUT_OF_MEMORY;
"URI mapped to two different specs?");
if (uriMapEntry->mDocMapEntry)
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
{
// Avoid repeatedly QI'ing to nsISeekableStream as we tell and seek.
// Capture the current file offset (XXXbe maintain our own via Write?)
return rv;
// Look for an existing entry keyed by aURI, added by StartMuxedDocument.
"SelectMuxedDocument without prior StartMuxedDocument?");
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.
}
// If there is a muxed document segment open, close it now by setting its
// length, stored in the second PRUint32 of the segment.
if (prevDocMapEntry) {
if (prevDocMapEntry == docMapEntry) {
return NS_OK;
}
prevSegmentOffset + 4);
return rv;
// The length counts all bytes in the segment, including the header
// that contains [nextSegmentOffset, length].
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) {
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) {
} else {
return rv;
return 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).
return rv;
return rv;
return NS_OK;
}
{
// 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)) {
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)
// Shrink the table if half the entries are removed sentinels.
else
return NS_OK;
}
struct nsDependencyMapEntry : public nsStringMapEntry {
};
{
return rv;
PL_DHASH_ADD));
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
if (!tmp)
return NS_ERROR_OUT_OF_MEMORY;
// 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.
}
}
return rv;
}
{
return rv;
return rv;
return rv;
return rv;
return NS_OK;
}
{
return rv;
return rv;
return rv;
&bytesWritten);
return rv;
return NS_ERROR_FAILURE;
return NS_OK;
}
{
}
{
"fastload writer: mCIDOffset cannot be zero!");
return rv;
return rv;
return rv;
return NS_OK;
}
{
return rv;
return rv;
return NS_OK;
}
void *aData)
{
return PL_DHASH_NEXT;
}
struct nsSharpObjectMapEntry : public nsObjectMapEntry {
};
void *aData)
{
// Ignore tagged object ids stored as object pointer keys (the updater
// code does this).
return PL_DHASH_NEXT;
}
void *aData)
{
}
void *aData)
{
if (NS_SUCCEEDED(*rvp))
}
{
return rv;
// Enumerate mIDMap into a vector indexed by mFastID and write it.
if (!idvec)
return NS_ERROR_OUT_OF_MEMORY;
for (i = 0; i < count; i++) {
}
delete[] idvec;
return rv;
// Enumerate mObjectMap into a vector indexed by mOID and write it.
if (!objvec)
return NS_ERROR_OUT_OF_MEMORY;
#ifdef NS_DEBUG
sizeof(nsFastLoadSharpObjectInfo));
#endif
"bad mObjectMap enumeration!");
for (i = 0; i < count; i++) {
}
delete[] objvec;
return rv;
// Enumerate mDocumentMap, writing nsFastLoadMuxedDocumentInfo records
return rv;
"bad mDocumentMap enumeration!");
// Write out make-like file dependencies.
return rv;
return NS_OK;
}
{
sizeof(nsIDMapEntry), PL_DHASH_MIN_SIZE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
sizeof(nsSharpObjectMapEntry), PL_DHASH_MIN_SIZE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
sizeof(nsDocumentMapWriteEntry),
return NS_ERROR_OUT_OF_MEMORY;
}
sizeof(nsURIMapWriteEntry), PL_DHASH_MIN_SIZE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
sizeof(nsDependencyMapEntry), PL_DHASH_MIN_SIZE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
{
if (!seekable)
return NS_ERROR_UNEXPECTED;
sizeof(nsFastLoadHeader));
return rv;
return Init();
}
{
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) {
currentSegmentOffset + 4);
return rv;
return rv;
// Seek back to the current offset to write the footer.
return rv;
}
rv = WriteFooter();
return rv;
return rv;
return 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.
return NS_ERROR_UNEXPECTED;
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.
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.
return rv;
char buf[MFL_CHECKSUM_BUFSIZE];
// Ok, we're finally ready to checksum the FastLoad file we just wrote!
while (NS_SUCCEEDED(rv =
len) {
buf),
len,
PR_FALSE);
if (rem)
}
return rv;
if (rem) {
rem,
PR_TRUE);
}
// Store the checksum in the FastLoad file header and remember it via
// mHeader.mChecksum, for GetChecksum.
return rv;
sizeof checksum,
&bytesWritten);
return rv;
if (bytesWritten != sizeof checksum)
return NS_ERROR_FAILURE;
}
return mOutputStream->Close();
}
// Psuedo-tag used as flag between WriteSingleRefObject and WriteObjectCommon.
{
"odd nsISupports*, oh no!");
// Here be manual refcounting dragons!
// 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).
} else {
// Object is presumed to be multiply connected through some combo of
// strong and weak refs. Hold onto it via mObjectMap.
PL_DHASH_ADD));
if (!entry) {
return NS_ERROR_OUT_OF_MEMORY;
}
// First time we've seen this object address: add it to mObjectMap
// and serialize the object at the current stream offset.
return rv;
}
// NB: aObject was already held, and mObject is a raw nsISupports*.
// NB: the (32-bit, fast) CID and object data follow the OID.
// 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.
if (!classInfo)
return NS_ERROR_FAILURE;
}
} else {
// Already serialized, recover oid and update the desired refcnt.
if (aIsStrongRef) {
"mStrongRefCnt overflow");
} else {
"mWeakRefCnt overflow");
}
}
}
if (!aIsStrongRef)
oid |= MFL_WEAK_REF_TAG;
return rv;
if (oid & MFL_OBJECT_DEF_TAG) {
if (!serializable)
return NS_ERROR_FAILURE;
return rv;
return rv;
return rv;
return rv;
}
return NS_OK;
}
{
#ifdef NS_DEBUG
"bad call to WriteObject -- call WriteCompoundObject!");
#endif
}
{
#ifdef NS_DEBUG
"bad call to WriteSingleRefObject -- call WriteCompoundObject!");
#endif
}
{
#ifdef NS_DEBUG
"wasteful call to WriteCompoundObject -- call WriteObject!");
"bad aggregation or multiple inheritance detected by call to "
"WriteCompoundObject!");
#endif
return rv;
return rv;
return WriteFastID(iid);
}
{
return rv;
return WriteFastID(fastID);
}
{
}
{
}
{
}
{
if (!writer)
return NS_ERROR_OUT_OF_MEMORY;
// Stabilize writer's refcnt.
return rv;
return NS_OK;
}
// -------------------------- nsFastLoadFileUpdater --------------------------
{
*aResult = mInputStream;
return NS_OK;
}
{
return NS_OK;
}
void *aData)
{
if (!spec)
return PL_DHASH_STOP;
PL_DHASH_ADD));
if (!writeEntry) {
return PL_DHASH_STOP;
}
return PL_DHASH_NEXT;
}
{
if (!seekable)
return NS_ERROR_UNEXPECTED;
return rv;
PRUint32 i, n;
// Map from dense, zero-based, uint32 NSFastLoadID in reader to 16-byte
// nsID in updater.
return rv;
}
// Map from reader dense, zero-based MFL_OID_TO_SHARP_INDEX(oid) to sharp
// object offset and refcnt information in updater.
// 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.
PRInt64 saveOffset = 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.
if (!saveDocMapEntry) {
return rv;
}
return rv;
return rv;
// 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.
return rv;
}
? NS_REINTERPRET_CAST(void*, obj)
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.
}
// If we had to read any singletons, restore aReader's saved state.
if (saveDocMapEntry) {
return rv;
}
// 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.
this);
return NS_ERROR_OUT_OF_MEMORY;
// Copy source filename dependencies from reader to updater.
return rv;
for (i = 0; i < n; i++) {
return 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.
return rv;
return 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;
return NS_OK;
}
{
// Call base-class Close implementation, which uses mFileIO.
// Break degenerate cycle from this->mFileIO to this.
return rv;
}
{
// Make sure that aReaderAsStream is an nsFastLoadFileReader.
if (!reader)
return NS_ERROR_UNEXPECTED;
if (!updater)
return NS_ERROR_OUT_OF_MEMORY;
// Stabilize updater's refcnt.
return rv;
return NS_OK;
}