1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla FastLoad code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
23 * Brendan Eich <brendan@mozilla.org> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
43 #include "nsEnumeratorUtils.h"
45 #include "nsXPIDLString.h"
47 #include "nsReadableUtils.h"
49 #include "nsIComponentManager.h"
51 #include "nsILocalFile.h"
52 #include "nsISeekableStream.h"
53 #include "nsISerializable.h"
54 #include "nsIStreamBufferAccess.h"
55 #include "nsIClassInfo.h"
57 #include "nsBinaryStream.h"
58 #include "nsFastLoadFile.h"
69 # define METER(x) /* nothing */
76 static void trace_mux(char mode
, const char *format
, ...)
82 sprintf(tfn
, "/tmp/mux.%ctrace", mode
);
83 tfp
= fopen(tfn
, "w");
86 setvbuf(tfp
, NULL
, _IOLBF
, 0);
89 vfprintf(tfp
, format
, ap
);
93 # define TRACE_MUX(args) trace_mux args
95 # define TRACE_MUX(args) /* nothing */
99 * Fletcher's 16-bit checksum, using 32-bit two's-complement arithmetic.
101 #define FOLD_ONES_COMPLEMENT_CARRY(X) ((X) = ((X) & 0xffff) + ((X) >> 16))
102 #define ONES_COMPLEMENT_ACCUMULATE(X,Y) (X) += (Y); if ((X) & 0x80000000) \
103 FOLD_ONES_COMPLEMENT_CARRY(X)
104 #define FLETCHER_ACCUMULATE(A,B,U) ONES_COMPLEMENT_ACCUMULATE(A, U); \
105 ONES_COMPLEMENT_ACCUMULATE(B, A)
108 NS_AccumulateFastLoadChecksum(PRUint32
*aChecksum
,
109 const PRUint8
* aBuffer
,
113 PRUint32 C
= *aChecksum
;
114 PRUint32 A
= C
& 0xffff;
115 PRUint32 B
= C
>> 16;
119 PRBool odd
= PRWord(aBuffer
) & 1;
120 switch (PRWord(aBuffer
) & 3) {
122 U
= (aBuffer
[0] << 8) | aBuffer
[1];
123 FLETCHER_ACCUMULATE(A
, B
, U
);
130 U
= (aBuffer
[0] << 8) | aBuffer
[1];
131 FLETCHER_ACCUMULATE(A
, B
, U
);
145 while (aLength
> 3) {
146 W
= *reinterpret_cast<const PRUint32
*>(aBuffer
);
150 FLETCHER_ACCUMULATE(A
, B
, U
);
151 U
= PRUint16(W
>> 8);
152 FLETCHER_ACCUMULATE(A
, B
, U
);
156 FLETCHER_ACCUMULATE(A
, B
, U
);
157 U
= PRUint16(W
>> 8);
159 FLETCHER_ACCUMULATE(A
, B
, U
);
165 aBuffer
--; // we're odd, we didn't checksum the last byte
168 while (aLength
> 3) {
169 W
= *reinterpret_cast<const PRUint32
*>(aBuffer
);
172 FLETCHER_ACCUMULATE(A
, B
, U
);
174 FLETCHER_ACCUMULATE(A
, B
, U
);
177 FLETCHER_ACCUMULATE(A
, B
, U
);
180 FLETCHER_ACCUMULATE(A
, B
, U
);
189 NS_ASSERTION(aLength
<= 4, "aLength botch");
192 U
= (aBuffer
[0] << 8) | aBuffer
[1];
193 FLETCHER_ACCUMULATE(A
, B
, U
);
194 U
= (aBuffer
[2] << 8) | aBuffer
[3];
195 FLETCHER_ACCUMULATE(A
, B
, U
);
199 U
= (aBuffer
[0] << 8) | aBuffer
[1];
200 FLETCHER_ACCUMULATE(A
, B
, U
);
202 FLETCHER_ACCUMULATE(A
, B
, U
);
206 U
= (aBuffer
[0] << 8) | aBuffer
[1];
207 FLETCHER_ACCUMULATE(A
, B
, U
);
212 FLETCHER_ACCUMULATE(A
, B
, U
);
220 FOLD_ONES_COMPLEMENT_CARRY(A
);
222 FOLD_ONES_COMPLEMENT_CARRY(B
);
224 *aChecksum
= (B
<< 16) | A
;
229 NS_AddFastLoadChecksums(PRUint32 sum1
, PRUint32 sum2
, PRUint32 sum2ByteCount
)
231 PRUint32 A1
= sum1
& 0xffff;
232 PRUint32 B1
= sum1
>> 16;
234 PRUint32 A2
= sum2
& 0xffff;
235 PRUint32 B2
= sum2
>> 16;
237 PRUint32 A
= A1
+ A2
;
239 FOLD_ONES_COMPLEMENT_CARRY(A
);
242 for (PRUint32 n
= (sum2ByteCount
+ 1) / 2; n
!= 0; n
--)
243 ONES_COMPLEMENT_ACCUMULATE(B
, B1
);
245 FOLD_ONES_COMPLEMENT_CARRY(B
);
247 return (B
<< 16) | A
;
250 #undef FOLD_ONES_COMPLEMENT_CARRY
251 #undef ONES_COMPLEMENT_ACCUMULATE
252 #undef FLETCHER_ACCUMULATE
254 static const char magic
[] = MFL_FILE_MAGIC
;
256 // -------------------------- nsFastLoadFileReader --------------------------
258 nsID
nsFastLoadFileReader::nsFastLoadFooter::gDummyID
;
259 nsFastLoadFileReader::nsObjectMapEntry
260 nsFastLoadFileReader::nsFastLoadFooter::gDummySharpObjectEntry
;
262 NS_IMPL_ISUPPORTS_INHERITED5(nsFastLoadFileReader
,
264 nsIObjectInputStream
,
265 nsIFastLoadFileControl
,
266 nsIFastLoadReadControl
,
268 nsIFastLoadFileReader
)
271 nsFastLoadFileReader::ReadHeader(nsFastLoadHeader
*aHeader
)
276 rv
= Read(reinterpret_cast<char*>(aHeader
), sizeof *aHeader
, &bytesRead
);
280 if (bytesRead
!= sizeof *aHeader
||
281 memcmp(aHeader
->mMagic
, magic
, MFL_FILE_MAGIC_SIZE
)) {
282 return NS_ERROR_UNEXPECTED
;
285 aHeader
->mChecksum
= NS_SWAP32(aHeader
->mChecksum
);
286 aHeader
->mVersion
= NS_SWAP32(aHeader
->mVersion
);
287 aHeader
->mFooterOffset
= NS_SWAP32(aHeader
->mFooterOffset
);
288 aHeader
->mFileSize
= NS_SWAP32(aHeader
->mFileSize
);
293 // nsIFastLoadFileControl methods:
296 nsFastLoadFileReader::GetChecksum(PRUint32
*aChecksum
)
298 *aChecksum
= mHeader
.mChecksum
;
303 nsFastLoadFileReader::SetChecksum(PRUint32 aChecksum
)
305 mHeader
.mChecksum
= aChecksum
;
309 struct nsStringMapEntry
: public PLDHashEntryHdr
{
310 const char* mString
; // key, must come first
311 nsISupports
* mURI
; // for SelectMuxedDocument return value
314 struct nsDocumentMapEntry
: public nsStringMapEntry
{
315 PRUint32 mInitialSegmentOffset
; // offset of URI's first segment in file
318 struct nsDocumentMapReadEntry
: public nsDocumentMapEntry
{
319 PRUint32 mNextSegmentOffset
; // offset of URI's next segment to read
320 PRUint32 mBytesLeft
: 31, // bytes remaining in current segment
321 mNeedToSeek
: 1; // flag to defer Seek from Select to
322 // Read, in case there is no Read before
323 // another entry is Selected (to improve
324 // input stream buffer utilization)
325 PRInt64 mSaveOffset
; // in case demux schedule differs from
329 PR_STATIC_CALLBACK(void)
330 strmap_ClearEntry(PLDHashTable
*aTable
, PLDHashEntryHdr
*aHdr
)
332 nsStringMapEntry
* entry
= static_cast<nsStringMapEntry
*>(aHdr
);
335 nsMemory::Free((void*) entry
->mString
);
336 NS_IF_RELEASE(entry
->mURI
);
337 PL_DHashClearEntryStub(aTable
, aHdr
);
340 static const PLDHashTableOps strmap_DHashTableOps
= {
344 PL_DHashMatchStringKey
,
345 PL_DHashMoveEntryStub
,
347 PL_DHashFinalizeStub
,
351 // An nsObjectMapEntry holds a strong reference to an XPCOM object, unless the
352 // mObject member, when cast to NSFastLoadOID, has its MFL_OBJECT_DEF_TAG bit
353 // set. NB: we rely on the fact that an nsISupports* is never an odd pointer.
354 struct nsObjectMapEntry
: public PLDHashEntryHdr
{
355 nsISupports
* mObject
; // key, must come first
358 // Fast mapping from URI object pointer back to spec-indexed document info.
359 struct nsURIMapReadEntry
: public nsObjectMapEntry
{
360 nsDocumentMapReadEntry
* mDocMapEntry
;
363 PR_STATIC_CALLBACK(void)
364 objmap_ClearEntry(PLDHashTable
*aTable
, PLDHashEntryHdr
*aHdr
)
366 nsObjectMapEntry
* entry
= static_cast<nsObjectMapEntry
*>(aHdr
);
368 // Ignore tagged object ids stored as object pointer keys (the updater
370 if ((NS_PTR_TO_INT32(entry
->mObject
) & MFL_OBJECT_DEF_TAG
) == 0)
371 NS_IF_RELEASE(entry
->mObject
);
372 PL_DHashClearEntryStub(aTable
, aHdr
);
375 static const PLDHashTableOps objmap_DHashTableOps
= {
378 PL_DHashVoidPtrKeyStub
,
379 PL_DHashMatchEntryStub
,
380 PL_DHashMoveEntryStub
,
382 PL_DHashFinalizeStub
,
387 nsFastLoadFileReader::HasMuxedDocument(const char* aURISpec
, PRBool
*aResult
)
389 nsDocumentMapReadEntry
* docMapEntry
=
390 static_cast<nsDocumentMapReadEntry
*>
391 (PL_DHashTableOperate(&mFooter
.mDocumentMap
, aURISpec
,
394 *aResult
= PL_DHASH_ENTRY_IS_BUSY(docMapEntry
);
399 nsFastLoadFileReader::StartMuxedDocument(nsISupports
* aURI
, const char* aURISpec
)
401 nsDocumentMapReadEntry
* docMapEntry
=
402 static_cast<nsDocumentMapReadEntry
*>
403 (PL_DHashTableOperate(&mFooter
.mDocumentMap
, aURISpec
,
406 // If the spec isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
407 // FastLoad service can try for a file update.
408 if (PL_DHASH_ENTRY_IS_FREE(docMapEntry
))
409 return NS_ERROR_NOT_AVAILABLE
;
411 nsCOMPtr
<nsISupports
> key(do_QueryInterface(aURI
));
412 nsURIMapReadEntry
* uriMapEntry
=
413 static_cast<nsURIMapReadEntry
*>
414 (PL_DHashTableOperate(&mFooter
.mURIMap
, key
,
417 return NS_ERROR_OUT_OF_MEMORY
;
419 NS_ASSERTION(uriMapEntry
->mDocMapEntry
== nsnull
,
420 "URI mapped to two different specs?");
421 if (uriMapEntry
->mDocMapEntry
)
422 return NS_ERROR_UNEXPECTED
;
424 docMapEntry
->mURI
= aURI
;
425 NS_ADDREF(docMapEntry
->mURI
);
426 uriMapEntry
->mObject
= key
;
427 NS_ADDREF(uriMapEntry
->mObject
);
428 uriMapEntry
->mDocMapEntry
= docMapEntry
;
429 TRACE_MUX(('r', "start %p (%p) %s\n", aURI
, key
.get(), aURISpec
));
434 nsFastLoadFileReader::SelectMuxedDocument(nsISupports
* aURI
,
435 nsISupports
** aResult
)
439 // Find the given URI's entry and select it for more reading.
440 nsCOMPtr
<nsISupports
> key(do_QueryInterface(aURI
));
441 nsURIMapReadEntry
* uriMapEntry
=
442 static_cast<nsURIMapReadEntry
*>
443 (PL_DHashTableOperate(&mFooter
.mURIMap
, key
,
446 // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
447 // FastLoad service can try selecting the file updater.
448 if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry
))
449 return NS_ERROR_NOT_AVAILABLE
;
451 // If we're interrupting another document's segment, save its offset so
452 // we can seek back when it's reselected. If prevDocMapEntry->mNeedToSeek
453 // is set, that means the stream is not positioned for prevDocMapEntry, to
454 // avoid buffer thrashing. See below in this function for more.
455 nsDocumentMapReadEntry
* prevDocMapEntry
= mCurrentDocumentMapEntry
;
456 if (prevDocMapEntry
&&
457 prevDocMapEntry
->mBytesLeft
&&
458 !prevDocMapEntry
->mNeedToSeek
) {
459 rv
= Tell(&prevDocMapEntry
->mSaveOffset
);
464 // It turns out we get a fair amount of redundant select calls, thanks to
465 // non-blocking hunks of data from the parser that are devoid of scripts.
466 // As more data gets FastLoaded, the number of these useless selects will
468 nsDocumentMapReadEntry
* docMapEntry
= uriMapEntry
->mDocMapEntry
;
469 if (docMapEntry
== prevDocMapEntry
) {
470 TRACE_MUX(('r', "select prev %s same as current!\n",
471 docMapEntry
->mString
));
474 // Invariant: docMapEntry->mBytesLeft implies docMapEntry->mSaveOffset has
475 // been set non-zero by the Tell call above.
476 else if (docMapEntry
->mBytesLeft
) {
477 NS_ASSERTION(docMapEntry
->mSaveOffset
!= 0,
478 "reselecting from multiplex at unsaved offset?");
480 // Defer Seek till Read, in case of "ping-pong" Selects without any
481 // intervening Reads, to avoid dumping the underlying mInputStream's
482 // input buffer for cases where alternate "pongs" fall in the same
484 docMapEntry
->mNeedToSeek
= PR_TRUE
;
487 *aResult
= prevDocMapEntry
? prevDocMapEntry
->mURI
: nsnull
;
488 NS_IF_ADDREF(*aResult
);
490 mCurrentDocumentMapEntry
= docMapEntry
;
492 PRInt64 currentSegmentOffset
;
493 Tell(¤tSegmentOffset
);
494 trace_mux('r', "select %p (%p) offset %ld\n",
495 aURI
, key
.get(), (long) currentSegmentOffset
);
501 nsFastLoadFileReader::EndMuxedDocument(nsISupports
* aURI
)
503 nsCOMPtr
<nsISupports
> key(do_QueryInterface(aURI
));
504 nsURIMapReadEntry
* uriMapEntry
=
505 static_cast<nsURIMapReadEntry
*>
506 (PL_DHashTableOperate(&mFooter
.mURIMap
, key
,
509 // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
510 // FastLoad service can try to end a select on its file updater.
511 if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry
))
512 return NS_ERROR_NOT_AVAILABLE
;
514 // Drop our ref to the URI object that was passed to StartMuxedDocument,
515 // we no longer need it, and we do not want to extend its lifetime.
516 if (uriMapEntry
->mDocMapEntry
)
517 NS_RELEASE(uriMapEntry
->mDocMapEntry
->mURI
);
519 // Shrink the table if half the entries are removed sentinels.
520 PRUint32 size
= PL_DHASH_TABLE_SIZE(&mFooter
.mURIMap
);
521 if (mFooter
.mURIMap
.removedCount
>= (size
>> 2))
522 PL_DHashTableOperate(&mFooter
.mURIMap
, key
, PL_DHASH_REMOVE
);
524 PL_DHashTableRawRemove(&mFooter
.mURIMap
, uriMapEntry
);
526 TRACE_MUX(('r', "end %p (%p)\n", aURI
, key
.get()));
531 nsFastLoadFileReader::Read(char* aBuffer
, PRUint32 aCount
, PRUint32
*aBytesRead
)
535 nsDocumentMapReadEntry
* entry
= mCurrentDocumentMapEntry
;
537 // Don't call our Seek wrapper, as it clears mCurrentDocumentMapEntry.
538 if (entry
->mNeedToSeek
) {
539 rv
= mSeekableInput
->Seek(nsISeekableStream::NS_SEEK_SET
,
544 entry
->mNeedToSeek
= PR_FALSE
;
547 // Loop to handle empty segments, which may be generated by the
548 // writer, given Start A; Start B; Select A; Select B; write B data;
549 // multiplexing schedules, which do tend to occur given non-blocking
550 // i/o with LIFO scheduling. XXXbe investigate LIFO issues
551 while (entry
->mBytesLeft
== 0) {
552 // Check for unexpected end of multiplexed stream.
553 NS_ASSERTION(entry
->mNextSegmentOffset
!= 0,
554 "document demuxed from FastLoad file more than once?");
555 if (entry
->mNextSegmentOffset
== 0)
556 return NS_ERROR_UNEXPECTED
;
558 rv
= mSeekableInput
->Seek(nsISeekableStream::NS_SEEK_SET
,
559 entry
->mNextSegmentOffset
);
563 // Clear mCurrentDocumentMapEntry temporarily to avoid recursion.
564 mCurrentDocumentMapEntry
= nsnull
;
566 rv
= Read32(&entry
->mNextSegmentOffset
);
567 if (NS_SUCCEEDED(rv
)) {
568 PRUint32 bytesLeft
= 0;
569 rv
= Read32(&bytesLeft
);
570 entry
->mBytesLeft
= bytesLeft
;
573 mCurrentDocumentMapEntry
= entry
;
577 NS_ASSERTION(entry
->mBytesLeft
>= 8, "demux segment length botch!");
578 entry
->mBytesLeft
-= 8;
582 rv
= nsBinaryInputStream::Read(aBuffer
, aCount
, aBytesRead
);
584 if (NS_SUCCEEDED(rv
) && entry
) {
585 NS_ASSERTION(entry
->mBytesLeft
>= *aBytesRead
, "demux Read underflow!");
586 entry
->mBytesLeft
-= *aBytesRead
;
589 // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
590 if (entry
->mBytesLeft
== 0)
591 entry
->mSaveOffset
= 0;
598 nsFastLoadFileReader::ReadSegments(nsWriteSegmentFun aWriter
, void* aClosure
,
599 PRUint32 aCount
, PRUint32
*aResult
)
601 nsDocumentMapReadEntry
* entry
= mCurrentDocumentMapEntry
;
603 NS_ASSERTION(!entry
|| (!entry
->mNeedToSeek
&& entry
->mBytesLeft
!= 0),
604 "ReadSegments called from above nsFastLoadFileReader layer?!");
606 nsresult rv
= nsBinaryInputStream::ReadSegments(aWriter
, aClosure
, aCount
,
608 if (NS_SUCCEEDED(rv
) && entry
) {
609 NS_ASSERTION(entry
->mBytesLeft
>= *aResult
,
610 "demux ReadSegments underflow!");
611 entry
->mBytesLeft
-= *aResult
;
614 // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
615 if (entry
->mBytesLeft
== 0)
616 entry
->mSaveOffset
= 0;
623 nsFastLoadFileReader::SetInputStream(nsIInputStream
*aInputStream
)
625 nsresult rv
= nsBinaryInputStream::SetInputStream(aInputStream
);
626 mSeekableInput
= do_QueryInterface(aInputStream
);
627 NS_ASSERTION(!mInputStream
|| mSeekableInput
,
628 "FastLoad requires a seekable input stream");
633 * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
635 #define MFL_CHECKSUM_BUFSIZE (6 * 8192)
638 nsFastLoadFileReader::ComputeChecksum(PRUint32
*aResult
)
640 nsCOMPtr
<nsIInputStream
> stream
= mInputStream
;
641 nsCOMPtr
<nsISeekableStream
> seekable
= mSeekableInput
;
644 nsresult rv
= seekable
->Tell(&saveOffset
);
649 rv
= mBufferAccess
->GetUnbufferedStream(getter_AddRefs(stream
));
653 seekable
= do_QueryInterface(stream
);
655 return NS_ERROR_UNEXPECTED
;
658 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, 0);
662 char buf
[MFL_CHECKSUM_BUFSIZE
];
665 rem
= offsetof(nsFastLoadHeader
, mChecksum
);
666 rv
= stream
->Read(buf
, rem
, &len
);
670 return NS_ERROR_UNEXPECTED
;
672 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_CUR
, 4);
675 memset(buf
+ rem
, 0, 4);
678 PRUint32 checksum
= 0;
679 while (NS_SUCCEEDED(rv
= stream
->Read(buf
+ rem
, sizeof buf
- rem
, &len
)) &&
682 rem
= NS_AccumulateFastLoadChecksum(&checksum
,
683 reinterpret_cast<PRUint8
*>(buf
),
687 memcpy(buf
, buf
+ len
- rem
, rem
);
693 NS_AccumulateFastLoadChecksum(&checksum
,
694 reinterpret_cast<PRUint8
*>(buf
),
699 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, saveOffset
);
708 nsFastLoadFileReader::GetDependencies(nsISimpleEnumerator
* *aDependencies
)
710 return NS_NewArrayEnumerator(aDependencies
, mFooter
.mDependencies
);
714 nsFastLoadFileReader::ReadFooter(nsFastLoadFooter
*aFooter
)
718 rv
= ReadFooterPrefix(aFooter
);
722 aFooter
->mIDMap
= new nsID
[aFooter
->mNumIDs
];
723 if (!aFooter
->mIDMap
)
724 return NS_ERROR_OUT_OF_MEMORY
;
727 for (i
= 0, n
= aFooter
->mNumIDs
; i
< n
; i
++) {
728 rv
= ReadSlowID(&aFooter
->mIDMap
[i
]);
733 aFooter
->mObjectMap
= new nsObjectMapEntry
[aFooter
->mNumSharpObjects
];
734 if (!aFooter
->mObjectMap
)
735 return NS_ERROR_OUT_OF_MEMORY
;
737 for (i
= 0, n
= aFooter
->mNumSharpObjects
; i
< n
; i
++) {
738 nsObjectMapEntry
* entry
= &aFooter
->mObjectMap
[i
];
740 rv
= ReadSharpObjectInfo(entry
);
744 entry
->mReadObject
= nsnull
;
745 entry
->mSkipOffset
= 0;
746 entry
->mSaveStrongRefCnt
= entry
->mStrongRefCnt
;
747 entry
->mSaveWeakRefCnt
= entry
->mWeakRefCnt
;
750 if (!PL_DHashTableInit(&aFooter
->mDocumentMap
, &strmap_DHashTableOps
,
751 (void *)this, sizeof(nsDocumentMapReadEntry
),
752 aFooter
->mNumMuxedDocuments
)) {
753 aFooter
->mDocumentMap
.ops
= nsnull
;
754 return NS_ERROR_OUT_OF_MEMORY
;
757 if (!PL_DHashTableInit(&aFooter
->mURIMap
, &objmap_DHashTableOps
,
758 (void *)this, sizeof(nsURIMapReadEntry
),
759 aFooter
->mNumMuxedDocuments
)) {
760 aFooter
->mURIMap
.ops
= nsnull
;
761 return NS_ERROR_OUT_OF_MEMORY
;
764 for (i
= 0, n
= aFooter
->mNumMuxedDocuments
; i
< n
; i
++) {
765 nsFastLoadMuxedDocumentInfo info
;
767 rv
= ReadMuxedDocumentInfo(&info
);
771 nsDocumentMapReadEntry
* entry
=
772 static_cast<nsDocumentMapReadEntry
*>
773 (PL_DHashTableOperate(&aFooter
->mDocumentMap
,
777 nsMemory::Free((void*) info
.mURISpec
);
778 return NS_ERROR_OUT_OF_MEMORY
;
781 NS_ASSERTION(!entry
->mString
, "duplicate URISpec in MuxedDocumentMap");
782 entry
->mString
= info
.mURISpec
;
783 entry
->mURI
= nsnull
;
784 entry
->mInitialSegmentOffset
= info
.mInitialSegmentOffset
;
785 entry
->mNextSegmentOffset
= info
.mInitialSegmentOffset
;
786 entry
->mBytesLeft
= 0;
787 entry
->mNeedToSeek
= PR_FALSE
;
788 entry
->mSaveOffset
= 0;
791 nsCOMPtr
<nsISupportsArray
> readDeps
;
792 rv
= NS_NewISupportsArray(getter_AddRefs(readDeps
));
796 nsCAutoString filename
;
797 for (i
= 0, n
= aFooter
->mNumDependencies
; i
< n
; i
++) {
798 rv
= ReadCString(filename
);
802 PRInt64 fastLoadMtime
;
803 rv
= Read64(reinterpret_cast<PRUint64
*>(&fastLoadMtime
));
807 nsCOMPtr
<nsILocalFile
> file
;
808 rv
= NS_NewNativeLocalFile(filename
, PR_TRUE
, getter_AddRefs(file
));
812 PRInt64 currentMtime
;
813 rv
= file
->GetLastModifiedTime(¤tMtime
);
817 if (LL_NE(fastLoadMtime
, currentMtime
)) {
820 file
->GetNativePath(path
);
821 printf("%s mtime changed, invalidating FastLoad file\n",
824 return NS_ERROR_FAILURE
;
827 rv
= readDeps
->AppendElement(file
);
832 aFooter
->mDependencies
= readDeps
;
837 nsFastLoadFileReader::ReadFooterPrefix(nsFastLoadFooterPrefix
*aFooterPrefix
)
841 rv
= Read32(&aFooterPrefix
->mNumIDs
);
845 rv
= Read32(&aFooterPrefix
->mNumSharpObjects
);
849 rv
= Read32(&aFooterPrefix
->mNumMuxedDocuments
);
853 rv
= Read32(&aFooterPrefix
->mNumDependencies
);
861 nsFastLoadFileReader::ReadSlowID(nsID
*aID
)
865 rv
= Read32(&aID
->m0
);
869 rv
= Read16(&aID
->m1
);
873 rv
= Read16(&aID
->m2
);
878 rv
= Read(reinterpret_cast<char*>(aID
->m3
), sizeof aID
->m3
, &bytesRead
);
882 if (bytesRead
!= sizeof aID
->m3
)
883 return NS_ERROR_FAILURE
;
888 nsFastLoadFileReader::ReadFastID(NSFastLoadID
*aID
)
890 nsresult rv
= Read32(aID
);
891 if (NS_SUCCEEDED(rv
))
892 *aID
^= MFL_ID_XOR_KEY
;
897 nsFastLoadFileReader::ReadSharpObjectInfo(nsFastLoadSharpObjectInfo
*aInfo
)
901 rv
= Read32(&aInfo
->mCIDOffset
);
905 NS_ASSERTION(aInfo
->mCIDOffset
!= 0,
906 "fastload reader: mCIDOffset cannot be zero!");
908 rv
= Read16(&aInfo
->mStrongRefCnt
);
912 rv
= Read16(&aInfo
->mWeakRefCnt
);
920 nsFastLoadFileReader::ReadMuxedDocumentInfo(nsFastLoadMuxedDocumentInfo
*aInfo
)
925 rv
= ReadCString(spec
);
929 rv
= Read32(&aInfo
->mInitialSegmentOffset
);
933 aInfo
->mURISpec
= ToNewCString(spec
);
938 nsFastLoadFileReader::Open()
942 // Don't bother buffering the header, as we immediately seek to EOF.
944 mBufferAccess
->DisableBuffering();
946 rv
= ReadHeader(&mHeader
);
949 mBufferAccess
->EnableBuffering();
953 if (mHeader
.mVersion
!= MFL_FILE_VERSION
)
954 return NS_ERROR_UNEXPECTED
;
955 if (mHeader
.mFooterOffset
== 0)
956 return NS_ERROR_UNEXPECTED
;
958 rv
= mSeekableInput
->Seek(nsISeekableStream::NS_SEEK_END
, 0);
963 rv
= mSeekableInput
->Tell(&fileSize
);
967 nsInt64 fileSize64
= fileSize
;
968 const nsInt64 maxUint32
= PR_UINT32_MAX
;
969 NS_ASSERTION(fileSize64
<= maxUint32
, "fileSize must fit in 32 bits");
970 if ((PRUint32
) fileSize64
!= mHeader
.mFileSize
)
971 return NS_ERROR_UNEXPECTED
;
973 rv
= mSeekableInput
->Seek(nsISeekableStream::NS_SEEK_SET
,
974 PRInt32(mHeader
.mFooterOffset
));
978 rv
= ReadFooter(&mFooter
);
982 return mSeekableInput
->Seek(nsISeekableStream::NS_SEEK_SET
,
983 sizeof(nsFastLoadHeader
));
987 nsFastLoadFileReader::Close()
989 // Give up our strong "keepalive" references, in case not all objects that
990 // were deserialized were fully re-connected.
992 // This happens for sure when an nsFastLoadFileUpdater is created and wraps
993 // an nsFastLoadFileReader whose data was already deserialized by an earlier
994 // FastLoad episode. The reader is useful in the second such episode during
995 // a session not so much for reading objects as for its footer information,
996 // which primes the updater's tables so that after the update completes, the
997 // FastLoad file has a superset footer.
999 for (PRUint32 i
= 0, n
= mFooter
.mNumSharpObjects
; i
< n
; i
++) {
1000 nsObjectMapEntry
* entry
= &mFooter
.mObjectMap
[i
];
1001 entry
->mReadObject
= nsnull
;
1004 return mInputStream
->Close();
1008 nsFastLoadFileReader::DeserializeObject(nsISupports
* *aObject
)
1011 NSFastLoadID fastCID
;
1013 rv
= ReadFastID(&fastCID
);
1017 const nsID
& slowCID
= mFooter
.GetID(fastCID
);
1018 nsCOMPtr
<nsISupports
> object(do_CreateInstance(slowCID
, &rv
));
1022 nsCOMPtr
<nsISerializable
> serializable(do_QueryInterface(object
));
1024 return NS_ERROR_FAILURE
;
1026 rv
= serializable
->Read(this);
1031 NS_ADDREF(*aObject
);
1036 nsFastLoadFileReader::ReadObject(PRBool aIsStrongRef
, nsISupports
* *aObject
)
1044 oid
^= MFL_OID_XOR_KEY
;
1046 nsCOMPtr
<nsISupports
> object
;
1048 if (oid
== MFL_DULL_OBJECT_OID
) {
1049 // A very dull object, defined at point of single (strong) reference.
1050 NS_ASSERTION(aIsStrongRef
, "dull object read via weak ref!");
1052 rv
= DeserializeObject(getter_AddRefs(object
));
1056 NS_ASSERTION((oid
& MFL_WEAK_REF_TAG
) ==
1057 (aIsStrongRef
? 0 : MFL_WEAK_REF_TAG
),
1058 "strong vs. weak ref deserialization mismatch!");
1060 nsObjectMapEntry
* entry
= &mFooter
.GetSharpObjectEntry(oid
);
1062 // Check whether we've already deserialized the object for this OID.
1063 object
= entry
->mReadObject
;
1066 nsDocumentMapReadEntry
* saveDocMapEntry
= nsnull
;
1068 rv
= mSeekableInput
->Tell(&saveOffset
);
1072 PRUint32 saveOffset32
= saveOffset
;
1073 if (entry
->mCIDOffset
!= saveOffset32
) {
1074 // We skipped deserialization of this object from its position
1075 // earlier in the input stream, presumably due to the reference
1076 // there being an nsFastLoadPtr, or (more likely) because the
1077 // object was muxed in another document, and deserialization
1078 // order does not match serialization order. So we must seek
1079 // back and read it now.
1080 NS_ASSERTION(entry
->mCIDOffset
< saveOffset32
,
1081 "out of order object?!");
1083 // Ape our Seek wrapper by clearing mCurrentDocumentMapEntry.
1084 // This allows for a skipped object to be referenced from two
1085 // or more multiplexed documents in the FastLoad file.
1086 saveDocMapEntry
= mCurrentDocumentMapEntry
;
1087 mCurrentDocumentMapEntry
= nsnull
;
1088 rv
= mSeekableInput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1094 rv
= DeserializeObject(getter_AddRefs(object
));
1098 if (entry
->mCIDOffset
!= saveOffset32
) {
1099 // Save the "skip offset" in case we need to skip this object
1100 // definition when reading forward, later on.
1101 rv
= mSeekableInput
->Tell(&entry
->mSkipOffset
);
1105 // Restore stream offset and mCurrentDocumentMapEntry in case
1106 // we're still reading forward through a part of the multiplex
1107 // to get object definitions eagerly.
1108 rv
= mSeekableInput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1112 mCurrentDocumentMapEntry
= saveDocMapEntry
;
1115 // Save object until all refs have been deserialized.
1116 entry
->mReadObject
= object
;
1118 // What if we are at a definition that's already been read? This
1119 // case arises when a sharp object's def is serialized before its
1120 // refs, while a non-defining ref is deserialized before the def.
1121 // We must skip over the object definition.
1122 if (oid
& MFL_OBJECT_DEF_TAG
) {
1123 NS_ASSERTION(entry
->mSkipOffset
!= 0, "impossible! see above");
1125 // Since we are seeking within a muxed segment, we must adjust
1126 // mBytesLeft, so that Seek called from Read will be triggered
1127 // when mBytesLeft goes to zero.
1128 PRInt64 currentOffset
;
1129 rv
= mSeekableInput
->Tell(¤tOffset
);
1133 NS_ASSERTION(entry
->mSkipOffset
> (PRUint32
)currentOffset
,
1134 "skipping backwards from object?!");
1135 NS_ASSERTION(mCurrentDocumentMapEntry
->mBytesLeft
>=
1136 entry
->mSkipOffset
- (PRUint32
)currentOffset
,
1137 "skipped object buffer underflow!");
1139 mCurrentDocumentMapEntry
->mBytesLeft
-=
1140 entry
->mSkipOffset
- (PRUint32
)currentOffset
;
1142 rv
= mSeekableInput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1143 entry
->mSkipOffset
);
1150 NS_ASSERTION(entry
->mStrongRefCnt
!= 0,
1151 "mStrongRefCnt underflow!");
1152 --entry
->mStrongRefCnt
;
1154 NS_ASSERTION(MFL_GET_WEAK_REFCNT(entry
) != 0,
1155 "mWeakRefCnt underflow!");
1156 MFL_DROP_WEAK_REFCNT(entry
);
1159 if (entry
->mStrongRefCnt
== 0 && MFL_GET_WEAK_REFCNT(entry
) == 0)
1160 entry
->mReadObject
= nsnull
;
1163 if (oid
& MFL_QUERY_INTERFACE_TAG
) {
1165 rv
= ReadFastID(&iid
);
1169 rv
= object
->QueryInterface(mFooter
.GetID(iid
),
1170 reinterpret_cast<void**>(aObject
));
1175 NS_ADDREF(*aObject
);
1182 nsFastLoadFileReader::ReadID(nsID
*aResult
)
1185 NSFastLoadID fastID
;
1187 rv
= ReadFastID(&fastID
);
1191 *aResult
= mFooter
.GetID(fastID
);
1196 nsFastLoadFileReader::Seek(PRInt32 aWhence
, PRInt64 aOffset
)
1198 mCurrentDocumentMapEntry
= nsnull
;
1199 return mSeekableInput
->Seek(aWhence
, aOffset
);
1203 nsFastLoadFileReader::Tell(PRInt64
*aResult
)
1205 return mSeekableInput
->Tell(aResult
);
1209 nsFastLoadFileReader::SetEOF()
1211 return mSeekableInput
->SetEOF();
1215 NS_NewFastLoadFileReader(nsIObjectInputStream
* *aResult
,
1216 nsIInputStream
* aSrcStream
)
1218 nsFastLoadFileReader
* reader
= new nsFastLoadFileReader(aSrcStream
);
1220 return NS_ERROR_OUT_OF_MEMORY
;
1222 // Stabilize reader's refcnt.
1223 nsCOMPtr
<nsIObjectInputStream
> stream(reader
);
1225 nsresult rv
= reader
->Open();
1230 NS_ADDREF(*aResult
);
1234 // -------------------------- nsFastLoadFileWriter --------------------------
1236 NS_IMPL_ISUPPORTS_INHERITED4(nsFastLoadFileWriter
,
1237 nsBinaryOutputStream
,
1238 nsIObjectOutputStream
,
1239 nsIFastLoadFileControl
,
1240 nsIFastLoadWriteControl
,
1243 struct nsIDMapEntry
: public PLDHashEntryHdr
{
1244 NSFastLoadID mFastID
; // 1 + nsFastLoadFooter::mIDMap index
1245 nsID mSlowID
; // key, used by PLDHashTableOps below
1248 PR_STATIC_CALLBACK(PLDHashNumber
)
1249 idmap_HashKey(PLDHashTable
*aTable
, const void *aKey
)
1251 const nsID
*idp
= reinterpret_cast<const nsID
*>(aKey
);
1256 PR_STATIC_CALLBACK(PRBool
)
1257 idmap_MatchEntry(PLDHashTable
*aTable
,
1258 const PLDHashEntryHdr
*aHdr
,
1261 const nsIDMapEntry
* entry
= static_cast<const nsIDMapEntry
*>(aHdr
);
1262 const nsID
*idp
= reinterpret_cast<const nsID
*>(aKey
);
1264 return memcmp(&entry
->mSlowID
, idp
, sizeof(nsID
)) == 0;
1267 static const PLDHashTableOps idmap_DHashTableOps
= {
1272 PL_DHashMoveEntryStub
,
1273 PL_DHashClearEntryStub
,
1274 PL_DHashFinalizeStub
,
1279 nsFastLoadFileWriter::MapID(const nsID
& aSlowID
, NSFastLoadID
*aResult
)
1281 nsIDMapEntry
* entry
=
1282 static_cast<nsIDMapEntry
*>
1283 (PL_DHashTableOperate(&mIDMap
, &aSlowID
, PL_DHASH_ADD
));
1285 return NS_ERROR_OUT_OF_MEMORY
;
1287 if (entry
->mFastID
== 0) {
1288 entry
->mFastID
= mIDMap
.entryCount
;
1289 entry
->mSlowID
= aSlowID
;
1292 *aResult
= entry
->mFastID
;
1297 nsFastLoadFileWriter::WriteHeader(nsFastLoadHeader
*aHeader
)
1300 PRUint32 bytesWritten
;
1302 rv
= Write(aHeader
->mMagic
, MFL_FILE_MAGIC_SIZE
, &bytesWritten
);
1306 if (bytesWritten
!= MFL_FILE_MAGIC_SIZE
)
1307 return NS_ERROR_FAILURE
;
1309 rv
= Write32(aHeader
->mChecksum
);
1313 rv
= Write32(aHeader
->mVersion
);
1317 rv
= Write32(aHeader
->mFooterOffset
);
1321 rv
= Write32(aHeader
->mFileSize
);
1328 // nsIFastLoadFileControl methods:
1331 nsFastLoadFileWriter::GetChecksum(PRUint32
*aChecksum
)
1333 if (mHeader
.mChecksum
== 0)
1334 return NS_ERROR_NOT_AVAILABLE
;
1335 *aChecksum
= mHeader
.mChecksum
;
1340 nsFastLoadFileWriter::SetChecksum(PRUint32 aChecksum
)
1342 mHeader
.mChecksum
= aChecksum
;
1346 struct nsDocumentMapWriteEntry
: public nsDocumentMapEntry
{
1347 PRUint32 mCurrentSegmentOffset
; // last written segment's offset
1350 // Fast mapping from URI object pointer back to spec-indexed document info.
1351 // We also may need the slow mapping from mURISpec to nsDocumentMapWriteEntry,
1352 // because the writer's mDocumentMap double hash table may grow "behind the
1353 // back of" each mURIMap entry's mDocMapEntry member.
1354 struct nsURIMapWriteEntry
: public nsObjectMapEntry
{
1355 nsDocumentMapWriteEntry
* mDocMapEntry
;
1356 PRUint32 mGeneration
;
1357 const char* mURISpec
;
1361 nsFastLoadFileWriter::HasMuxedDocument(const char* aURISpec
, PRBool
*aResult
)
1363 nsDocumentMapWriteEntry
* docMapEntry
=
1364 static_cast<nsDocumentMapWriteEntry
*>
1365 (PL_DHashTableOperate(&mDocumentMap
, aURISpec
,
1368 *aResult
= PL_DHASH_ENTRY_IS_BUSY(docMapEntry
);
1373 nsFastLoadFileWriter::StartMuxedDocument(nsISupports
* aURI
,
1374 const char* aURISpec
)
1376 // Save mDocumentMap table generation and mCurrentDocumentMapEntry key in
1377 // case the hash table grows during the PL_DHASH_ADD operation.
1378 PRUint32 saveGeneration
= mDocumentMap
.generation
;
1379 const char* saveURISpec
= mCurrentDocumentMapEntry
1380 ? mCurrentDocumentMapEntry
->mString
1383 nsDocumentMapWriteEntry
* docMapEntry
=
1384 static_cast<nsDocumentMapWriteEntry
*>
1385 (PL_DHashTableOperate(&mDocumentMap
, aURISpec
,
1388 return NS_ERROR_OUT_OF_MEMORY
;
1390 // If the generation number changed, refresh mCurrentDocumentMapEntry.
1391 if (mCurrentDocumentMapEntry
&& mDocumentMap
.generation
!= saveGeneration
) {
1392 mCurrentDocumentMapEntry
=
1393 static_cast<nsDocumentMapWriteEntry
*>
1394 (PL_DHashTableOperate(&mDocumentMap
, saveURISpec
,
1396 NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(mCurrentDocumentMapEntry
),
1397 "mCurrentDocumentMapEntry lost during table growth?!");
1399 // Refresh saveGeneration for use below when initializing uriMapEntry.
1400 saveGeneration
= mDocumentMap
.generation
;
1403 NS_WARN_IF_FALSE(docMapEntry
->mString
== nsnull
,
1404 "redundant multiplexed document?");
1405 if (docMapEntry
->mString
)
1406 return NS_ERROR_UNEXPECTED
;
1408 void* spec
= nsMemory::Clone(aURISpec
, strlen(aURISpec
) + 1);
1410 return NS_ERROR_OUT_OF_MEMORY
;
1411 docMapEntry
->mString
= reinterpret_cast<const char*>(spec
);
1412 docMapEntry
->mURI
= aURI
;
1413 NS_ADDREF(docMapEntry
->mURI
);
1415 nsCOMPtr
<nsISupports
> key(do_QueryInterface(aURI
));
1416 nsURIMapWriteEntry
* uriMapEntry
=
1417 static_cast<nsURIMapWriteEntry
*>
1418 (PL_DHashTableOperate(&mURIMap
, key
, PL_DHASH_ADD
));
1420 return NS_ERROR_OUT_OF_MEMORY
;
1422 NS_ASSERTION(uriMapEntry
->mDocMapEntry
== nsnull
,
1423 "URI mapped to two different specs?");
1424 if (uriMapEntry
->mDocMapEntry
)
1425 return NS_ERROR_UNEXPECTED
;
1427 uriMapEntry
->mObject
= key
;
1428 NS_ADDREF(uriMapEntry
->mObject
);
1429 uriMapEntry
->mDocMapEntry
= docMapEntry
;
1430 uriMapEntry
->mGeneration
= saveGeneration
;
1431 uriMapEntry
->mURISpec
= reinterpret_cast<const char*>(spec
);
1432 TRACE_MUX(('w', "start %p (%p) %s\n", aURI
, key
.get(), aURISpec
));
1437 nsFastLoadFileWriter::SelectMuxedDocument(nsISupports
* aURI
,
1438 nsISupports
** aResult
)
1440 // Capture the current file offset (XXXbe maintain our own via Write?)
1442 PRInt64 currentSegmentOffset
;
1443 rv
= mSeekableOutput
->Tell(¤tSegmentOffset
);
1447 PRUint32 currentSegmentOffset32
= currentSegmentOffset
;
1448 // Look for an existing entry keyed by aURI, added by StartMuxedDocument.
1449 nsCOMPtr
<nsISupports
> key(do_QueryInterface(aURI
));
1450 nsURIMapWriteEntry
* uriMapEntry
=
1451 static_cast<nsURIMapWriteEntry
*>
1452 (PL_DHashTableOperate(&mURIMap
, key
, PL_DHASH_LOOKUP
));
1453 NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(uriMapEntry
),
1454 "SelectMuxedDocument without prior StartMuxedDocument?");
1455 if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry
))
1456 return NS_ERROR_UNEXPECTED
;
1458 // Beware that uriMapEntry->mDocMapEntry may be stale, if an mDocumentMap
1459 // addition caused that table to grow. We save the mDocumentMap generation
1460 // in each uriMapEntry and compare it to the current generation, rehashing
1461 // uriMapEntry->mURISpec if necessary.
1463 nsDocumentMapWriteEntry
* docMapEntry
= uriMapEntry
->mDocMapEntry
;
1464 if (uriMapEntry
->mGeneration
!= mDocumentMap
.generation
) {
1466 static_cast<nsDocumentMapWriteEntry
*>
1467 (PL_DHashTableOperate(&mDocumentMap
,
1468 uriMapEntry
->mURISpec
,
1470 NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(docMapEntry
), "lost mDocMapEntry!?");
1471 uriMapEntry
->mDocMapEntry
= docMapEntry
;
1472 uriMapEntry
->mGeneration
= mDocumentMap
.generation
;
1475 // If there is a muxed document segment open, close it now by setting its
1476 // length, stored in the second PRUint32 of the segment.
1477 nsDocumentMapWriteEntry
* prevDocMapEntry
= mCurrentDocumentMapEntry
;
1478 if (prevDocMapEntry
) {
1479 if (prevDocMapEntry
== docMapEntry
) {
1480 TRACE_MUX(('w', "select prev %s same as current!\n",
1481 prevDocMapEntry
->mString
));
1482 *aResult
= docMapEntry
->mURI
;
1483 NS_ADDREF(*aResult
);
1487 PRUint32 prevSegmentOffset
= prevDocMapEntry
->mCurrentSegmentOffset
;
1488 TRACE_MUX(('w', "select prev %s offset %lu\n",
1489 prevDocMapEntry
->mString
, prevSegmentOffset
));
1491 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1492 prevSegmentOffset
+ 4);
1496 // The length counts all bytes in the segment, including the header
1497 // that contains [nextSegmentOffset, length].
1498 rv
= Write32(currentSegmentOffset32
- prevSegmentOffset
);
1502 // Seek back to the current offset only if we are not going to seek
1503 // back to *this* entry's last "current" segment offset and write its
1504 // next segment offset at the first PRUint32 of the segment.
1505 if (!docMapEntry
->mInitialSegmentOffset
) {
1506 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1507 currentSegmentOffset
);
1513 // If this entry was newly added, set its key and initial segment offset.
1514 // Otherwise, seek back to write the next segment offset of the previous
1515 // segment for this document in the multiplex.
1516 if (!docMapEntry
->mInitialSegmentOffset
) {
1517 docMapEntry
->mInitialSegmentOffset
= currentSegmentOffset32
;
1519 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1520 docMapEntry
->mCurrentSegmentOffset
);
1524 rv
= Write32(currentSegmentOffset32
);
1528 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1529 currentSegmentOffset
);
1534 // Update this document's current segment offset so we can later fix its
1535 // next segment offset (unless it is last, in which case we leave the zero
1536 // placeholder as a terminator).
1537 docMapEntry
->mCurrentSegmentOffset
= currentSegmentOffset32
;
1539 rv
= Write32(0); // nextSegmentOffset placeholder
1543 rv
= Write32(0); // length placeholder
1547 *aResult
= prevDocMapEntry
? prevDocMapEntry
->mURI
: nsnull
;
1548 NS_IF_ADDREF(*aResult
);
1550 mCurrentDocumentMapEntry
= docMapEntry
;
1551 TRACE_MUX(('w', "select %p (%p) offset %lu\n",
1552 aURI
, key
.get(), currentSegmentOffset
));
1557 nsFastLoadFileWriter::EndMuxedDocument(nsISupports
* aURI
)
1559 nsCOMPtr
<nsISupports
> key(do_QueryInterface(aURI
));
1560 nsURIMapWriteEntry
* uriMapEntry
=
1561 static_cast<nsURIMapWriteEntry
*>
1562 (PL_DHashTableOperate(&mURIMap
, key
, PL_DHASH_LOOKUP
));
1564 // If the URI isn't in the map, nsFastLoadFileWriter::StartMuxedDocument
1565 // must have been called with a redundant URI, *and* its caller must have
1566 // ignored the NS_ERROR_UNEXPECTED it returned in that case.
1567 if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry
)) {
1568 TRACE_MUX(('w', "bad end %p (%p)\n", aURI
, key
.get()));
1569 return NS_ERROR_UNEXPECTED
;
1572 // Drop our ref to the URI object that was passed to StartMuxedDocument,
1573 // we no longer need it, and we do not want to extend its lifetime.
1574 if (uriMapEntry
->mDocMapEntry
)
1575 NS_RELEASE(uriMapEntry
->mDocMapEntry
->mURI
);
1577 // Shrink the table if half the entries are removed sentinels.
1578 PRUint32 size
= PL_DHASH_TABLE_SIZE(&mURIMap
);
1579 if (mURIMap
.removedCount
>= (size
>> 2))
1580 PL_DHashTableOperate(&mURIMap
, key
, PL_DHASH_REMOVE
);
1582 PL_DHashTableRawRemove(&mURIMap
, uriMapEntry
);
1584 TRACE_MUX(('w', "end %p (%p)\n", aURI
, key
.get()));
1588 struct nsDependencyMapEntry
: public nsStringMapEntry
{
1589 PRInt64 mLastModified
;
1593 nsFastLoadFileWriter::AddDependency(nsIFile
* aFile
)
1596 nsresult rv
= aFile
->GetNativePath(path
);
1600 nsDependencyMapEntry
* entry
=
1601 static_cast<nsDependencyMapEntry
*>
1602 (PL_DHashTableOperate(&mDependencyMap
, path
.get(),
1605 return NS_ERROR_OUT_OF_MEMORY
;
1607 if (!entry
->mString
) {
1608 const char *tmp
= ToNewCString(path
);
1610 return NS_ERROR_OUT_OF_MEMORY
;
1611 entry
->mString
= tmp
;
1613 // If we can't get the last modified time from aFile, assume it does
1614 // not exist, or is otherwise inaccessible to us (due to permissions),
1615 // remove the dependency, and suppress the failure.
1617 // Otherwise, we would end up aborting the fastload process due to a
1618 // missing .js or .xul or other file on every startup.
1620 rv
= aFile
->GetLastModifiedTime(&entry
->mLastModified
);
1621 if (NS_FAILED(rv
)) {
1622 PL_DHashTableOperate(&mDependencyMap
, path
.get(), PL_DHASH_REMOVE
);
1630 nsFastLoadFileWriter::WriteFooterPrefix(const nsFastLoadFooterPrefix
& aFooterPrefix
)
1634 rv
= Write32(aFooterPrefix
.mNumIDs
);
1638 rv
= Write32(aFooterPrefix
.mNumSharpObjects
);
1642 rv
= Write32(aFooterPrefix
.mNumMuxedDocuments
);
1646 rv
= Write32(aFooterPrefix
.mNumDependencies
);
1654 nsFastLoadFileWriter::WriteSlowID(const nsID
& aID
)
1658 rv
= Write32(aID
.m0
);
1662 rv
= Write16(aID
.m1
);
1666 rv
= Write16(aID
.m2
);
1670 PRUint32 bytesWritten
;
1671 rv
= Write(reinterpret_cast<const char*>(aID
.m3
), sizeof aID
.m3
,
1676 if (bytesWritten
!= sizeof aID
.m3
)
1677 return NS_ERROR_FAILURE
;
1682 nsFastLoadFileWriter::WriteFastID(NSFastLoadID aID
)
1684 return Write32(aID
^ MFL_ID_XOR_KEY
);
1688 nsFastLoadFileWriter::WriteSharpObjectInfo(const nsFastLoadSharpObjectInfo
& aInfo
)
1692 NS_ASSERTION(aInfo
.mCIDOffset
!= 0,
1693 "fastload writer: mCIDOffset cannot be zero!");
1695 rv
= Write32(aInfo
.mCIDOffset
);
1699 rv
= Write16(aInfo
.mStrongRefCnt
);
1703 rv
= Write16(aInfo
.mWeakRefCnt
);
1711 nsFastLoadFileWriter::WriteMuxedDocumentInfo(const nsFastLoadMuxedDocumentInfo
& aInfo
)
1715 rv
= WriteStringZ(aInfo
.mURISpec
);
1719 rv
= Write32(aInfo
.mInitialSegmentOffset
);
1726 PLDHashOperator PR_CALLBACK
1727 nsFastLoadFileWriter::IDMapEnumerate(PLDHashTable
*aTable
,
1728 PLDHashEntryHdr
*aHdr
,
1732 nsIDMapEntry
* entry
= static_cast<nsIDMapEntry
*>(aHdr
);
1733 PRUint32 index
= entry
->mFastID
- 1;
1734 nsID
* vector
= reinterpret_cast<nsID
*>(aData
);
1736 NS_ASSERTION(index
< aTable
->entryCount
, "bad nsIDMap index!");
1737 vector
[index
] = entry
->mSlowID
;
1738 return PL_DHASH_NEXT
;
1741 struct nsSharpObjectMapEntry
: public nsObjectMapEntry
{
1743 nsFastLoadSharpObjectInfo mInfo
;
1746 PLDHashOperator PR_CALLBACK
1747 nsFastLoadFileWriter::ObjectMapEnumerate(PLDHashTable
*aTable
,
1748 PLDHashEntryHdr
*aHdr
,
1752 nsSharpObjectMapEntry
* entry
= static_cast<nsSharpObjectMapEntry
*>(aHdr
);
1753 PRUint32 index
= MFL_OID_TO_SHARP_INDEX(entry
->mOID
);
1754 nsFastLoadSharpObjectInfo
* vector
=
1755 reinterpret_cast<nsFastLoadSharpObjectInfo
*>(aData
);
1757 NS_ASSERTION(index
< aTable
->entryCount
, "bad nsObjectMap index!");
1758 vector
[index
] = entry
->mInfo
;
1760 NS_ASSERTION(entry
->mInfo
.mStrongRefCnt
, "no strong ref in serialization!");
1762 // Ignore tagged object ids stored as object pointer keys (the updater
1764 if ((NS_PTR_TO_INT32(entry
->mObject
) & MFL_OBJECT_DEF_TAG
) == 0)
1765 NS_RELEASE(entry
->mObject
);
1767 return PL_DHASH_NEXT
;
1770 PLDHashOperator PR_CALLBACK
1771 nsFastLoadFileWriter::DocumentMapEnumerate(PLDHashTable
*aTable
,
1772 PLDHashEntryHdr
*aHdr
,
1776 nsFastLoadFileWriter
* writer
=
1777 reinterpret_cast<nsFastLoadFileWriter
*>(aTable
->data
);
1778 nsDocumentMapWriteEntry
* entry
=
1779 static_cast<nsDocumentMapWriteEntry
*>(aHdr
);
1780 nsresult
* rvp
= reinterpret_cast<nsresult
*>(aData
);
1782 nsFastLoadMuxedDocumentInfo info
;
1783 info
.mURISpec
= entry
->mString
;
1784 info
.mInitialSegmentOffset
= entry
->mInitialSegmentOffset
;
1785 *rvp
= writer
->WriteMuxedDocumentInfo(info
);
1787 return NS_FAILED(*rvp
) ? PL_DHASH_STOP
: PL_DHASH_NEXT
;
1790 PLDHashOperator PR_CALLBACK
1791 nsFastLoadFileWriter::DependencyMapEnumerate(PLDHashTable
*aTable
,
1792 PLDHashEntryHdr
*aHdr
,
1796 nsFastLoadFileWriter
* writer
=
1797 reinterpret_cast<nsFastLoadFileWriter
*>(aTable
->data
);
1798 nsDependencyMapEntry
* entry
= static_cast<nsDependencyMapEntry
*>(aHdr
);
1799 nsresult
* rvp
= reinterpret_cast<nsresult
*>(aData
);
1801 *rvp
= writer
->WriteStringZ(entry
->mString
);
1802 if (NS_SUCCEEDED(*rvp
))
1803 *rvp
= writer
->Write64(entry
->mLastModified
);
1805 return NS_FAILED(*rvp
) ? PL_DHASH_STOP
:PL_DHASH_NEXT
;
1809 nsFastLoadFileWriter::WriteFooter()
1814 nsFastLoadFooterPrefix footerPrefix
;
1815 footerPrefix
.mNumIDs
= mIDMap
.entryCount
;
1816 footerPrefix
.mNumSharpObjects
= mObjectMap
.entryCount
;
1817 footerPrefix
.mNumMuxedDocuments
= mDocumentMap
.entryCount
;
1818 footerPrefix
.mNumDependencies
= mDependencyMap
.entryCount
;
1820 rv
= WriteFooterPrefix(footerPrefix
);
1824 // Enumerate mIDMap into a vector indexed by mFastID and write it.
1825 nsID
* idvec
= new nsID
[footerPrefix
.mNumIDs
];
1827 return NS_ERROR_OUT_OF_MEMORY
;
1829 count
= PL_DHashTableEnumerate(&mIDMap
, IDMapEnumerate
, idvec
);
1830 NS_ASSERTION(count
== footerPrefix
.mNumIDs
, "bad mIDMap enumeration!");
1831 for (i
= 0; i
< count
; i
++) {
1832 rv
= WriteSlowID(idvec
[i
]);
1833 if (NS_FAILED(rv
)) break;
1840 // Enumerate mObjectMap into a vector indexed by mOID and write it.
1841 nsFastLoadSharpObjectInfo
* objvec
=
1842 new nsFastLoadSharpObjectInfo
[footerPrefix
.mNumSharpObjects
];
1844 return NS_ERROR_OUT_OF_MEMORY
;
1846 memset(objvec
, 0, footerPrefix
.mNumSharpObjects
*
1847 sizeof(nsFastLoadSharpObjectInfo
));
1850 count
= PL_DHashTableEnumerate(&mObjectMap
, ObjectMapEnumerate
, objvec
);
1851 NS_ASSERTION(count
== footerPrefix
.mNumSharpObjects
,
1852 "bad mObjectMap enumeration!");
1853 for (i
= 0; i
< count
; i
++) {
1854 rv
= WriteSharpObjectInfo(objvec
[i
]);
1855 if (NS_FAILED(rv
)) break;
1862 // Enumerate mDocumentMap, writing nsFastLoadMuxedDocumentInfo records
1863 count
= PL_DHashTableEnumerate(&mDocumentMap
, DocumentMapEnumerate
, &rv
);
1867 NS_ASSERTION(count
== footerPrefix
.mNumMuxedDocuments
,
1868 "bad mDocumentMap enumeration!");
1870 // Write out make-like file dependencies.
1871 count
= PL_DHashTableEnumerate(&mDependencyMap
, DependencyMapEnumerate
, &rv
);
1879 nsFastLoadFileWriter::Init()
1881 if (!PL_DHashTableInit(&mIDMap
, &idmap_DHashTableOps
, (void *)this,
1882 sizeof(nsIDMapEntry
), PL_DHASH_MIN_SIZE
)) {
1883 mIDMap
.ops
= nsnull
;
1884 return NS_ERROR_OUT_OF_MEMORY
;
1887 if (!PL_DHashTableInit(&mObjectMap
, &objmap_DHashTableOps
, (void *)this,
1888 sizeof(nsSharpObjectMapEntry
), PL_DHASH_MIN_SIZE
)) {
1889 mObjectMap
.ops
= nsnull
;
1890 return NS_ERROR_OUT_OF_MEMORY
;
1893 if (!PL_DHashTableInit(&mDocumentMap
, &strmap_DHashTableOps
, (void *)this,
1894 sizeof(nsDocumentMapWriteEntry
),
1895 PL_DHASH_MIN_SIZE
)) {
1896 mDocumentMap
.ops
= nsnull
;
1897 return NS_ERROR_OUT_OF_MEMORY
;
1900 if (!PL_DHashTableInit(&mURIMap
, &objmap_DHashTableOps
, (void *)this,
1901 sizeof(nsURIMapWriteEntry
), PL_DHASH_MIN_SIZE
)) {
1902 mURIMap
.ops
= nsnull
;
1903 return NS_ERROR_OUT_OF_MEMORY
;
1906 if (!PL_DHashTableInit(&mDependencyMap
, &strmap_DHashTableOps
, (void *)this,
1907 sizeof(nsDependencyMapEntry
), PL_DHASH_MIN_SIZE
)) {
1908 mDependencyMap
.ops
= nsnull
;
1909 return NS_ERROR_OUT_OF_MEMORY
;
1916 nsFastLoadFileWriter::Open()
1920 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1921 sizeof(nsFastLoadHeader
));
1929 nsFastLoadFileWriter::Close()
1933 memcpy(mHeader
.mMagic
, magic
, MFL_FILE_MAGIC_SIZE
);
1934 mHeader
.mChecksum
= 0;
1935 mHeader
.mVersion
= MFL_FILE_VERSION
;
1937 PRInt64 footerOffset
;
1938 rv
= mSeekableOutput
->Tell(&footerOffset
);
1940 LL_L2UI(mHeader
.mFooterOffset
, footerOffset
);
1944 // If there is a muxed document segment open, close it now by setting its
1945 // length, stored in the second PRUint32 of the segment.
1946 if (mCurrentDocumentMapEntry
) {
1947 PRUint32 currentSegmentOffset
=
1948 mCurrentDocumentMapEntry
->mCurrentSegmentOffset
;
1949 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1950 currentSegmentOffset
+ 4);
1954 rv
= Write32(mHeader
.mFooterOffset
- currentSegmentOffset
);
1958 // Seek back to the current offset to write the footer.
1959 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
1960 mHeader
.mFooterOffset
);
1964 mCurrentDocumentMapEntry
= nsnull
;
1971 rv
= mSeekableOutput
->Tell(&fileSize
);
1972 LL_L2UI(mHeader
.mFileSize
, fileSize
);
1976 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
, 0);
1980 rv
= WriteHeader(&mHeader
);
1984 // Now compute the checksum, using mFileIO to get an input stream on the
1985 // underlying FastLoad file.
1987 // Get the unbuffered output stream, which flushes the buffered header
1988 // so we can read and checksum it along with the rest of the file, and
1989 // which allows us to write the checksum directly.
1990 nsCOMPtr
<nsIOutputStream
> output
;
1991 rv
= mBufferAccess
->GetUnbufferedStream(getter_AddRefs(output
));
1992 if (NS_FAILED(rv
) || !output
)
1993 return NS_ERROR_UNEXPECTED
;
1995 nsCOMPtr
<nsIInputStream
> input
;
1996 rv
= mFileIO
->GetInputStream(getter_AddRefs(input
));
2000 // Get the unbuffered input stream, to avoid copying overhead and to
2001 // keep our view of the file coherent with the writer -- we don't want
2002 // to hit a stale buffer in the reader's underlying stream.
2003 nsCOMPtr
<nsIStreamBufferAccess
> bufferAccess
=
2004 do_QueryInterface(input
);
2005 rv
= bufferAccess
->GetUnbufferedStream(getter_AddRefs(input
));
2006 if (NS_FAILED(rv
) || !input
)
2007 return NS_ERROR_UNEXPECTED
;
2009 // Seek the input stream to offset 0, in case it's a reader who has
2010 // already been used to consume some of the FastLoad file.
2011 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(input
);
2012 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, 0);
2016 char buf
[MFL_CHECKSUM_BUFSIZE
];
2017 PRUint32 len
, rem
= 0;
2018 PRUint32 checksum
= 0;
2020 // Ok, we're finally ready to checksum the FastLoad file we just wrote!
2021 while (NS_SUCCEEDED(rv
=
2022 input
->Read(buf
+ rem
, sizeof buf
- rem
, &len
)) &&
2025 rem
= NS_AccumulateFastLoadChecksum(&checksum
,
2026 reinterpret_cast<PRUint8
*>
2031 memcpy(buf
, buf
+ len
- rem
, rem
);
2037 NS_AccumulateFastLoadChecksum(&checksum
,
2038 reinterpret_cast<PRUint8
*>(buf
),
2043 // Store the checksum in the FastLoad file header and remember it via
2044 // mHeader.mChecksum, for GetChecksum.
2045 seekable
= do_QueryInterface(output
);
2046 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
,
2047 offsetof(nsFastLoadHeader
, mChecksum
));
2051 mHeader
.mChecksum
= checksum
;
2052 checksum
= NS_SWAP32(checksum
);
2053 PRUint32 bytesWritten
;
2054 rv
= output
->Write(reinterpret_cast<char*>(&checksum
),
2059 if (bytesWritten
!= sizeof checksum
)
2060 return NS_ERROR_FAILURE
;
2063 return mOutputStream
->Close();
2066 // Pseudo-tag used as flag between WriteSingleRefObject and WriteObjectCommon.
2067 #define MFL_SINGLE_REF_PSEUDO_TAG PR_BIT(MFL_OBJECT_TAG_BITS)
2070 nsFastLoadFileWriter::WriteObjectCommon(nsISupports
* aObject
,
2071 PRBool aIsStrongRef
,
2077 NS_ASSERTION((NS_PTR_TO_INT32(aObject
) & MFL_OBJECT_DEF_TAG
) == 0,
2078 "odd nsISupports*, oh no!");
2080 // Here be manual refcounting dragons!
2081 rc
= aObject
->AddRef();
2082 NS_ASSERTION(rc
!= 0, "bad refcnt when writing aObject!");
2085 nsCOMPtr
<nsIClassInfo
> classInfo
;
2087 if (rc
== 2 && (aTags
& MFL_SINGLE_REF_PSEUDO_TAG
)) {
2088 // Dull object: only one strong ref and no weak refs in serialization.
2089 // Conservative: we don't trust the caller if there are more than two
2090 // refs (one from the AddRef above, one from the data structure that's
2091 // being serialized).
2092 oid
= MFL_DULL_OBJECT_OID
;
2095 // Object is presumed to be multiply connected through some combo of
2096 // strong and weak refs. Hold onto it via mObjectMap.
2097 nsSharpObjectMapEntry
* entry
=
2098 static_cast<nsSharpObjectMapEntry
*>
2099 (PL_DHashTableOperate(&mObjectMap
, aObject
,
2103 return NS_ERROR_OUT_OF_MEMORY
;
2106 if (!entry
->mObject
) {
2107 // First time we've seen this object address: add it to mObjectMap
2108 // and serialize the object at the current stream offset.
2110 rv
= Tell(&thisOffset
);
2111 if (NS_FAILED(rv
)) {
2116 // NB: aObject was already held, and mObject is a raw nsISupports*.
2117 entry
->mObject
= aObject
;
2119 oid
= (mObjectMap
.entryCount
<< MFL_OBJECT_TAG_BITS
);
2122 // NB: the (32-bit, fast) CID and object data follow the OID.
2123 entry
->mInfo
.mCIDOffset
= thisOffset
+ sizeof(oid
);
2124 entry
->mInfo
.mStrongRefCnt
= aIsStrongRef
? 1 : 0;
2125 entry
->mInfo
.mWeakRefCnt
= aIsStrongRef
? 0 : 1;
2127 // Record in oid the fact that we're defining this object in the
2128 // stream, and get the object's class info here, so we can take
2129 // note of singletons in order to avoid reserializing them when
2130 // updating after reading.
2131 oid
|= MFL_OBJECT_DEF_TAG
;
2132 classInfo
= do_QueryInterface(aObject
);
2134 NS_NOTREACHED("aObject must implement nsIClassInfo");
2135 return NS_ERROR_FAILURE
;
2139 if (NS_SUCCEEDED(classInfo
->GetFlags(&flags
)) &&
2140 (flags
& nsIClassInfo::SINGLETON
)) {
2141 MFL_SET_SINGLETON_FLAG(&entry
->mInfo
);
2144 // Already serialized, recover oid and update the desired refcnt.
2147 ++entry
->mInfo
.mStrongRefCnt
;
2148 NS_ASSERTION(entry
->mInfo
.mStrongRefCnt
!= 0,
2149 "mStrongRefCnt overflow");
2151 MFL_BUMP_WEAK_REFCNT(&entry
->mInfo
);
2152 NS_ASSERTION(MFL_GET_WEAK_REFCNT(&entry
->mInfo
) != 0,
2153 "mWeakRefCnt overflow");
2161 oid
|= MFL_WEAK_REF_TAG
;
2162 oid
|= (aTags
& MFL_QUERY_INTERFACE_TAG
);
2164 rv
= Write32(oid
^ MFL_OID_XOR_KEY
);
2168 if (oid
& MFL_OBJECT_DEF_TAG
) {
2169 nsCOMPtr
<nsISerializable
> serializable(do_QueryInterface(aObject
));
2170 if (!serializable
) {
2171 NS_NOTREACHED("aObject must implement nsISerializable");
2172 return NS_ERROR_FAILURE
;
2176 rv
= classInfo
->GetClassIDNoAlloc(&slowCID
);
2180 NSFastLoadID fastCID
;
2181 rv
= MapID(slowCID
, &fastCID
);
2185 rv
= WriteFastID(fastCID
);
2189 rv
= serializable
->Write(this);
2198 nsFastLoadFileWriter::WriteObject(nsISupports
* aObject
, PRBool aIsStrongRef
)
2201 nsCOMPtr
<nsISupports
> rootObject(do_QueryInterface(aObject
));
2203 NS_ASSERTION(rootObject
.get() == aObject
,
2204 "bad call to WriteObject -- call WriteCompoundObject!");
2207 return WriteObjectCommon(aObject
, aIsStrongRef
, 0);
2211 nsFastLoadFileWriter::WriteSingleRefObject(nsISupports
* aObject
)
2214 nsCOMPtr
<nsISupports
> rootObject(do_QueryInterface(aObject
));
2216 NS_ASSERTION(rootObject
.get() == aObject
,
2217 "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
2220 return WriteObjectCommon(aObject
, PR_TRUE
, MFL_SINGLE_REF_PSEUDO_TAG
);
2224 nsFastLoadFileWriter::WriteCompoundObject(nsISupports
* aObject
,
2226 PRBool aIsStrongRef
)
2229 nsCOMPtr
<nsISupports
> rootObject(do_QueryInterface(aObject
));
2231 // We could assert that |rootObject != aObject|, but that would prevent
2232 // callers who don't know whether they're dealing with the primary
2233 // nsISupports pointer (e.g., they don't know which implementation of
2234 // nsIURI they have) from using this function.
2237 nsCOMPtr
<nsISupports
> roundtrip
;
2238 rootObject
->QueryInterface(aIID
, getter_AddRefs(roundtrip
));
2239 NS_ASSERTION(roundtrip
.get() == aObject
,
2240 "bad aggregation or multiple inheritance detected by call to "
2241 "WriteCompoundObject!");
2244 rv
= WriteObjectCommon(rootObject
, aIsStrongRef
, MFL_QUERY_INTERFACE_TAG
);
2249 rv
= MapID(aIID
, &iid
);
2253 return WriteFastID(iid
);
2257 nsFastLoadFileWriter::WriteID(const nsID
& aID
)
2260 NSFastLoadID fastID
;
2262 rv
= MapID(aID
, &fastID
);
2266 return WriteFastID(fastID
);
2270 nsFastLoadFileWriter::Seek(PRInt32 aWhence
, PRInt64 aOffset
)
2272 mCurrentDocumentMapEntry
= nsnull
;
2273 return mSeekableOutput
->Seek(aWhence
, aOffset
);
2277 nsFastLoadFileWriter::Tell(PRInt64
*aResult
)
2279 return mSeekableOutput
->Tell(aResult
);
2283 nsFastLoadFileWriter::SetEOF()
2285 return mSeekableOutput
->SetEOF();
2289 nsFastLoadFileWriter::SetOutputStream(nsIOutputStream
*aStream
)
2291 nsresult rv
= nsBinaryOutputStream::SetOutputStream(aStream
);
2292 mSeekableOutput
= do_QueryInterface(mOutputStream
);
2297 NS_NewFastLoadFileWriter(nsIObjectOutputStream
* *aResult
,
2298 nsIOutputStream
* aDestStream
,
2299 nsIFastLoadFileIO
* aFileIO
)
2301 nsFastLoadFileWriter
* writer
=
2302 new nsFastLoadFileWriter(aDestStream
, aFileIO
);
2304 return NS_ERROR_OUT_OF_MEMORY
;
2306 // Stabilize writer's refcnt.
2307 nsCOMPtr
<nsIObjectOutputStream
> stream(writer
);
2309 nsresult rv
= writer
->Open();
2314 NS_ADDREF(*aResult
);
2318 // -------------------------- nsFastLoadFileUpdater --------------------------
2320 NS_IMPL_ISUPPORTS_INHERITED1(nsFastLoadFileUpdater
,
2321 nsFastLoadFileWriter
,
2325 nsFastLoadFileUpdater::GetInputStream(nsIInputStream
** aResult
)
2327 *aResult
= mInputStream
;
2328 NS_IF_ADDREF(*aResult
);
2333 nsFastLoadFileUpdater::GetOutputStream(nsIOutputStream
** aResult
)
2339 PLDHashOperator PR_CALLBACK
2340 nsFastLoadFileUpdater::CopyReadDocumentMapEntryToUpdater(PLDHashTable
*aTable
,
2341 PLDHashEntryHdr
*aHdr
,
2345 nsDocumentMapReadEntry
* readEntry
=
2346 static_cast<nsDocumentMapReadEntry
*>(aHdr
);
2347 nsFastLoadFileUpdater
* updater
=
2348 reinterpret_cast<nsFastLoadFileUpdater
*>(aData
);
2350 void* spec
= nsMemory::Clone(readEntry
->mString
,
2351 strlen(readEntry
->mString
) + 1);
2353 return PL_DHASH_STOP
;
2355 nsDocumentMapWriteEntry
* writeEntry
=
2356 static_cast<nsDocumentMapWriteEntry
*>
2357 (PL_DHashTableOperate(&updater
->mDocumentMap
, spec
,
2360 nsMemory::Free(spec
);
2361 return PL_DHASH_STOP
;
2364 writeEntry
->mString
= reinterpret_cast<const char*>(spec
);
2365 writeEntry
->mURI
= nsnull
;
2366 writeEntry
->mInitialSegmentOffset
= readEntry
->mInitialSegmentOffset
;
2367 writeEntry
->mCurrentSegmentOffset
= 0;
2368 return PL_DHASH_NEXT
;
2372 nsFastLoadFileUpdater::Open(nsFastLoadFileReader
* aReader
)
2375 rv
= nsFastLoadFileWriter::Init();
2381 // Map from dense, zero-based, uint32 NSFastLoadID in reader to 16-byte
2383 nsID
* readIDMap
= aReader
->mFooter
.mIDMap
;
2384 for (i
= 0, n
= aReader
->mFooter
.mNumIDs
; i
< n
; i
++) {
2385 NSFastLoadID fastID
;
2386 rv
= MapID(readIDMap
[i
], &fastID
);
2387 NS_ASSERTION(fastID
== i
+ 1, "huh?");
2392 // Map from reader dense, zero-based MFL_OID_TO_SHARP_INDEX(oid) to sharp
2393 // object offset and refcnt information in updater.
2394 nsFastLoadFileReader::nsObjectMapEntry
* readObjectMap
=
2395 aReader
->mFooter
.mObjectMap
;
2397 // Prepare to save aReader state in case we need to seek back and read a
2398 // singleton object that might otherwise get written by this updater.
2399 nsDocumentMapReadEntry
* saveDocMapEntry
= nsnull
;
2400 nsISeekableStream
* inputSeekable
= nsnull
;
2401 PRInt64 saveOffset
= 0;
2403 for (i
= 0, n
= aReader
->mFooter
.mNumSharpObjects
; i
< n
; i
++) {
2404 nsFastLoadFileReader::nsObjectMapEntry
* readEntry
= &readObjectMap
[i
];
2406 NS_ASSERTION(readEntry
->mCIDOffset
!= 0,
2407 "fastload updater: mCIDOffset cannot be zero!");
2409 // If the reader didn't read this object but it's a singleton, we must
2410 // "deserialize" it now, to discover its one and only root nsISupports
2411 // address. The object already exists in memory if it was created at
2412 // startup without resort to the FastLoad file. The canonical example
2413 // is the system principal object held by all XUL JS scripts.
2415 nsISupports
* obj
= readEntry
->mReadObject
;
2416 if (!obj
&& MFL_GET_SINGLETON_FLAG(readEntry
)) {
2417 if (!saveDocMapEntry
) {
2418 inputSeekable
= aReader
->mSeekableInput
;
2419 rv
= inputSeekable
->Tell(&saveOffset
);
2423 saveDocMapEntry
= aReader
->mCurrentDocumentMapEntry
;
2424 aReader
->mCurrentDocumentMapEntry
= nsnull
;
2427 rv
= inputSeekable
->Seek(nsISeekableStream::NS_SEEK_SET
,
2428 readEntry
->mCIDOffset
);
2433 ->DeserializeObject(getter_AddRefs(readEntry
->mReadObject
));
2436 obj
= readEntry
->mReadObject
;
2438 // Don't forget to set mSkipOffset in case someone calls the reader
2439 // to "deserialize" (yet again) the object we just read.
2441 // Say the singleton is the system principal, and the FastLoad file
2442 // contains data for navigator.xul including scripts and functions.
2443 // If we update the FastLoad file to contain data for messenger.xul
2444 // in a separate session started via mozilla -mail, *and during the
2445 // same FastLoad episode in this session* race to open a navigator
2446 // window, we will attempt to read all objects serialized in the
2447 // navigator.xul portion of the FastLoad file.
2449 // mSkipOffset must be set in such a case so the reader can skip
2450 // the system principal's serialized data, because the updater for
2451 // messenger.xul being opened here has already read it.
2453 rv
= inputSeekable
->Tell(&readEntry
->mSkipOffset
);
2458 NSFastLoadOID oid
= MFL_SHARP_INDEX_TO_OID(i
);
2460 ? reinterpret_cast<void*>(obj
)
2461 : reinterpret_cast<void*>((oid
| MFL_OBJECT_DEF_TAG
));
2463 nsSharpObjectMapEntry
* writeEntry
=
2464 static_cast<nsSharpObjectMapEntry
*>
2465 (PL_DHashTableOperate(&mObjectMap
, key
,
2468 return NS_ERROR_OUT_OF_MEMORY
;
2470 // Hold the object if there is one, so that objmap_ClearEntry can
2471 // release the reference.
2473 writeEntry
->mObject
= reinterpret_cast<nsISupports
*>(key
);
2474 writeEntry
->mOID
= oid
;
2475 writeEntry
->mInfo
.mCIDOffset
= readEntry
->mCIDOffset
;
2476 writeEntry
->mInfo
.mStrongRefCnt
= readEntry
->mSaveStrongRefCnt
;
2477 writeEntry
->mInfo
.mWeakRefCnt
= readEntry
->mSaveWeakRefCnt
;
2480 // If we had to read any singletons, restore aReader's saved state.
2481 if (saveDocMapEntry
) {
2482 rv
= inputSeekable
->Seek(nsISeekableStream::NS_SEEK_SET
, saveOffset
);
2486 aReader
->mCurrentDocumentMapEntry
= saveDocMapEntry
;
2489 // Copy URI spec string and initial segment offset in FastLoad file from
2490 // nsDocumentMapReadEntry in reader to nsDocumentMapWriteEntry in updater.
2491 // If we didn't enumerate all entries, we ran out of memory.
2492 n
= PL_DHashTableEnumerate(&aReader
->mFooter
.mDocumentMap
,
2493 CopyReadDocumentMapEntryToUpdater
,
2495 if (n
!= aReader
->mFooter
.mDocumentMap
.entryCount
)
2496 return NS_ERROR_OUT_OF_MEMORY
;
2498 // Copy source filename dependencies from reader to updater.
2499 nsISupportsArray
* readDeps
= aReader
->mFooter
.mDependencies
;
2500 rv
= readDeps
->Count(&n
);
2504 for (i
= 0; i
< n
; i
++) {
2505 nsCOMPtr
<nsIFile
> file
;
2506 rv
= readDeps
->GetElementAt(i
, getter_AddRefs(file
));
2510 rv
= AddDependency(file
);
2515 // Seek to the reader's footer offset so we overwrite the footer. First,
2516 // update the header to have a zero mFooterOffset, which will invalidate
2517 // the FastLoad file on next startup read attempt, should we crash before
2518 // completing this update.
2519 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
2520 offsetof(nsFastLoadHeader
, mFooterOffset
));
2528 rv
= mSeekableOutput
->Seek(nsISeekableStream::NS_SEEK_SET
,
2529 aReader
->mHeader
.mFooterOffset
);
2533 // Avoid creating yet another object by implementing nsIFastLoadFileIO on
2534 // this updater, and save aReader's input stream so it can be returned by
2535 // GetInputStream called from nsFastLoadFileWriter::Close. This requires
2536 // that we override Close to break the resulting zero-length cycle.
2538 mInputStream
= aReader
->mInputStream
;
2539 mSeekableInput
= aReader
->mSeekableInput
;
2544 nsFastLoadFileUpdater::Close()
2546 // Call base-class Close implementation, which uses mFileIO.
2547 nsresult rv
= nsFastLoadFileWriter::Close();
2549 // Break degenerate cycle from this->mFileIO to this.
2555 NS_NewFastLoadFileUpdater(nsIObjectOutputStream
* *aResult
,
2556 nsIOutputStream
* aOutputStream
,
2557 nsIObjectInputStream
* aReaderAsStream
)
2559 // Make sure that aReaderAsStream is an nsFastLoadFileReader.
2560 nsCOMPtr
<nsIFastLoadFileReader
> reader(do_QueryInterface(aReaderAsStream
));
2562 return NS_ERROR_UNEXPECTED
;
2564 nsFastLoadFileUpdater
* updater
= new nsFastLoadFileUpdater(aOutputStream
);
2566 return NS_ERROR_OUT_OF_MEMORY
;
2568 // Stabilize updater's refcnt.
2569 nsCOMPtr
<nsIObjectOutputStream
> stream(updater
);
2571 nsresult rv
= updater
->Open(static_cast<nsFastLoadFileReader
*>
2577 NS_ADDREF(*aResult
);