Bug 452317 - FeedConverter.js: QueryInterface should throw NS_ERROR_NO_INTERFACE...
[wine-gecko.git] / xpcom / io / nsFastLoadFile.cpp
blob1c62e8f301c1649b176bfd1265c34505839cffac
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
13 * License.
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.
22 * Contributor(s):
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 ***** */
39 #include <string.h>
40 #include "prtypes.h"
41 #include "nscore.h"
42 #include "nsDebug.h"
43 #include "nsEnumeratorUtils.h"
44 #include "nsMemory.h"
45 #include "nsXPIDLString.h"
46 #include "nsString.h"
47 #include "nsReadableUtils.h"
49 #include "nsIComponentManager.h"
50 #include "nsIFile.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"
59 #include "nsInt64.h"
61 #ifdef DEBUG_brendan
62 # define METERING
63 # define DEBUG_MUX
64 #endif
66 #ifdef METERING
67 # define METER(x) x
68 #else
69 # define METER(x) /* nothing */
70 #endif
72 #ifdef DEBUG_MUX
73 # include <stdio.h>
74 # include <stdarg.h>
76 static void trace_mux(char mode, const char *format, ...)
78 va_list ap;
79 static FILE *tfp;
80 if (!tfp) {
81 char tfn[16];
82 sprintf(tfn, "/tmp/mux.%ctrace", mode);
83 tfp = fopen(tfn, "w");
84 if (!tfp)
85 return;
86 setvbuf(tfp, NULL, _IOLBF, 0);
88 va_start(ap, format);
89 vfprintf(tfp, format, ap);
90 va_end(ap);
93 # define TRACE_MUX(args) trace_mux args
94 #else
95 # define TRACE_MUX(args) /* nothing */
96 #endif
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)
107 PRUint32
108 NS_AccumulateFastLoadChecksum(PRUint32 *aChecksum,
109 const PRUint8* aBuffer,
110 PRUint32 aLength,
111 PRBool aLastBuffer)
113 PRUint32 C = *aChecksum;
114 PRUint32 A = C & 0xffff;
115 PRUint32 B = C >> 16;
117 PRUint16 U = 0;
118 if (aLength >= 4) {
119 PRBool odd = PRWord(aBuffer) & 1;
120 switch (PRWord(aBuffer) & 3) {
121 case 3:
122 U = (aBuffer[0] << 8) | aBuffer[1];
123 FLETCHER_ACCUMULATE(A, B, U);
124 U = aBuffer[2];
125 aBuffer += 3;
126 aLength -= 3;
127 break;
129 case 2:
130 U = (aBuffer[0] << 8) | aBuffer[1];
131 FLETCHER_ACCUMULATE(A, B, U);
132 U = 0;
133 aBuffer += 2;
134 aLength -= 2;
135 break;
137 case 1:
138 U = *aBuffer++;
139 aLength--;
140 break;
143 PRUint32 W;
144 if (odd) {
145 while (aLength > 3) {
146 W = *reinterpret_cast<const PRUint32*>(aBuffer);
147 U <<= 8;
148 #ifdef IS_BIG_ENDIAN
149 U |= W >> 24;
150 FLETCHER_ACCUMULATE(A, B, U);
151 U = PRUint16(W >> 8);
152 FLETCHER_ACCUMULATE(A, B, U);
153 U = W & 0xff;
154 #else
155 U |= W & 0xff;
156 FLETCHER_ACCUMULATE(A, B, U);
157 U = PRUint16(W >> 8);
158 U = NS_SWAP16(U);
159 FLETCHER_ACCUMULATE(A, B, U);
160 U = W >> 24;
161 #endif
162 aBuffer += 4;
163 aLength -= 4;
165 aBuffer--; // we're odd, we didn't checksum the last byte
166 aLength++;
167 } else {
168 while (aLength > 3) {
169 W = *reinterpret_cast<const PRUint32*>(aBuffer);
170 #ifdef IS_BIG_ENDIAN
171 U = W >> 16;
172 FLETCHER_ACCUMULATE(A, B, U);
173 U = PRUint16(W);
174 FLETCHER_ACCUMULATE(A, B, U);
175 #else
176 U = NS_SWAP16(W);
177 FLETCHER_ACCUMULATE(A, B, U);
178 U = W >> 16;
179 U = NS_SWAP16(W);
180 FLETCHER_ACCUMULATE(A, B, U);
181 #endif
182 aBuffer += 4;
183 aLength -= 4;
188 if (aLastBuffer) {
189 NS_ASSERTION(aLength <= 4, "aLength botch");
190 switch (aLength) {
191 case 4:
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);
196 break;
198 case 3:
199 U = (aBuffer[0] << 8) | aBuffer[1];
200 FLETCHER_ACCUMULATE(A, B, U);
201 U = aBuffer[2];
202 FLETCHER_ACCUMULATE(A, B, U);
203 break;
205 case 2:
206 U = (aBuffer[0] << 8) | aBuffer[1];
207 FLETCHER_ACCUMULATE(A, B, U);
208 break;
210 case 1:
211 U = aBuffer[0];
212 FLETCHER_ACCUMULATE(A, B, U);
213 break;
216 aLength = 0;
219 while (A >> 16)
220 FOLD_ONES_COMPLEMENT_CARRY(A);
221 while (B >> 16)
222 FOLD_ONES_COMPLEMENT_CARRY(B);
224 *aChecksum = (B << 16) | A;
225 return aLength;
228 PRUint32
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;
238 while (A >> 16)
239 FOLD_ONES_COMPLEMENT_CARRY(A);
241 PRUint32 B = B2;
242 for (PRUint32 n = (sum2ByteCount + 1) / 2; n != 0; n--)
243 ONES_COMPLEMENT_ACCUMULATE(B, B1);
244 while (B >> 16)
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,
263 nsBinaryInputStream,
264 nsIObjectInputStream,
265 nsIFastLoadFileControl,
266 nsIFastLoadReadControl,
267 nsISeekableStream,
268 nsIFastLoadFileReader)
270 nsresult
271 nsFastLoadFileReader::ReadHeader(nsFastLoadHeader *aHeader)
273 nsresult rv;
274 PRUint32 bytesRead;
276 rv = Read(reinterpret_cast<char*>(aHeader), sizeof *aHeader, &bytesRead);
277 if (NS_FAILED(rv))
278 return rv;
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);
290 return NS_OK;
293 // nsIFastLoadFileControl methods:
295 NS_IMETHODIMP
296 nsFastLoadFileReader::GetChecksum(PRUint32 *aChecksum)
298 *aChecksum = mHeader.mChecksum;
299 return NS_OK;
302 NS_IMETHODIMP
303 nsFastLoadFileReader::SetChecksum(PRUint32 aChecksum)
305 mHeader.mChecksum = aChecksum;
306 return NS_OK;
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
326 // mux schedule
329 PR_STATIC_CALLBACK(void)
330 strmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
332 nsStringMapEntry* entry = static_cast<nsStringMapEntry*>(aHdr);
334 if (entry->mString)
335 nsMemory::Free((void*) entry->mString);
336 NS_IF_RELEASE(entry->mURI);
337 PL_DHashClearEntryStub(aTable, aHdr);
340 static const PLDHashTableOps strmap_DHashTableOps = {
341 PL_DHashAllocTable,
342 PL_DHashFreeTable,
343 PL_DHashStringKey,
344 PL_DHashMatchStringKey,
345 PL_DHashMoveEntryStub,
346 strmap_ClearEntry,
347 PL_DHashFinalizeStub,
348 NULL
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
369 // code does this).
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 = {
376 PL_DHashAllocTable,
377 PL_DHashFreeTable,
378 PL_DHashVoidPtrKeyStub,
379 PL_DHashMatchEntryStub,
380 PL_DHashMoveEntryStub,
381 objmap_ClearEntry,
382 PL_DHashFinalizeStub,
383 NULL
386 NS_IMETHODIMP
387 nsFastLoadFileReader::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
389 nsDocumentMapReadEntry* docMapEntry =
390 static_cast<nsDocumentMapReadEntry*>
391 (PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
392 PL_DHASH_LOOKUP));
394 *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
395 return NS_OK;
398 NS_IMETHODIMP
399 nsFastLoadFileReader::StartMuxedDocument(nsISupports* aURI, const char* aURISpec)
401 nsDocumentMapReadEntry* docMapEntry =
402 static_cast<nsDocumentMapReadEntry*>
403 (PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
404 PL_DHASH_LOOKUP));
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,
415 PL_DHASH_ADD));
416 if (!uriMapEntry)
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));
430 return NS_OK;
433 NS_IMETHODIMP
434 nsFastLoadFileReader::SelectMuxedDocument(nsISupports* aURI,
435 nsISupports** aResult)
437 nsresult rv;
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,
444 PL_DHASH_LOOKUP));
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);
460 if (NS_FAILED(rv))
461 return rv;
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
467 // decline.
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
483 // buffer.
484 docMapEntry->mNeedToSeek = PR_TRUE;
487 *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
488 NS_IF_ADDREF(*aResult);
490 mCurrentDocumentMapEntry = docMapEntry;
491 #ifdef DEBUG_MUX
492 PRInt64 currentSegmentOffset;
493 Tell(&currentSegmentOffset);
494 trace_mux('r', "select %p (%p) offset %ld\n",
495 aURI, key.get(), (long) currentSegmentOffset);
496 #endif
497 return NS_OK;
500 NS_IMETHODIMP
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,
507 PL_DHASH_LOOKUP));
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);
523 else
524 PL_DHashTableRawRemove(&mFooter.mURIMap, uriMapEntry);
526 TRACE_MUX(('r', "end %p (%p)\n", aURI, key.get()));
527 return NS_OK;
530 NS_IMETHODIMP
531 nsFastLoadFileReader::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
533 nsresult rv;
535 nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
536 if (entry) {
537 // Don't call our Seek wrapper, as it clears mCurrentDocumentMapEntry.
538 if (entry->mNeedToSeek) {
539 rv = mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
540 entry->mSaveOffset);
541 if (NS_FAILED(rv))
542 return rv;
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);
560 if (NS_FAILED(rv))
561 return rv;
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;
574 if (NS_FAILED(rv))
575 return rv;
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;
588 #ifdef NS_DEBUG
589 // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
590 if (entry->mBytesLeft == 0)
591 entry->mSaveOffset = 0;
592 #endif
594 return rv;
597 NS_IMETHODIMP
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,
607 aResult);
608 if (NS_SUCCEEDED(rv) && entry) {
609 NS_ASSERTION(entry->mBytesLeft >= *aResult,
610 "demux ReadSegments underflow!");
611 entry->mBytesLeft -= *aResult;
613 #ifdef NS_DEBUG
614 // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
615 if (entry->mBytesLeft == 0)
616 entry->mSaveOffset = 0;
617 #endif
619 return rv;
622 NS_IMETHODIMP
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");
629 return rv;
633 * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
635 #define MFL_CHECKSUM_BUFSIZE (6 * 8192)
637 NS_IMETHODIMP
638 nsFastLoadFileReader::ComputeChecksum(PRUint32 *aResult)
640 nsCOMPtr<nsIInputStream> stream = mInputStream;
641 nsCOMPtr<nsISeekableStream> seekable = mSeekableInput;
643 PRInt64 saveOffset;
644 nsresult rv = seekable->Tell(&saveOffset);
645 if (NS_FAILED(rv))
646 return rv;
648 if (mBufferAccess) {
649 rv = mBufferAccess->GetUnbufferedStream(getter_AddRefs(stream));
650 if (NS_FAILED(rv))
651 return rv;
653 seekable = do_QueryInterface(stream);
654 if (!seekable)
655 return NS_ERROR_UNEXPECTED;
658 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
659 if (NS_FAILED(rv))
660 return rv;
662 char buf[MFL_CHECKSUM_BUFSIZE];
663 PRUint32 len, rem;
665 rem = offsetof(nsFastLoadHeader, mChecksum);
666 rv = stream->Read(buf, rem, &len);
667 if (NS_FAILED(rv))
668 return rv;
669 if (len != rem)
670 return NS_ERROR_UNEXPECTED;
672 rv = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, 4);
673 if (NS_FAILED(rv))
674 return rv;
675 memset(buf + rem, 0, 4);
676 rem += 4;
678 PRUint32 checksum = 0;
679 while (NS_SUCCEEDED(rv = stream->Read(buf + rem, sizeof buf - rem, &len)) &&
680 len) {
681 len += rem;
682 rem = NS_AccumulateFastLoadChecksum(&checksum,
683 reinterpret_cast<PRUint8*>(buf),
684 len,
685 PR_FALSE);
686 if (rem)
687 memcpy(buf, buf + len - rem, rem);
689 if (NS_FAILED(rv))
690 return rv;
692 if (rem) {
693 NS_AccumulateFastLoadChecksum(&checksum,
694 reinterpret_cast<PRUint8*>(buf),
695 rem,
696 PR_TRUE);
699 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
700 if (NS_FAILED(rv))
701 return rv;
703 *aResult = checksum;
704 return NS_OK;
707 NS_IMETHODIMP
708 nsFastLoadFileReader::GetDependencies(nsISimpleEnumerator* *aDependencies)
710 return NS_NewArrayEnumerator(aDependencies, mFooter.mDependencies);
713 nsresult
714 nsFastLoadFileReader::ReadFooter(nsFastLoadFooter *aFooter)
716 nsresult rv;
718 rv = ReadFooterPrefix(aFooter);
719 if (NS_FAILED(rv))
720 return rv;
722 aFooter->mIDMap = new nsID[aFooter->mNumIDs];
723 if (!aFooter->mIDMap)
724 return NS_ERROR_OUT_OF_MEMORY;
726 PRUint32 i, n;
727 for (i = 0, n = aFooter->mNumIDs; i < n; i++) {
728 rv = ReadSlowID(&aFooter->mIDMap[i]);
729 if (NS_FAILED(rv))
730 return rv;
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);
741 if (NS_FAILED(rv))
742 return rv;
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);
768 if (NS_FAILED(rv))
769 return rv;
771 nsDocumentMapReadEntry* entry =
772 static_cast<nsDocumentMapReadEntry*>
773 (PL_DHashTableOperate(&aFooter->mDocumentMap,
774 info.mURISpec,
775 PL_DHASH_ADD));
776 if (!entry) {
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));
793 if (NS_FAILED(rv))
794 return rv;
796 nsCAutoString filename;
797 for (i = 0, n = aFooter->mNumDependencies; i < n; i++) {
798 rv = ReadCString(filename);
799 if (NS_FAILED(rv))
800 return rv;
802 PRInt64 fastLoadMtime;
803 rv = Read64(reinterpret_cast<PRUint64*>(&fastLoadMtime));
804 if (NS_FAILED(rv))
805 return rv;
807 nsCOMPtr<nsILocalFile> file;
808 rv = NS_NewNativeLocalFile(filename, PR_TRUE, getter_AddRefs(file));
809 if (NS_FAILED(rv))
810 return rv;
812 PRInt64 currentMtime;
813 rv = file->GetLastModifiedTime(&currentMtime);
814 if (NS_FAILED(rv))
815 return rv;
817 if (LL_NE(fastLoadMtime, currentMtime)) {
818 #ifdef DEBUG
819 nsCAutoString path;
820 file->GetNativePath(path);
821 printf("%s mtime changed, invalidating FastLoad file\n",
822 path.get());
823 #endif
824 return NS_ERROR_FAILURE;
827 rv = readDeps->AppendElement(file);
828 if (NS_FAILED(rv))
829 return rv;
832 aFooter->mDependencies = readDeps;
833 return NS_OK;
836 nsresult
837 nsFastLoadFileReader::ReadFooterPrefix(nsFastLoadFooterPrefix *aFooterPrefix)
839 nsresult rv;
841 rv = Read32(&aFooterPrefix->mNumIDs);
842 if (NS_FAILED(rv))
843 return rv;
845 rv = Read32(&aFooterPrefix->mNumSharpObjects);
846 if (NS_FAILED(rv))
847 return rv;
849 rv = Read32(&aFooterPrefix->mNumMuxedDocuments);
850 if (NS_FAILED(rv))
851 return rv;
853 rv = Read32(&aFooterPrefix->mNumDependencies);
854 if (NS_FAILED(rv))
855 return rv;
857 return NS_OK;
860 nsresult
861 nsFastLoadFileReader::ReadSlowID(nsID *aID)
863 nsresult rv;
865 rv = Read32(&aID->m0);
866 if (NS_FAILED(rv))
867 return rv;
869 rv = Read16(&aID->m1);
870 if (NS_FAILED(rv))
871 return rv;
873 rv = Read16(&aID->m2);
874 if (NS_FAILED(rv))
875 return rv;
877 PRUint32 bytesRead;
878 rv = Read(reinterpret_cast<char*>(aID->m3), sizeof aID->m3, &bytesRead);
879 if (NS_FAILED(rv))
880 return rv;
882 if (bytesRead != sizeof aID->m3)
883 return NS_ERROR_FAILURE;
884 return NS_OK;
887 nsresult
888 nsFastLoadFileReader::ReadFastID(NSFastLoadID *aID)
890 nsresult rv = Read32(aID);
891 if (NS_SUCCEEDED(rv))
892 *aID ^= MFL_ID_XOR_KEY;
893 return rv;
896 nsresult
897 nsFastLoadFileReader::ReadSharpObjectInfo(nsFastLoadSharpObjectInfo *aInfo)
899 nsresult rv;
901 rv = Read32(&aInfo->mCIDOffset);
902 if (NS_FAILED(rv))
903 return rv;
905 NS_ASSERTION(aInfo->mCIDOffset != 0,
906 "fastload reader: mCIDOffset cannot be zero!");
908 rv = Read16(&aInfo->mStrongRefCnt);
909 if (NS_FAILED(rv))
910 return rv;
912 rv = Read16(&aInfo->mWeakRefCnt);
913 if (NS_FAILED(rv))
914 return rv;
916 return NS_OK;
919 nsresult
920 nsFastLoadFileReader::ReadMuxedDocumentInfo(nsFastLoadMuxedDocumentInfo *aInfo)
922 nsresult rv;
924 nsCAutoString spec;
925 rv = ReadCString(spec);
926 if (NS_FAILED(rv))
927 return rv;
929 rv = Read32(&aInfo->mInitialSegmentOffset);
930 if (NS_FAILED(rv))
931 return rv;
933 aInfo->mURISpec = ToNewCString(spec);
934 return NS_OK;
937 nsresult
938 nsFastLoadFileReader::Open()
940 nsresult rv;
942 // Don't bother buffering the header, as we immediately seek to EOF.
943 if (mBufferAccess)
944 mBufferAccess->DisableBuffering();
946 rv = ReadHeader(&mHeader);
948 if (mBufferAccess)
949 mBufferAccess->EnableBuffering();
950 if (NS_FAILED(rv))
951 return rv;
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);
959 if (NS_FAILED(rv))
960 return rv;
962 PRInt64 fileSize;
963 rv = mSeekableInput->Tell(&fileSize);
964 if (NS_FAILED(rv))
965 return rv;
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));
975 if (NS_FAILED(rv))
976 return rv;
978 rv = ReadFooter(&mFooter);
979 if (NS_FAILED(rv))
980 return rv;
982 return mSeekableInput->Seek(nsISeekableStream::NS_SEEK_SET,
983 sizeof(nsFastLoadHeader));
986 NS_IMETHODIMP
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();
1007 nsresult
1008 nsFastLoadFileReader::DeserializeObject(nsISupports* *aObject)
1010 nsresult rv;
1011 NSFastLoadID fastCID;
1013 rv = ReadFastID(&fastCID);
1014 if (NS_FAILED(rv))
1015 return rv;
1017 const nsID& slowCID = mFooter.GetID(fastCID);
1018 nsCOMPtr<nsISupports> object(do_CreateInstance(slowCID, &rv));
1019 if (NS_FAILED(rv))
1020 return rv;
1022 nsCOMPtr<nsISerializable> serializable(do_QueryInterface(object));
1023 if (!serializable)
1024 return NS_ERROR_FAILURE;
1026 rv = serializable->Read(this);
1027 if (NS_FAILED(rv))
1028 return rv;
1030 *aObject = object;
1031 NS_ADDREF(*aObject);
1032 return NS_OK;
1035 nsresult
1036 nsFastLoadFileReader::ReadObject(PRBool aIsStrongRef, nsISupports* *aObject)
1038 nsresult rv;
1039 NSFastLoadOID oid;
1041 rv = Read32(&oid);
1042 if (NS_FAILED(rv))
1043 return rv;
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));
1053 if (NS_FAILED(rv))
1054 return rv;
1055 } else {
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;
1064 if (!object) {
1065 PRInt64 saveOffset;
1066 nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
1068 rv = mSeekableInput->Tell(&saveOffset);
1069 if (NS_FAILED(rv))
1070 return rv;
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,
1089 entry->mCIDOffset);
1090 if (NS_FAILED(rv))
1091 return rv;
1094 rv = DeserializeObject(getter_AddRefs(object));
1095 if (NS_FAILED(rv))
1096 return rv;
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);
1102 if (NS_FAILED(rv))
1103 return rv;
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,
1109 saveOffset);
1110 if (NS_FAILED(rv))
1111 return rv;
1112 mCurrentDocumentMapEntry = saveDocMapEntry;
1115 // Save object until all refs have been deserialized.
1116 entry->mReadObject = object;
1117 } else {
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(&currentOffset);
1130 if (NS_FAILED(rv))
1131 return rv;
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);
1144 if (NS_FAILED(rv))
1145 return rv;
1149 if (aIsStrongRef) {
1150 NS_ASSERTION(entry->mStrongRefCnt != 0,
1151 "mStrongRefCnt underflow!");
1152 --entry->mStrongRefCnt;
1153 } else {
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) {
1164 NSFastLoadID iid;
1165 rv = ReadFastID(&iid);
1166 if (NS_FAILED(rv))
1167 return rv;
1169 rv = object->QueryInterface(mFooter.GetID(iid),
1170 reinterpret_cast<void**>(aObject));
1171 if (NS_FAILED(rv))
1172 return rv;
1173 } else {
1174 *aObject = object;
1175 NS_ADDREF(*aObject);
1178 return NS_OK;
1181 NS_IMETHODIMP
1182 nsFastLoadFileReader::ReadID(nsID *aResult)
1184 nsresult rv;
1185 NSFastLoadID fastID;
1187 rv = ReadFastID(&fastID);
1188 if (NS_FAILED(rv))
1189 return rv;
1191 *aResult = mFooter.GetID(fastID);
1192 return NS_OK;
1195 NS_IMETHODIMP
1196 nsFastLoadFileReader::Seek(PRInt32 aWhence, PRInt64 aOffset)
1198 mCurrentDocumentMapEntry = nsnull;
1199 return mSeekableInput->Seek(aWhence, aOffset);
1202 NS_IMETHODIMP
1203 nsFastLoadFileReader::Tell(PRInt64 *aResult)
1205 return mSeekableInput->Tell(aResult);
1208 NS_IMETHODIMP
1209 nsFastLoadFileReader::SetEOF()
1211 return mSeekableInput->SetEOF();
1214 NS_COM nsresult
1215 NS_NewFastLoadFileReader(nsIObjectInputStream* *aResult,
1216 nsIInputStream* aSrcStream)
1218 nsFastLoadFileReader* reader = new nsFastLoadFileReader(aSrcStream);
1219 if (!reader)
1220 return NS_ERROR_OUT_OF_MEMORY;
1222 // Stabilize reader's refcnt.
1223 nsCOMPtr<nsIObjectInputStream> stream(reader);
1225 nsresult rv = reader->Open();
1226 if (NS_FAILED(rv))
1227 return rv;
1229 *aResult = stream;
1230 NS_ADDREF(*aResult);
1231 return NS_OK;
1234 // -------------------------- nsFastLoadFileWriter --------------------------
1236 NS_IMPL_ISUPPORTS_INHERITED4(nsFastLoadFileWriter,
1237 nsBinaryOutputStream,
1238 nsIObjectOutputStream,
1239 nsIFastLoadFileControl,
1240 nsIFastLoadWriteControl,
1241 nsISeekableStream)
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);
1253 return idp->m0;
1256 PR_STATIC_CALLBACK(PRBool)
1257 idmap_MatchEntry(PLDHashTable *aTable,
1258 const PLDHashEntryHdr *aHdr,
1259 const void *aKey)
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 = {
1268 PL_DHashAllocTable,
1269 PL_DHashFreeTable,
1270 idmap_HashKey,
1271 idmap_MatchEntry,
1272 PL_DHashMoveEntryStub,
1273 PL_DHashClearEntryStub,
1274 PL_DHashFinalizeStub,
1275 NULL
1278 nsresult
1279 nsFastLoadFileWriter::MapID(const nsID& aSlowID, NSFastLoadID *aResult)
1281 nsIDMapEntry* entry =
1282 static_cast<nsIDMapEntry*>
1283 (PL_DHashTableOperate(&mIDMap, &aSlowID, PL_DHASH_ADD));
1284 if (!entry)
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;
1293 return NS_OK;
1296 nsresult
1297 nsFastLoadFileWriter::WriteHeader(nsFastLoadHeader *aHeader)
1299 nsresult rv;
1300 PRUint32 bytesWritten;
1302 rv = Write(aHeader->mMagic, MFL_FILE_MAGIC_SIZE, &bytesWritten);
1303 if (NS_FAILED(rv))
1304 return rv;
1306 if (bytesWritten != MFL_FILE_MAGIC_SIZE)
1307 return NS_ERROR_FAILURE;
1309 rv = Write32(aHeader->mChecksum);
1310 if (NS_FAILED(rv))
1311 return rv;
1313 rv = Write32(aHeader->mVersion);
1314 if (NS_FAILED(rv))
1315 return rv;
1317 rv = Write32(aHeader->mFooterOffset);
1318 if (NS_FAILED(rv))
1319 return rv;
1321 rv = Write32(aHeader->mFileSize);
1322 if (NS_FAILED(rv))
1323 return rv;
1325 return NS_OK;
1328 // nsIFastLoadFileControl methods:
1330 NS_IMETHODIMP
1331 nsFastLoadFileWriter::GetChecksum(PRUint32 *aChecksum)
1333 if (mHeader.mChecksum == 0)
1334 return NS_ERROR_NOT_AVAILABLE;
1335 *aChecksum = mHeader.mChecksum;
1336 return NS_OK;
1339 NS_IMETHODIMP
1340 nsFastLoadFileWriter::SetChecksum(PRUint32 aChecksum)
1342 mHeader.mChecksum = aChecksum;
1343 return NS_OK;
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;
1360 NS_IMETHODIMP
1361 nsFastLoadFileWriter::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
1363 nsDocumentMapWriteEntry* docMapEntry =
1364 static_cast<nsDocumentMapWriteEntry*>
1365 (PL_DHashTableOperate(&mDocumentMap, aURISpec,
1366 PL_DHASH_LOOKUP));
1368 *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
1369 return NS_OK;
1372 NS_IMETHODIMP
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
1381 : nsnull;
1383 nsDocumentMapWriteEntry* docMapEntry =
1384 static_cast<nsDocumentMapWriteEntry*>
1385 (PL_DHashTableOperate(&mDocumentMap, aURISpec,
1386 PL_DHASH_ADD));
1387 if (!docMapEntry)
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,
1395 PL_DHASH_LOOKUP));
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);
1409 if (!spec)
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));
1419 if (!uriMapEntry)
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));
1433 return NS_OK;
1436 NS_IMETHODIMP
1437 nsFastLoadFileWriter::SelectMuxedDocument(nsISupports* aURI,
1438 nsISupports** aResult)
1440 // Capture the current file offset (XXXbe maintain our own via Write?)
1441 nsresult rv;
1442 PRInt64 currentSegmentOffset;
1443 rv = mSeekableOutput->Tell(&currentSegmentOffset);
1444 if (NS_FAILED(rv))
1445 return rv;
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) {
1465 docMapEntry =
1466 static_cast<nsDocumentMapWriteEntry*>
1467 (PL_DHashTableOperate(&mDocumentMap,
1468 uriMapEntry->mURISpec,
1469 PL_DHASH_LOOKUP));
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);
1484 return NS_OK;
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);
1493 if (NS_FAILED(rv))
1494 return rv;
1496 // The length counts all bytes in the segment, including the header
1497 // that contains [nextSegmentOffset, length].
1498 rv = Write32(currentSegmentOffset32 - prevSegmentOffset);
1499 if (NS_FAILED(rv))
1500 return rv;
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);
1508 if (NS_FAILED(rv))
1509 return rv;
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;
1518 } else {
1519 rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
1520 docMapEntry->mCurrentSegmentOffset);
1521 if (NS_FAILED(rv))
1522 return rv;
1524 rv = Write32(currentSegmentOffset32);
1525 if (NS_FAILED(rv))
1526 return rv;
1528 rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
1529 currentSegmentOffset);
1530 if (NS_FAILED(rv))
1531 return rv;
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
1540 if (NS_FAILED(rv))
1541 return rv;
1543 rv = Write32(0); // length placeholder
1544 if (NS_FAILED(rv))
1545 return rv;
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));
1553 return NS_OK;
1556 NS_IMETHODIMP
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);
1581 else
1582 PL_DHashTableRawRemove(&mURIMap, uriMapEntry);
1584 TRACE_MUX(('w', "end %p (%p)\n", aURI, key.get()));
1585 return NS_OK;
1588 struct nsDependencyMapEntry : public nsStringMapEntry {
1589 PRInt64 mLastModified;
1592 NS_IMETHODIMP
1593 nsFastLoadFileWriter::AddDependency(nsIFile* aFile)
1595 nsCAutoString path;
1596 nsresult rv = aFile->GetNativePath(path);
1597 if (NS_FAILED(rv))
1598 return rv;
1600 nsDependencyMapEntry* entry =
1601 static_cast<nsDependencyMapEntry*>
1602 (PL_DHashTableOperate(&mDependencyMap, path.get(),
1603 PL_DHASH_ADD));
1604 if (!entry)
1605 return NS_ERROR_OUT_OF_MEMORY;
1607 if (!entry->mString) {
1608 const char *tmp = ToNewCString(path);
1609 if (!tmp)
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);
1623 rv = NS_OK;
1626 return rv;
1629 nsresult
1630 nsFastLoadFileWriter::WriteFooterPrefix(const nsFastLoadFooterPrefix& aFooterPrefix)
1632 nsresult rv;
1634 rv = Write32(aFooterPrefix.mNumIDs);
1635 if (NS_FAILED(rv))
1636 return rv;
1638 rv = Write32(aFooterPrefix.mNumSharpObjects);
1639 if (NS_FAILED(rv))
1640 return rv;
1642 rv = Write32(aFooterPrefix.mNumMuxedDocuments);
1643 if (NS_FAILED(rv))
1644 return rv;
1646 rv = Write32(aFooterPrefix.mNumDependencies);
1647 if (NS_FAILED(rv))
1648 return rv;
1650 return NS_OK;
1653 nsresult
1654 nsFastLoadFileWriter::WriteSlowID(const nsID& aID)
1656 nsresult rv;
1658 rv = Write32(aID.m0);
1659 if (NS_FAILED(rv))
1660 return rv;
1662 rv = Write16(aID.m1);
1663 if (NS_FAILED(rv))
1664 return rv;
1666 rv = Write16(aID.m2);
1667 if (NS_FAILED(rv))
1668 return rv;
1670 PRUint32 bytesWritten;
1671 rv = Write(reinterpret_cast<const char*>(aID.m3), sizeof aID.m3,
1672 &bytesWritten);
1673 if (NS_FAILED(rv))
1674 return rv;
1676 if (bytesWritten != sizeof aID.m3)
1677 return NS_ERROR_FAILURE;
1678 return NS_OK;
1681 nsresult
1682 nsFastLoadFileWriter::WriteFastID(NSFastLoadID aID)
1684 return Write32(aID ^ MFL_ID_XOR_KEY);
1687 nsresult
1688 nsFastLoadFileWriter::WriteSharpObjectInfo(const nsFastLoadSharpObjectInfo& aInfo)
1690 nsresult rv;
1692 NS_ASSERTION(aInfo.mCIDOffset != 0,
1693 "fastload writer: mCIDOffset cannot be zero!");
1695 rv = Write32(aInfo.mCIDOffset);
1696 if (NS_FAILED(rv))
1697 return rv;
1699 rv = Write16(aInfo.mStrongRefCnt);
1700 if (NS_FAILED(rv))
1701 return rv;
1703 rv = Write16(aInfo.mWeakRefCnt);
1704 if (NS_FAILED(rv))
1705 return rv;
1707 return NS_OK;
1710 nsresult
1711 nsFastLoadFileWriter::WriteMuxedDocumentInfo(const nsFastLoadMuxedDocumentInfo& aInfo)
1713 nsresult rv;
1715 rv = WriteStringZ(aInfo.mURISpec);
1716 if (NS_FAILED(rv))
1717 return rv;
1719 rv = Write32(aInfo.mInitialSegmentOffset);
1720 if (NS_FAILED(rv))
1721 return rv;
1723 return NS_OK;
1726 PLDHashOperator PR_CALLBACK
1727 nsFastLoadFileWriter::IDMapEnumerate(PLDHashTable *aTable,
1728 PLDHashEntryHdr *aHdr,
1729 PRUint32 aNumber,
1730 void *aData)
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 {
1742 NSFastLoadOID mOID;
1743 nsFastLoadSharpObjectInfo mInfo;
1746 PLDHashOperator PR_CALLBACK
1747 nsFastLoadFileWriter::ObjectMapEnumerate(PLDHashTable *aTable,
1748 PLDHashEntryHdr *aHdr,
1749 PRUint32 aNumber,
1750 void *aData)
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
1763 // code does this).
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,
1773 PRUint32 aNumber,
1774 void *aData)
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,
1793 PRUint32 aNumber,
1794 void *aData)
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;
1808 nsresult
1809 nsFastLoadFileWriter::WriteFooter()
1811 nsresult rv;
1812 PRUint32 i, count;
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);
1821 if (NS_FAILED(rv))
1822 return rv;
1824 // Enumerate mIDMap into a vector indexed by mFastID and write it.
1825 nsID* idvec = new nsID[footerPrefix.mNumIDs];
1826 if (!idvec)
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;
1836 delete[] idvec;
1837 if (NS_FAILED(rv))
1838 return rv;
1840 // Enumerate mObjectMap into a vector indexed by mOID and write it.
1841 nsFastLoadSharpObjectInfo* objvec =
1842 new nsFastLoadSharpObjectInfo[footerPrefix.mNumSharpObjects];
1843 if (!objvec)
1844 return NS_ERROR_OUT_OF_MEMORY;
1845 #ifdef NS_DEBUG
1846 memset(objvec, 0, footerPrefix.mNumSharpObjects *
1847 sizeof(nsFastLoadSharpObjectInfo));
1848 #endif
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;
1858 delete[] objvec;
1859 if (NS_FAILED(rv))
1860 return rv;
1862 // Enumerate mDocumentMap, writing nsFastLoadMuxedDocumentInfo records
1863 count = PL_DHashTableEnumerate(&mDocumentMap, DocumentMapEnumerate, &rv);
1864 if (NS_FAILED(rv))
1865 return 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);
1872 if (NS_FAILED(rv))
1873 return rv;
1875 return NS_OK;
1878 nsresult
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;
1912 return NS_OK;
1915 nsresult
1916 nsFastLoadFileWriter::Open()
1918 nsresult rv;
1920 rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
1921 sizeof(nsFastLoadHeader));
1922 if (NS_FAILED(rv))
1923 return rv;
1925 return Init();
1928 NS_IMETHODIMP
1929 nsFastLoadFileWriter::Close()
1931 nsresult rv;
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);
1941 if (NS_FAILED(rv))
1942 return rv;
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);
1951 if (NS_FAILED(rv))
1952 return rv;
1954 rv = Write32(mHeader.mFooterOffset - currentSegmentOffset);
1955 if (NS_FAILED(rv))
1956 return rv;
1958 // Seek back to the current offset to write the footer.
1959 rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
1960 mHeader.mFooterOffset);
1961 if (NS_FAILED(rv))
1962 return rv;
1964 mCurrentDocumentMapEntry = nsnull;
1967 rv = WriteFooter();
1968 if (NS_FAILED(rv))
1969 return rv;
1970 PRInt64 fileSize;
1971 rv = mSeekableOutput->Tell(&fileSize);
1972 LL_L2UI(mHeader.mFileSize, fileSize);
1973 if (NS_FAILED(rv))
1974 return rv;
1976 rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1977 if (NS_FAILED(rv))
1978 return rv;
1980 rv = WriteHeader(&mHeader);
1981 if (NS_FAILED(rv))
1982 return rv;
1984 // Now compute the checksum, using mFileIO to get an input stream on the
1985 // underlying FastLoad file.
1986 if (mFileIO) {
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));
1997 if (NS_FAILED(rv))
1998 return rv;
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);
2013 if (NS_FAILED(rv))
2014 return rv;
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)) &&
2023 len) {
2024 len += rem;
2025 rem = NS_AccumulateFastLoadChecksum(&checksum,
2026 reinterpret_cast<PRUint8*>
2027 (buf),
2028 len,
2029 PR_FALSE);
2030 if (rem)
2031 memcpy(buf, buf + len - rem, rem);
2033 if (NS_FAILED(rv))
2034 return rv;
2036 if (rem) {
2037 NS_AccumulateFastLoadChecksum(&checksum,
2038 reinterpret_cast<PRUint8*>(buf),
2039 rem,
2040 PR_TRUE);
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));
2048 if (NS_FAILED(rv))
2049 return rv;
2051 mHeader.mChecksum = checksum;
2052 checksum = NS_SWAP32(checksum);
2053 PRUint32 bytesWritten;
2054 rv = output->Write(reinterpret_cast<char*>(&checksum),
2055 sizeof checksum,
2056 &bytesWritten);
2057 if (NS_FAILED(rv))
2058 return rv;
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)
2069 nsresult
2070 nsFastLoadFileWriter::WriteObjectCommon(nsISupports* aObject,
2071 PRBool aIsStrongRef,
2072 PRUint32 aTags)
2074 nsrefcnt rc;
2075 nsresult rv;
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!");
2084 NSFastLoadOID oid;
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;
2093 aObject->Release();
2094 } else {
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,
2100 PL_DHASH_ADD));
2101 if (!entry) {
2102 aObject->Release();
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.
2109 PRInt64 thisOffset;
2110 rv = Tell(&thisOffset);
2111 if (NS_FAILED(rv)) {
2112 aObject->Release();
2113 return 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);
2120 entry->mOID = oid;
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);
2133 if (!classInfo) {
2134 NS_NOTREACHED("aObject must implement nsIClassInfo");
2135 return NS_ERROR_FAILURE;
2138 PRUint32 flags;
2139 if (NS_SUCCEEDED(classInfo->GetFlags(&flags)) &&
2140 (flags & nsIClassInfo::SINGLETON)) {
2141 MFL_SET_SINGLETON_FLAG(&entry->mInfo);
2143 } else {
2144 // Already serialized, recover oid and update the desired refcnt.
2145 oid = entry->mOID;
2146 if (aIsStrongRef) {
2147 ++entry->mInfo.mStrongRefCnt;
2148 NS_ASSERTION(entry->mInfo.mStrongRefCnt != 0,
2149 "mStrongRefCnt overflow");
2150 } else {
2151 MFL_BUMP_WEAK_REFCNT(&entry->mInfo);
2152 NS_ASSERTION(MFL_GET_WEAK_REFCNT(&entry->mInfo) != 0,
2153 "mWeakRefCnt overflow");
2156 aObject->Release();
2160 if (!aIsStrongRef)
2161 oid |= MFL_WEAK_REF_TAG;
2162 oid |= (aTags & MFL_QUERY_INTERFACE_TAG);
2164 rv = Write32(oid ^ MFL_OID_XOR_KEY);
2165 if (NS_FAILED(rv))
2166 return rv;
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;
2175 nsCID slowCID;
2176 rv = classInfo->GetClassIDNoAlloc(&slowCID);
2177 if (NS_FAILED(rv))
2178 return rv;
2180 NSFastLoadID fastCID;
2181 rv = MapID(slowCID, &fastCID);
2182 if (NS_FAILED(rv))
2183 return rv;
2185 rv = WriteFastID(fastCID);
2186 if (NS_FAILED(rv))
2187 return rv;
2189 rv = serializable->Write(this);
2190 if (NS_FAILED(rv))
2191 return rv;
2194 return NS_OK;
2197 NS_IMETHODIMP
2198 nsFastLoadFileWriter::WriteObject(nsISupports* aObject, PRBool aIsStrongRef)
2200 #ifdef NS_DEBUG
2201 nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
2203 NS_ASSERTION(rootObject.get() == aObject,
2204 "bad call to WriteObject -- call WriteCompoundObject!");
2205 #endif
2207 return WriteObjectCommon(aObject, aIsStrongRef, 0);
2210 NS_IMETHODIMP
2211 nsFastLoadFileWriter::WriteSingleRefObject(nsISupports* aObject)
2213 #ifdef NS_DEBUG
2214 nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
2216 NS_ASSERTION(rootObject.get() == aObject,
2217 "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
2218 #endif
2220 return WriteObjectCommon(aObject, PR_TRUE, MFL_SINGLE_REF_PSEUDO_TAG);
2223 NS_IMETHODIMP
2224 nsFastLoadFileWriter::WriteCompoundObject(nsISupports* aObject,
2225 const nsIID& aIID,
2226 PRBool aIsStrongRef)
2228 nsresult rv;
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.
2236 #ifdef NS_DEBUG
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!");
2242 #endif
2244 rv = WriteObjectCommon(rootObject, aIsStrongRef, MFL_QUERY_INTERFACE_TAG);
2245 if (NS_FAILED(rv))
2246 return rv;
2248 NSFastLoadID iid;
2249 rv = MapID(aIID, &iid);
2250 if (NS_FAILED(rv))
2251 return rv;
2253 return WriteFastID(iid);
2256 NS_IMETHODIMP
2257 nsFastLoadFileWriter::WriteID(const nsID& aID)
2259 nsresult rv;
2260 NSFastLoadID fastID;
2262 rv = MapID(aID, &fastID);
2263 if (NS_FAILED(rv))
2264 return rv;
2266 return WriteFastID(fastID);
2269 NS_IMETHODIMP
2270 nsFastLoadFileWriter::Seek(PRInt32 aWhence, PRInt64 aOffset)
2272 mCurrentDocumentMapEntry = nsnull;
2273 return mSeekableOutput->Seek(aWhence, aOffset);
2276 NS_IMETHODIMP
2277 nsFastLoadFileWriter::Tell(PRInt64 *aResult)
2279 return mSeekableOutput->Tell(aResult);
2282 NS_IMETHODIMP
2283 nsFastLoadFileWriter::SetEOF()
2285 return mSeekableOutput->SetEOF();
2288 NS_IMETHODIMP
2289 nsFastLoadFileWriter::SetOutputStream(nsIOutputStream *aStream)
2291 nsresult rv = nsBinaryOutputStream::SetOutputStream(aStream);
2292 mSeekableOutput = do_QueryInterface(mOutputStream);
2293 return rv;
2296 NS_COM nsresult
2297 NS_NewFastLoadFileWriter(nsIObjectOutputStream* *aResult,
2298 nsIOutputStream* aDestStream,
2299 nsIFastLoadFileIO* aFileIO)
2301 nsFastLoadFileWriter* writer =
2302 new nsFastLoadFileWriter(aDestStream, aFileIO);
2303 if (!writer)
2304 return NS_ERROR_OUT_OF_MEMORY;
2306 // Stabilize writer's refcnt.
2307 nsCOMPtr<nsIObjectOutputStream> stream(writer);
2309 nsresult rv = writer->Open();
2310 if (NS_FAILED(rv))
2311 return rv;
2313 *aResult = stream;
2314 NS_ADDREF(*aResult);
2315 return NS_OK;
2318 // -------------------------- nsFastLoadFileUpdater --------------------------
2320 NS_IMPL_ISUPPORTS_INHERITED1(nsFastLoadFileUpdater,
2321 nsFastLoadFileWriter,
2322 nsIFastLoadFileIO)
2324 NS_IMETHODIMP
2325 nsFastLoadFileUpdater::GetInputStream(nsIInputStream** aResult)
2327 *aResult = mInputStream;
2328 NS_IF_ADDREF(*aResult);
2329 return NS_OK;
2332 NS_IMETHODIMP
2333 nsFastLoadFileUpdater::GetOutputStream(nsIOutputStream** aResult)
2335 *aResult = nsnull;
2336 return NS_OK;
2339 PLDHashOperator PR_CALLBACK
2340 nsFastLoadFileUpdater::CopyReadDocumentMapEntryToUpdater(PLDHashTable *aTable,
2341 PLDHashEntryHdr *aHdr,
2342 PRUint32 aNumber,
2343 void *aData)
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);
2352 if (!spec)
2353 return PL_DHASH_STOP;
2355 nsDocumentMapWriteEntry* writeEntry =
2356 static_cast<nsDocumentMapWriteEntry*>
2357 (PL_DHashTableOperate(&updater->mDocumentMap, spec,
2358 PL_DHASH_ADD));
2359 if (!writeEntry) {
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;
2371 nsresult
2372 nsFastLoadFileUpdater::Open(nsFastLoadFileReader* aReader)
2374 nsresult rv;
2375 rv = nsFastLoadFileWriter::Init();
2376 if (NS_FAILED(rv))
2377 return rv;
2379 PRUint32 i, n;
2381 // Map from dense, zero-based, uint32 NSFastLoadID in reader to 16-byte
2382 // nsID in updater.
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?");
2388 if (NS_FAILED(rv))
2389 return rv;
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);
2420 if (NS_FAILED(rv))
2421 return rv;
2423 saveDocMapEntry = aReader->mCurrentDocumentMapEntry;
2424 aReader->mCurrentDocumentMapEntry = nsnull;
2427 rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET,
2428 readEntry->mCIDOffset);
2429 if (NS_FAILED(rv))
2430 return rv;
2432 rv = aReader
2433 ->DeserializeObject(getter_AddRefs(readEntry->mReadObject));
2434 if (NS_FAILED(rv))
2435 return rv;
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);
2454 if (NS_FAILED(rv))
2455 return rv;
2458 NSFastLoadOID oid = MFL_SHARP_INDEX_TO_OID(i);
2459 void* key = obj
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,
2466 PL_DHASH_ADD));
2467 if (!writeEntry)
2468 return NS_ERROR_OUT_OF_MEMORY;
2470 // Hold the object if there is one, so that objmap_ClearEntry can
2471 // release the reference.
2472 NS_IF_ADDREF(obj);
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);
2483 if (NS_FAILED(rv))
2484 return rv;
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,
2494 this);
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);
2501 if (NS_FAILED(rv))
2502 return rv;
2504 for (i = 0; i < n; i++) {
2505 nsCOMPtr<nsIFile> file;
2506 rv = readDeps->GetElementAt(i, getter_AddRefs(file));
2507 if (NS_FAILED(rv))
2508 return rv;
2510 rv = AddDependency(file);
2511 if (NS_FAILED(rv))
2512 return rv;
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));
2521 if (NS_FAILED(rv))
2522 return rv;
2524 rv = Write32(0);
2525 if (NS_FAILED(rv))
2526 return rv;
2528 rv = mSeekableOutput->Seek(nsISeekableStream::NS_SEEK_SET,
2529 aReader->mHeader.mFooterOffset);
2530 if (NS_FAILED(rv))
2531 return rv;
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.
2537 mFileIO = this;
2538 mInputStream = aReader->mInputStream;
2539 mSeekableInput = aReader->mSeekableInput;
2540 return NS_OK;
2543 NS_IMETHODIMP
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.
2550 mFileIO = nsnull;
2551 return rv;
2554 NS_COM nsresult
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));
2561 if (!reader)
2562 return NS_ERROR_UNEXPECTED;
2564 nsFastLoadFileUpdater* updater = new nsFastLoadFileUpdater(aOutputStream);
2565 if (!updater)
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*>
2572 (aReaderAsStream));
2573 if (NS_FAILED(rv))
2574 return rv;
2576 *aResult = stream;
2577 NS_ADDREF(*aResult);
2578 return NS_OK;