VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/io/nsFastLoadFile.cpp@ 26286

Last change on this file since 26286 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 82.0 KB
Line 
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
4 *
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/
9 *
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.
14 *
15 * The Original Code is Mozilla FastLoad code.
16 *
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.
21 *
22 * Contributor(s):
23 * Brendan Eich <brendan@mozilla.org> (original author)
24 *
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.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
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"
48
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
56#include "nsBinaryStream.h"
57#include "nsFastLoadFile.h"
58#include "nsInt64.h"
59
60#ifdef DEBUG_brendan
61# define METERING
62# define DEBUG_MUX
63#endif
64
65#ifdef METERING
66# define METER(x) x
67#else
68# define METER(x) /* nothing */
69#endif
70
71#ifdef DEBUG_MUX
72# include <stdio.h>
73# include <stdarg.h>
74
75static void trace_mux(char mode, const char *format, ...)
76{
77 va_list ap;
78 static FILE *tfp;
79 if (!tfp) {
80 char tfn[16];
81 sprintf(tfn, "/tmp/mux.%ctrace", mode);
82 tfp = fopen(tfn, "w");
83 if (!tfp)
84 return;
85 setvbuf(tfp, NULL, _IOLBF, 0);
86 }
87 va_start(ap, format);
88 vfprintf(tfp, format, ap);
89 va_end(ap);
90}
91
92# define TRACE_MUX(args) trace_mux args
93#else
94# define TRACE_MUX(args) /* nothing */
95#endif
96
97/*
98 * Fletcher's 16-bit checksum, using 32-bit two's-complement arithmetic.
99 */
100#define FOLD_ONES_COMPLEMENT_CARRY(X) ((X) = ((X) & 0xffff) + ((X) >> 16))
101#define ONES_COMPLEMENT_ACCUMULATE(X,Y) (X) += (Y); if ((X) & 0x80000000) \
102 FOLD_ONES_COMPLEMENT_CARRY(X)
103#define FLETCHER_ACCUMULATE(A,B,U) ONES_COMPLEMENT_ACCUMULATE(A, U); \
104 ONES_COMPLEMENT_ACCUMULATE(B, A)
105
106PR_IMPLEMENT(PRUint32)
107NS_AccumulateFastLoadChecksum(PRUint32 *aChecksum,
108 const PRUint8* aBuffer,
109 PRUint32 aLength,
110 PRBool aLastBuffer)
111{
112 PRUint32 C = *aChecksum;
113 PRUint32 A = C & 0xffff;
114 PRUint32 B = C >> 16;
115
116 PRUint16 U = 0;
117 if (aLength >= 4) {
118 PRBool odd = PRWord(aBuffer) & 1;
119 switch (PRWord(aBuffer) & 3) {
120 case 3:
121 U = (aBuffer[0] << 8) | aBuffer[1];
122 FLETCHER_ACCUMULATE(A, B, U);
123 U = aBuffer[2];
124 aBuffer += 3;
125 aLength -= 3;
126 break;
127
128 case 2:
129 U = (aBuffer[0] << 8) | aBuffer[1];
130 FLETCHER_ACCUMULATE(A, B, U);
131 U = 0;
132 aBuffer += 2;
133 aLength -= 2;
134 break;
135
136 case 1:
137 U = *aBuffer++;
138 aLength--;
139 break;
140 }
141
142 PRUint32 W;
143 if (odd) {
144 while (aLength > 3) {
145 W = *NS_REINTERPRET_CAST(const PRUint32*, aBuffer);
146 U <<= 8;
147#ifdef IS_BIG_ENDIAN
148 U |= W >> 24;
149 FLETCHER_ACCUMULATE(A, B, U);
150 U = PRUint16(W >> 8);
151 FLETCHER_ACCUMULATE(A, B, U);
152 U = W & 0xff;
153#else
154 U |= W & 0xff;
155 FLETCHER_ACCUMULATE(A, B, U);
156 U = PRUint16(W >> 8);
157 U = NS_SWAP16(U);
158 FLETCHER_ACCUMULATE(A, B, U);
159 U = W >> 24;
160#endif
161 aBuffer += 4;
162 aLength -= 4;
163 }
164 aBuffer--; // we're odd, we didn't checksum the last byte
165 aLength++;
166 } else {
167 while (aLength > 3) {
168 W = *NS_REINTERPRET_CAST(const PRUint32*, aBuffer);
169#ifdef IS_BIG_ENDIAN
170 U = W >> 16;
171 FLETCHER_ACCUMULATE(A, B, U);
172 U = PRUint16(W);
173 FLETCHER_ACCUMULATE(A, B, U);
174#else
175 U = NS_SWAP16(W);
176 FLETCHER_ACCUMULATE(A, B, U);
177 U = W >> 16;
178 U = NS_SWAP16(W);
179 FLETCHER_ACCUMULATE(A, B, U);
180#endif
181 aBuffer += 4;
182 aLength -= 4;
183 }
184 }
185 }
186
187 if (aLastBuffer) {
188 NS_ASSERTION(aLength <= 4, "aLength botch");
189 switch (aLength) {
190 case 4:
191 U = (aBuffer[0] << 8) | aBuffer[1];
192 FLETCHER_ACCUMULATE(A, B, U);
193 U = (aBuffer[2] << 8) | aBuffer[3];
194 FLETCHER_ACCUMULATE(A, B, U);
195 break;
196
197 case 3:
198 U = (aBuffer[0] << 8) | aBuffer[1];
199 FLETCHER_ACCUMULATE(A, B, U);
200 U = aBuffer[2];
201 FLETCHER_ACCUMULATE(A, B, U);
202 break;
203
204 case 2:
205 U = (aBuffer[0] << 8) | aBuffer[1];
206 FLETCHER_ACCUMULATE(A, B, U);
207 break;
208
209 case 1:
210 U = aBuffer[0];
211 FLETCHER_ACCUMULATE(A, B, U);
212 break;
213 }
214
215 aLength = 0;
216 }
217
218 while (A >> 16)
219 FOLD_ONES_COMPLEMENT_CARRY(A);
220 while (B >> 16)
221 FOLD_ONES_COMPLEMENT_CARRY(B);
222
223 *aChecksum = (B << 16) | A;
224 return aLength;
225}
226
227PR_IMPLEMENT(PRUint32)
228NS_AddFastLoadChecksums(PRUint32 sum1, PRUint32 sum2, PRUint32 sum2ByteCount)
229{
230 PRUint32 A1 = sum1 & 0xffff;
231 PRUint32 B1 = sum1 >> 16;
232
233 PRUint32 A2 = sum2 & 0xffff;
234 PRUint32 B2 = sum2 >> 16;
235
236 PRUint32 A = A1 + A2;
237 while (A >> 16)
238 FOLD_ONES_COMPLEMENT_CARRY(A);
239
240 PRUint32 B = B2;
241 for (PRUint32 n = (sum2ByteCount + 1) / 2; n != 0; n--)
242 ONES_COMPLEMENT_ACCUMULATE(B, B1);
243 while (B >> 16)
244 FOLD_ONES_COMPLEMENT_CARRY(B);
245
246 return (B << 16) | A;
247}
248
249#undef FOLD_ONES_COMPLEMENT_CARRY
250#undef ONES_COMPLEMENT_ACCUMULATE
251#undef FLETCHER_ACCUMULATE
252
253static const char magic[] = MFL_FILE_MAGIC;
254
255// -------------------------- nsFastLoadFileReader --------------------------
256
257nsID nsFastLoadFileReader::nsFastLoadFooter::gDummyID;
258nsFastLoadFileReader::nsObjectMapEntry
259 nsFastLoadFileReader::nsFastLoadFooter::gDummySharpObjectEntry;
260
261NS_IMPL_ISUPPORTS_INHERITED5(nsFastLoadFileReader,
262 nsBinaryInputStream,
263 nsIObjectInputStream,
264 nsIFastLoadFileControl,
265 nsIFastLoadReadControl,
266 nsISeekableStream,
267 nsIFastLoadFileReader)
268
269MOZ_DECL_CTOR_COUNTER(nsFastLoadFileReader)
270
271nsresult
272nsFastLoadFileReader::ReadHeader(nsFastLoadHeader *aHeader)
273{
274 nsresult rv;
275 PRUint32 bytesRead;
276
277 rv = Read(NS_REINTERPRET_CAST(char*, aHeader), sizeof *aHeader, &bytesRead);
278 if (NS_FAILED(rv))
279 return rv;
280
281 if (bytesRead != sizeof *aHeader ||
282 memcmp(aHeader->mMagic, magic, MFL_FILE_MAGIC_SIZE)) {
283 return NS_ERROR_UNEXPECTED;
284 }
285
286 aHeader->mChecksum = NS_SWAP32(aHeader->mChecksum);
287 aHeader->mVersion = NS_SWAP32(aHeader->mVersion);
288 aHeader->mFooterOffset = NS_SWAP32(aHeader->mFooterOffset);
289 aHeader->mFileSize = NS_SWAP32(aHeader->mFileSize);
290
291 return NS_OK;
292}
293
294// nsIFastLoadFileControl methods:
295
296NS_IMETHODIMP
297nsFastLoadFileReader::GetChecksum(PRUint32 *aChecksum)
298{
299 *aChecksum = mHeader.mChecksum;
300 return NS_OK;
301}
302
303NS_IMETHODIMP
304nsFastLoadFileReader::SetChecksum(PRUint32 aChecksum)
305{
306 mHeader.mChecksum = aChecksum;
307 return NS_OK;
308}
309
310struct nsStringMapEntry : public PLDHashEntryHdr {
311 const char* mString; // key, must come first
312 nsISupports* mURI; // for SelectMuxedDocument return value
313};
314
315struct nsDocumentMapEntry : public nsStringMapEntry {
316 PRUint32 mInitialSegmentOffset; // offset of URI's first segment in file
317};
318
319struct nsDocumentMapReadEntry : public nsDocumentMapEntry {
320 PRUint32 mNextSegmentOffset; // offset of URI's next segment to read
321 PRUint32 mBytesLeft : 31, // bytes remaining in current segment
322 mNeedToSeek : 1; // flag to defer Seek from Select to
323 // Read, in case there is no Read before
324 // another entry is Selected (to improve
325 // input stream buffer utilization)
326 PRInt64 mSaveOffset; // in case demux schedule differs from
327 // mux schedule
328};
329
330PR_STATIC_CALLBACK(void)
331strmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
332{
333 nsStringMapEntry* entry = NS_STATIC_CAST(nsStringMapEntry*, aHdr);
334
335 if (entry->mString)
336 nsMemory::Free((void*) entry->mString);
337 NS_IF_RELEASE(entry->mURI);
338 PL_DHashClearEntryStub(aTable, aHdr);
339}
340
341static const PLDHashTableOps strmap_DHashTableOps = {
342 PL_DHashAllocTable,
343 PL_DHashFreeTable,
344 PL_DHashGetKeyStub,
345 PL_DHashStringKey,
346 PL_DHashMatchStringKey,
347 PL_DHashMoveEntryStub,
348 strmap_ClearEntry,
349 PL_DHashFinalizeStub,
350 NULL
351};
352
353// An nsObjectMapEntry holds a strong reference to an XPCOM object, unless the
354// mObject member, when cast to NSFastLoadOID, has its MFL_OBJECT_DEF_TAG bit
355// set. NB: we rely on the fact that an nsISupports* is never an odd pointer.
356struct nsObjectMapEntry : public PLDHashEntryHdr {
357 nsISupports* mObject; // key, must come first
358};
359
360// Fast mapping from URI object pointer back to spec-indexed document info.
361struct nsURIMapReadEntry : public nsObjectMapEntry {
362 nsDocumentMapReadEntry* mDocMapEntry;
363};
364
365PR_STATIC_CALLBACK(void)
366objmap_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
367{
368 nsObjectMapEntry* entry = NS_STATIC_CAST(nsObjectMapEntry*, aHdr);
369
370 // Ignore tagged object ids stored as object pointer keys (the updater
371 // code does this).
372 if ((NS_PTR_TO_INT32(entry->mObject) & MFL_OBJECT_DEF_TAG) == 0)
373 NS_IF_RELEASE(entry->mObject);
374 PL_DHashClearEntryStub(aTable, aHdr);
375}
376
377static const PLDHashTableOps objmap_DHashTableOps = {
378 PL_DHashAllocTable,
379 PL_DHashFreeTable,
380 PL_DHashGetKeyStub,
381 PL_DHashVoidPtrKeyStub,
382 PL_DHashMatchEntryStub,
383 PL_DHashMoveEntryStub,
384 objmap_ClearEntry,
385 PL_DHashFinalizeStub,
386 NULL
387};
388
389NS_IMETHODIMP
390nsFastLoadFileReader::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
391{
392 nsDocumentMapReadEntry* docMapEntry =
393 NS_STATIC_CAST(nsDocumentMapReadEntry*,
394 PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
395 PL_DHASH_LOOKUP));
396
397 *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
398 return NS_OK;
399}
400
401NS_IMETHODIMP
402nsFastLoadFileReader::StartMuxedDocument(nsISupports* aURI, const char* aURISpec)
403{
404 nsDocumentMapReadEntry* docMapEntry =
405 NS_STATIC_CAST(nsDocumentMapReadEntry*,
406 PL_DHashTableOperate(&mFooter.mDocumentMap, aURISpec,
407 PL_DHASH_LOOKUP));
408
409 // If the spec isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
410 // FastLoad service can try for a file update.
411 if (PL_DHASH_ENTRY_IS_FREE(docMapEntry))
412 return NS_ERROR_NOT_AVAILABLE;
413
414 nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
415 nsURIMapReadEntry* uriMapEntry =
416 NS_STATIC_CAST(nsURIMapReadEntry*,
417 PL_DHashTableOperate(&mFooter.mURIMap, key,
418 PL_DHASH_ADD));
419 if (!uriMapEntry)
420 return NS_ERROR_OUT_OF_MEMORY;
421
422 NS_ASSERTION(uriMapEntry->mDocMapEntry == nsnull,
423 "URI mapped to two different specs?");
424 if (uriMapEntry->mDocMapEntry)
425 return NS_ERROR_UNEXPECTED;
426
427 docMapEntry->mURI = aURI;
428 NS_ADDREF(docMapEntry->mURI);
429 uriMapEntry->mObject = key;
430 NS_ADDREF(uriMapEntry->mObject);
431 uriMapEntry->mDocMapEntry = docMapEntry;
432 TRACE_MUX(('r', "start %p (%p) %s\n", aURI, key.get(), aURISpec));
433 return NS_OK;
434}
435
436NS_IMETHODIMP
437nsFastLoadFileReader::SelectMuxedDocument(nsISupports* aURI,
438 nsISupports** aResult)
439{
440 nsresult rv;
441
442 // Find the given URI's entry and select it for more reading.
443 nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
444 nsURIMapReadEntry* uriMapEntry =
445 NS_STATIC_CAST(nsURIMapReadEntry*,
446 PL_DHashTableOperate(&mFooter.mURIMap, key,
447 PL_DHASH_LOOKUP));
448
449 // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
450 // FastLoad service can try selecting the file updater.
451 if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
452 return NS_ERROR_NOT_AVAILABLE;
453
454 // If we're interrupting another document's segment, save its offset so
455 // we can seek back when it's reselected. If prevDocMapEntry->mNeedToSeek
456 // is set, that means the stream is not positioned for prevDocMapEntry, to
457 // avoid buffer thrashing. See below in this function for more.
458 nsDocumentMapReadEntry* prevDocMapEntry = mCurrentDocumentMapEntry;
459 if (prevDocMapEntry &&
460 prevDocMapEntry->mBytesLeft &&
461 !prevDocMapEntry->mNeedToSeek) {
462 rv = Tell(&prevDocMapEntry->mSaveOffset);
463 if (NS_FAILED(rv))
464 return rv;
465 }
466
467 // It turns out we get a fair amount of redundant select calls, thanks to
468 // non-blocking hunks of data from the parser that are devoid of scripts.
469 // As more data gets FastLoaded, the number of these useless selects will
470 // decline.
471 nsDocumentMapReadEntry* docMapEntry = uriMapEntry->mDocMapEntry;
472 if (docMapEntry == prevDocMapEntry) {
473 TRACE_MUX(('r', "select prev %s same as current!\n",
474 docMapEntry->mString));
475 }
476
477 // Invariant: docMapEntry->mBytesLeft implies docMapEntry->mSaveOffset has
478 // been set non-zero by the Tell call above.
479 else if (docMapEntry->mBytesLeft) {
480 NS_ASSERTION(docMapEntry->mSaveOffset != 0,
481 "reselecting from multiplex at unsaved offset?");
482
483 // Defer Seek till Read, in case of "ping-pong" Selects without any
484 // intervening Reads, to avoid dumping the underlying mInputStream's
485 // input buffer for cases where alternate "pongs" fall in the same
486 // buffer.
487 docMapEntry->mNeedToSeek = PR_TRUE;
488 }
489
490 *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
491 NS_IF_ADDREF(*aResult);
492
493 mCurrentDocumentMapEntry = docMapEntry;
494#ifdef DEBUG_MUX
495 PRInt64 currentSegmentOffset;
496 Tell(&currentSegmentOffset);
497 trace_mux('r', "select %p (%p) offset %ld\n",
498 aURI, key.get(), (long) currentSegmentOffset);
499#endif
500 return NS_OK;
501}
502
503NS_IMETHODIMP
504nsFastLoadFileReader::EndMuxedDocument(nsISupports* aURI)
505{
506 nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
507 nsURIMapReadEntry* uriMapEntry =
508 NS_STATIC_CAST(nsURIMapReadEntry*,
509 PL_DHashTableOperate(&mFooter.mURIMap, key,
510 PL_DHASH_LOOKUP));
511
512 // If the URI isn't in the map, return NS_ERROR_NOT_AVAILABLE so the
513 // FastLoad service can try to end a select on its file updater.
514 if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
515 return NS_ERROR_NOT_AVAILABLE;
516
517 // Drop our ref to the URI object that was passed to StartMuxedDocument,
518 // we no longer need it, and we do not want to extend its lifetime.
519 if (uriMapEntry->mDocMapEntry)
520 NS_RELEASE(uriMapEntry->mDocMapEntry->mURI);
521
522 // Shrink the table if half the entries are removed sentinels.
523 PRUint32 size = PL_DHASH_TABLE_SIZE(&mFooter.mURIMap);
524 if (mFooter.mURIMap.removedCount >= (size >> 2))
525 PL_DHashTableOperate(&mFooter.mURIMap, key, PL_DHASH_REMOVE);
526 else
527 PL_DHashTableRawRemove(&mFooter.mURIMap, uriMapEntry);
528
529 TRACE_MUX(('r', "end %p (%p)\n", aURI, key.get()));
530 return NS_OK;
531}
532
533NS_IMETHODIMP
534nsFastLoadFileReader::Read(char* aBuffer, PRUint32 aCount, PRUint32 *aBytesRead)
535{
536 nsresult rv;
537
538 nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
539 if (entry) {
540 // Don't call our Seek wrapper, as it clears mCurrentDocumentMapEntry.
541 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
542 if (entry->mNeedToSeek) {
543 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
544 entry->mSaveOffset);
545 if (NS_FAILED(rv))
546 return rv;
547
548 entry->mNeedToSeek = PR_FALSE;
549 }
550
551 // Loop to handle empty segments, which may be generated by the
552 // writer, given Start A; Start B; Select A; Select B; write B data;
553 // multiplexing schedules, which do tend to occur given non-blocking
554 // i/o with LIFO scheduling. XXXbe investigate LIFO issues
555 while (entry->mBytesLeft == 0) {
556 // Check for unexpected end of multiplexed stream.
557 NS_ASSERTION(entry->mNextSegmentOffset != 0,
558 "document demuxed from FastLoad file more than once?");
559 if (entry->mNextSegmentOffset == 0)
560 return NS_ERROR_UNEXPECTED;
561
562 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
563 entry->mNextSegmentOffset);
564 if (NS_FAILED(rv))
565 return rv;
566
567 // Clear mCurrentDocumentMapEntry temporarily to avoid recursion.
568 mCurrentDocumentMapEntry = nsnull;
569
570 rv = Read32(&entry->mNextSegmentOffset);
571 if (NS_SUCCEEDED(rv)) {
572 PRUint32 bytesLeft = 0;
573 rv = Read32(&bytesLeft);
574 entry->mBytesLeft = bytesLeft;
575 }
576
577 mCurrentDocumentMapEntry = entry;
578 if (NS_FAILED(rv))
579 return rv;
580
581 NS_ASSERTION(entry->mBytesLeft >= 8, "demux segment length botch!");
582 entry->mBytesLeft -= 8;
583 }
584 }
585
586 rv = mInputStream->Read(aBuffer, aCount, aBytesRead);
587
588 if (NS_SUCCEEDED(rv) && entry) {
589 NS_ASSERTION(entry->mBytesLeft >= *aBytesRead, "demux Read underflow!");
590 entry->mBytesLeft -= *aBytesRead;
591
592#ifdef NS_DEBUG
593 // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
594 if (entry->mBytesLeft == 0)
595 entry->mSaveOffset = 0;
596#endif
597 }
598 return rv;
599}
600
601NS_IMETHODIMP
602nsFastLoadFileReader::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
603 PRUint32 aCount, PRUint32 *aResult)
604{
605 nsDocumentMapReadEntry* entry = mCurrentDocumentMapEntry;
606
607 NS_ASSERTION(!entry || (!entry->mNeedToSeek && entry->mBytesLeft != 0),
608 "ReadSegments called from above nsFastLoadFileReader layer?!");
609
610 nsresult rv = nsBinaryInputStream::ReadSegments(aWriter, aClosure, aCount,
611 aResult);
612 if (NS_SUCCEEDED(rv) && entry) {
613 NS_ASSERTION(entry->mBytesLeft >= *aResult,
614 "demux ReadSegments underflow!");
615 entry->mBytesLeft -= *aResult;
616
617#ifdef NS_DEBUG
618 // Invariant: !entry->mBytesLeft implies entry->mSaveOffset == 0.
619 if (entry->mBytesLeft == 0)
620 entry->mSaveOffset = 0;
621#endif
622 }
623 return rv;
624}
625
626/**
627 * XXX tuneme
628 */
629#define MFL_CHECKSUM_BUFSIZE 8192
630
631NS_IMETHODIMP
632nsFastLoadFileReader::ComputeChecksum(PRUint32 *aResult)
633{
634 nsCOMPtr<nsIInputStream> stream = mInputStream;
635
636 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(stream));
637 PRInt64 saveOffset;
638 nsresult rv = seekable->Tell(&saveOffset);
639 if (NS_FAILED(rv))
640 return rv;
641
642 nsCOMPtr<nsIStreamBufferAccess> bufferAccess(do_QueryInterface(stream));
643 if (bufferAccess) {
644 rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(stream));
645 if (NS_FAILED(rv))
646 return rv;
647
648 seekable = do_QueryInterface(stream);
649 if (!seekable)
650 return NS_ERROR_UNEXPECTED;
651 }
652
653 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
654 if (NS_FAILED(rv))
655 return rv;
656
657 char buf[MFL_CHECKSUM_BUFSIZE];
658 PRUint32 len, rem;
659
660 rem = offsetof(nsFastLoadHeader, mChecksum);
661 rv = stream->Read(buf, rem, &len);
662 if (NS_FAILED(rv))
663 return rv;
664 if (len != rem)
665 return NS_ERROR_UNEXPECTED;
666
667 rv = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, 4);
668 if (NS_FAILED(rv))
669 return rv;
670 memset(buf + rem, 0, 4);
671 rem += 4;
672
673 PRUint32 checksum = 0;
674 while (NS_SUCCEEDED(rv = stream->Read(buf + rem, sizeof buf - rem, &len)) &&
675 len) {
676 len += rem;
677 rem = NS_AccumulateFastLoadChecksum(&checksum,
678 NS_REINTERPRET_CAST(PRUint8*, buf),
679 len,
680 PR_FALSE);
681 if (rem)
682 memcpy(buf, buf + len - rem, rem);
683 }
684 if (NS_FAILED(rv))
685 return rv;
686
687 if (rem) {
688 NS_AccumulateFastLoadChecksum(&checksum,
689 NS_REINTERPRET_CAST(PRUint8*, buf),
690 rem,
691 PR_TRUE);
692 }
693
694 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
695 if (NS_FAILED(rv))
696 return rv;
697
698 *aResult = checksum;
699 return NS_OK;
700}
701
702NS_IMETHODIMP
703nsFastLoadFileReader::GetDependencies(nsISimpleEnumerator* *aDependencies)
704{
705 return NS_NewArrayEnumerator(aDependencies, mFooter.mDependencies);
706}
707
708nsresult
709nsFastLoadFileReader::ReadFooter(nsFastLoadFooter *aFooter)
710{
711 nsresult rv;
712
713 rv = ReadFooterPrefix(aFooter);
714 if (NS_FAILED(rv))
715 return rv;
716
717 aFooter->mIDMap = new nsID[aFooter->mNumIDs];
718 if (!aFooter->mIDMap)
719 return NS_ERROR_OUT_OF_MEMORY;
720
721 PRUint32 i, n;
722 for (i = 0, n = aFooter->mNumIDs; i < n; i++) {
723 rv = ReadSlowID(&aFooter->mIDMap[i]);
724 if (NS_FAILED(rv))
725 return rv;
726 }
727
728 aFooter->mObjectMap = new nsObjectMapEntry[aFooter->mNumSharpObjects];
729 if (!aFooter->mObjectMap)
730 return NS_ERROR_OUT_OF_MEMORY;
731
732 for (i = 0, n = aFooter->mNumSharpObjects; i < n; i++) {
733 nsObjectMapEntry* entry = &aFooter->mObjectMap[i];
734
735 rv = ReadSharpObjectInfo(entry);
736 if (NS_FAILED(rv))
737 return rv;
738
739 entry->mReadObject = nsnull;
740 entry->mSkipOffset = 0;
741 entry->mSaveStrongRefCnt = entry->mStrongRefCnt;
742 entry->mSaveWeakRefCnt = entry->mWeakRefCnt;
743 }
744
745 if (!PL_DHashTableInit(&aFooter->mDocumentMap, &strmap_DHashTableOps,
746 (void *)this, sizeof(nsDocumentMapReadEntry),
747 aFooter->mNumMuxedDocuments)) {
748 aFooter->mDocumentMap.ops = nsnull;
749 return NS_ERROR_OUT_OF_MEMORY;
750 }
751
752 if (!PL_DHashTableInit(&aFooter->mURIMap, &objmap_DHashTableOps,
753 (void *)this, sizeof(nsURIMapReadEntry),
754 aFooter->mNumMuxedDocuments)) {
755 aFooter->mURIMap.ops = nsnull;
756 return NS_ERROR_OUT_OF_MEMORY;
757 }
758
759 for (i = 0, n = aFooter->mNumMuxedDocuments; i < n; i++) {
760 nsFastLoadMuxedDocumentInfo info;
761
762 rv = ReadMuxedDocumentInfo(&info);
763 if (NS_FAILED(rv))
764 return rv;
765
766 nsDocumentMapReadEntry* entry =
767 NS_STATIC_CAST(nsDocumentMapReadEntry*,
768 PL_DHashTableOperate(&aFooter->mDocumentMap,
769 info.mURISpec,
770 PL_DHASH_ADD));
771 if (!entry) {
772 nsMemory::Free((void*) info.mURISpec);
773 return NS_ERROR_OUT_OF_MEMORY;
774 }
775
776 NS_ASSERTION(!entry->mString, "duplicate URISpec in MuxedDocumentMap");
777 entry->mString = info.mURISpec;
778 entry->mURI = nsnull;
779 entry->mInitialSegmentOffset = info.mInitialSegmentOffset;
780 entry->mNextSegmentOffset = info.mInitialSegmentOffset;
781 entry->mBytesLeft = 0;
782 entry->mNeedToSeek = PR_FALSE;
783 entry->mSaveOffset = 0;
784 }
785
786 nsCOMPtr<nsISupportsArray> readDeps;
787 rv = NS_NewISupportsArray(getter_AddRefs(readDeps));
788 if (NS_FAILED(rv))
789 return rv;
790
791 nsCAutoString filename;
792 for (i = 0, n = aFooter->mNumDependencies; i < n; i++) {
793 rv = ReadCString(filename);
794 if (NS_FAILED(rv))
795 return rv;
796
797 PRInt64 fastLoadMtime;
798 rv = Read64(NS_REINTERPRET_CAST(PRUint64*, &fastLoadMtime));
799 if (NS_FAILED(rv))
800 return rv;
801
802 nsCOMPtr<nsILocalFile> file;
803 rv = NS_NewNativeLocalFile(filename, PR_TRUE, getter_AddRefs(file));
804 if (NS_FAILED(rv))
805 return rv;
806
807 PRInt64 currentMtime;
808 rv = file->GetLastModifiedTime(&currentMtime);
809 if (NS_FAILED(rv))
810 return rv;
811
812 if (LL_NE(fastLoadMtime, currentMtime)) {
813#ifdef DEBUG
814 nsCAutoString path;
815 file->GetNativePath(path);
816 printf("%s mtime changed, invalidating FastLoad file\n",
817 path.get());
818#endif
819 return NS_ERROR_FAILURE;
820 }
821
822 rv = readDeps->AppendElement(file);
823 if (NS_FAILED(rv))
824 return rv;
825 }
826
827 aFooter->mDependencies = readDeps;
828 return NS_OK;
829}
830
831nsresult
832nsFastLoadFileReader::ReadFooterPrefix(nsFastLoadFooterPrefix *aFooterPrefix)
833{
834 nsresult rv;
835
836 rv = Read32(&aFooterPrefix->mNumIDs);
837 if (NS_FAILED(rv))
838 return rv;
839
840 rv = Read32(&aFooterPrefix->mNumSharpObjects);
841 if (NS_FAILED(rv))
842 return rv;
843
844 rv = Read32(&aFooterPrefix->mNumMuxedDocuments);
845 if (NS_FAILED(rv))
846 return rv;
847
848 rv = Read32(&aFooterPrefix->mNumDependencies);
849 if (NS_FAILED(rv))
850 return rv;
851
852 return NS_OK;
853}
854
855nsresult
856nsFastLoadFileReader::ReadSlowID(nsID *aID)
857{
858 nsresult rv;
859
860 rv = Read32(&aID->m0);
861 if (NS_FAILED(rv))
862 return rv;
863
864 rv = Read16(&aID->m1);
865 if (NS_FAILED(rv))
866 return rv;
867
868 rv = Read16(&aID->m2);
869 if (NS_FAILED(rv))
870 return rv;
871
872 PRUint32 bytesRead;
873 rv = Read(NS_REINTERPRET_CAST(char*, aID->m3), sizeof aID->m3, &bytesRead);
874 if (NS_FAILED(rv))
875 return rv;
876
877 if (bytesRead != sizeof aID->m3)
878 return NS_ERROR_FAILURE;
879 return NS_OK;
880}
881
882nsresult
883nsFastLoadFileReader::ReadFastID(NSFastLoadID *aID)
884{
885 nsresult rv = Read32(aID);
886 if (NS_SUCCEEDED(rv))
887 *aID ^= MFL_ID_XOR_KEY;
888 return rv;
889}
890
891nsresult
892nsFastLoadFileReader::ReadSharpObjectInfo(nsFastLoadSharpObjectInfo *aInfo)
893{
894 nsresult rv;
895
896 rv = Read32(&aInfo->mCIDOffset);
897 if (NS_FAILED(rv))
898 return rv;
899
900 NS_ASSERTION(aInfo->mCIDOffset != 0,
901 "fastload reader: mCIDOffset cannot be zero!");
902
903 rv = Read16(&aInfo->mStrongRefCnt);
904 if (NS_FAILED(rv))
905 return rv;
906
907 rv = Read16(&aInfo->mWeakRefCnt);
908 if (NS_FAILED(rv))
909 return rv;
910
911 return NS_OK;
912}
913
914nsresult
915nsFastLoadFileReader::ReadMuxedDocumentInfo(nsFastLoadMuxedDocumentInfo *aInfo)
916{
917 nsresult rv;
918
919 nsCAutoString spec;
920 rv = ReadCString(spec);
921 if (NS_FAILED(rv))
922 return rv;
923
924 rv = Read32(&aInfo->mInitialSegmentOffset);
925 if (NS_FAILED(rv))
926 return rv;
927
928 aInfo->mURISpec = ToNewCString(spec);
929 return NS_OK;
930}
931
932nsresult
933nsFastLoadFileReader::Open()
934{
935 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
936 if (!seekable)
937 return NS_ERROR_UNEXPECTED;
938
939 nsresult rv;
940
941 // Don't bother buffering the header, as we immediately seek to EOF.
942 nsCOMPtr<nsIStreamBufferAccess>
943 bufferAccess(do_QueryInterface(mInputStream));
944 if (bufferAccess)
945 bufferAccess->DisableBuffering();
946
947 rv = ReadHeader(&mHeader);
948
949 if (bufferAccess)
950 bufferAccess->EnableBuffering();
951 if (NS_FAILED(rv))
952 return rv;
953
954 if (mHeader.mVersion != MFL_FILE_VERSION)
955 return NS_ERROR_UNEXPECTED;
956 if (mHeader.mFooterOffset == 0)
957 return NS_ERROR_UNEXPECTED;
958
959 rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
960 if (NS_FAILED(rv))
961 return rv;
962
963 PRInt64 fileSize;
964 rv = seekable->Tell(&fileSize);
965 if (NS_FAILED(rv))
966 return rv;
967
968 nsInt64 fileSize64 = fileSize;
969 const nsInt64 maxUint32 = PR_UINT32_MAX;
970 NS_ASSERTION(fileSize64 <= maxUint32, "fileSize must fit in 32 bits");
971 if ((PRUint32) fileSize64 != mHeader.mFileSize)
972 return NS_ERROR_UNEXPECTED;
973
974 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
975 PRInt32(mHeader.mFooterOffset));
976 if (NS_FAILED(rv))
977 return rv;
978
979 rv = ReadFooter(&mFooter);
980 if (NS_FAILED(rv))
981 return rv;
982
983 return seekable->Seek(nsISeekableStream::NS_SEEK_SET,
984 sizeof(nsFastLoadHeader));
985}
986
987NS_IMETHODIMP
988nsFastLoadFileReader::Close()
989{
990 // Give up our strong "keepalive" references, in case not all objects that
991 // were deserialized were fully re-connected.
992 //
993 // This happens for sure when an nsFastLoadFileUpdater is created and wraps
994 // an nsFastLoadFileReader whose data was already deserialized by an earlier
995 // FastLoad episode. The reader is useful in the second such episode during
996 // a session not so much for reading objects as for its footer information,
997 // which primes the updater's tables so that after the update completes, the
998 // FastLoad file has a superset footer.
999
1000 for (PRUint32 i = 0, n = mFooter.mNumSharpObjects; i < n; i++) {
1001 nsObjectMapEntry* entry = &mFooter.mObjectMap[i];
1002 entry->mReadObject = nsnull;
1003 }
1004
1005 return mInputStream->Close();
1006}
1007
1008nsresult
1009nsFastLoadFileReader::DeserializeObject(nsISupports* *aObject)
1010{
1011 nsresult rv;
1012 NSFastLoadID fastCID;
1013
1014 rv = ReadFastID(&fastCID);
1015 if (NS_FAILED(rv))
1016 return rv;
1017
1018 const nsID& slowCID = mFooter.GetID(fastCID);
1019 nsCOMPtr<nsISupports> object(do_CreateInstance(slowCID, &rv));
1020 if (NS_FAILED(rv))
1021 return rv;
1022
1023 nsCOMPtr<nsISerializable> serializable(do_QueryInterface(object));
1024 if (!serializable)
1025 return NS_ERROR_FAILURE;
1026
1027 rv = serializable->Read(this);
1028 if (NS_FAILED(rv))
1029 return rv;
1030
1031 *aObject = object;
1032 NS_ADDREF(*aObject);
1033 return NS_OK;
1034}
1035
1036nsresult
1037nsFastLoadFileReader::ReadObject(PRBool aIsStrongRef, nsISupports* *aObject)
1038{
1039 nsresult rv;
1040 NSFastLoadOID oid;
1041
1042 rv = Read32(&oid);
1043 if (NS_FAILED(rv))
1044 return rv;
1045 oid ^= MFL_OID_XOR_KEY;
1046
1047 nsCOMPtr<nsISupports> object;
1048
1049 if (oid == MFL_DULL_OBJECT_OID) {
1050 // A very dull object, defined at point of single (strong) reference.
1051 NS_ASSERTION(aIsStrongRef, "dull object read via weak ref!");
1052
1053 rv = DeserializeObject(getter_AddRefs(object));
1054 if (NS_FAILED(rv))
1055 return rv;
1056 } else {
1057 NS_ASSERTION((oid & MFL_WEAK_REF_TAG) ==
1058 (aIsStrongRef ? 0 : MFL_WEAK_REF_TAG),
1059 "strong vs. weak ref deserialization mismatch!");
1060
1061 nsObjectMapEntry* entry = &mFooter.GetSharpObjectEntry(oid);
1062
1063 // Check whether we've already deserialized the object for this OID.
1064 object = entry->mReadObject;
1065 if (!object) {
1066 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
1067 PRInt64 saveOffset;
1068 nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
1069
1070 rv = seekable->Tell(&saveOffset);
1071 if (NS_FAILED(rv))
1072 return rv;
1073
1074 PRUint32 saveOffset32 = saveOffset;
1075 if (entry->mCIDOffset != saveOffset32) {
1076 // We skipped deserialization of this object from its position
1077 // earlier in the input stream, presumably due to the reference
1078 // there being an nsFastLoadPtr, or (more likely) because the
1079 // object was muxed in another document, and deserialization
1080 // order does not match serialization order. So we must seek
1081 // back and read it now.
1082 NS_ASSERTION(entry->mCIDOffset < saveOffset32,
1083 "out of order object?!");
1084
1085 // Ape our Seek wrapper by clearing mCurrentDocumentMapEntry.
1086 // This allows for a skipped object to be referenced from two
1087 // or more multiplexed documents in the FastLoad file.
1088 saveDocMapEntry = mCurrentDocumentMapEntry;
1089 mCurrentDocumentMapEntry = nsnull;
1090 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1091 entry->mCIDOffset);
1092 if (NS_FAILED(rv))
1093 return rv;
1094 }
1095
1096 rv = DeserializeObject(getter_AddRefs(object));
1097 if (NS_FAILED(rv))
1098 return rv;
1099
1100 if (entry->mCIDOffset != saveOffset32) {
1101 // Save the "skip offset" in case we need to skip this object
1102 // definition when reading forward, later on.
1103 rv = seekable->Tell(&entry->mSkipOffset);
1104 if (NS_FAILED(rv))
1105 return rv;
1106
1107 // Restore stream offset and mCurrentDocumentMapEntry in case
1108 // we're still reading forward through a part of the multiplex
1109 // to get object definitions eagerly.
1110 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
1111 if (NS_FAILED(rv))
1112 return rv;
1113 mCurrentDocumentMapEntry = saveDocMapEntry;
1114 }
1115
1116 // Save object until all refs have been deserialized.
1117 entry->mReadObject = object;
1118 } else {
1119 // What if we are at a definition that's already been read? This
1120 // case arises when a sharp object's def is serialized before its
1121 // refs, while a non-defining ref is deserialized before the def.
1122 // We must skip over the object definition.
1123 if (oid & MFL_OBJECT_DEF_TAG) {
1124 NS_ASSERTION(entry->mSkipOffset != 0, "impossible! see above");
1125 nsCOMPtr<nsISeekableStream>
1126 seekable(do_QueryInterface(mInputStream));
1127 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1128 entry->mSkipOffset);
1129 if (NS_FAILED(rv))
1130 return rv;
1131 }
1132 }
1133
1134 if (aIsStrongRef) {
1135 NS_ASSERTION(entry->mStrongRefCnt != 0,
1136 "mStrongRefCnt underflow!");
1137 --entry->mStrongRefCnt;
1138 } else {
1139 NS_ASSERTION(MFL_GET_WEAK_REFCNT(entry) != 0,
1140 "mWeakRefCnt underflow!");
1141 MFL_DROP_WEAK_REFCNT(entry);
1142 }
1143
1144 if (entry->mStrongRefCnt == 0 && MFL_GET_WEAK_REFCNT(entry) == 0)
1145 entry->mReadObject = nsnull;
1146 }
1147
1148 if (oid & MFL_QUERY_INTERFACE_TAG) {
1149 NSFastLoadID iid;
1150 rv = ReadFastID(&iid);
1151 if (NS_FAILED(rv))
1152 return rv;
1153
1154 rv = object->QueryInterface(mFooter.GetID(iid),
1155 NS_REINTERPRET_CAST(void**, aObject));
1156 if (NS_FAILED(rv))
1157 return rv;
1158 } else {
1159 *aObject = object;
1160 NS_ADDREF(*aObject);
1161 }
1162
1163 return NS_OK;
1164}
1165
1166NS_IMETHODIMP
1167nsFastLoadFileReader::ReadID(nsID *aResult)
1168{
1169 nsresult rv;
1170 NSFastLoadID fastID;
1171
1172 rv = ReadFastID(&fastID);
1173 if (NS_FAILED(rv))
1174 return rv;
1175
1176 *aResult = mFooter.GetID(fastID);
1177 return NS_OK;
1178}
1179
1180NS_IMETHODIMP
1181nsFastLoadFileReader::Seek(PRInt32 aWhence, PRInt64 aOffset)
1182{
1183 mCurrentDocumentMapEntry = nsnull;
1184 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
1185 return seekable->Seek(aWhence, aOffset);
1186}
1187
1188NS_IMETHODIMP
1189nsFastLoadFileReader::Tell(PRInt64 *aResult)
1190{
1191 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
1192 return seekable->Tell(aResult);
1193}
1194
1195NS_IMETHODIMP
1196nsFastLoadFileReader::SetEOF()
1197{
1198 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
1199 return seekable->SetEOF();
1200}
1201
1202NS_COM nsresult
1203NS_NewFastLoadFileReader(nsIObjectInputStream* *aResult,
1204 nsIInputStream* aSrcStream)
1205{
1206 nsFastLoadFileReader* reader = new nsFastLoadFileReader(aSrcStream);
1207 if (!reader)
1208 return NS_ERROR_OUT_OF_MEMORY;
1209
1210 // Stabilize reader's refcnt.
1211 nsCOMPtr<nsIObjectInputStream> stream(reader);
1212
1213 nsresult rv = reader->Open();
1214 if (NS_FAILED(rv))
1215 return rv;
1216
1217 *aResult = stream;
1218 NS_ADDREF(*aResult);
1219 return NS_OK;
1220}
1221
1222// -------------------------- nsFastLoadFileWriter --------------------------
1223
1224NS_IMPL_ISUPPORTS_INHERITED4(nsFastLoadFileWriter,
1225 nsBinaryOutputStream,
1226 nsIObjectOutputStream,
1227 nsIFastLoadFileControl,
1228 nsIFastLoadWriteControl,
1229 nsISeekableStream)
1230
1231MOZ_DECL_CTOR_COUNTER(nsFastLoadFileWriter)
1232
1233struct nsIDMapEntry : public PLDHashEntryHdr {
1234 NSFastLoadID mFastID; // 1 + nsFastLoadFooter::mIDMap index
1235 nsID mSlowID; // key, used by PLDHashTableOps below
1236};
1237
1238PR_STATIC_CALLBACK(const void *)
1239idmap_GetKey(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
1240{
1241 nsIDMapEntry* entry = NS_STATIC_CAST(nsIDMapEntry*, aHdr);
1242
1243 return &entry->mSlowID;
1244}
1245
1246PR_STATIC_CALLBACK(PLDHashNumber)
1247idmap_HashKey(PLDHashTable *aTable, const void *aKey)
1248{
1249 const nsID *idp = NS_REINTERPRET_CAST(const nsID*, aKey);
1250
1251 return idp->m0;
1252}
1253
1254PR_STATIC_CALLBACK(PRBool)
1255idmap_MatchEntry(PLDHashTable *aTable,
1256 const PLDHashEntryHdr *aHdr,
1257 const void *aKey)
1258{
1259 const nsIDMapEntry* entry = NS_STATIC_CAST(const nsIDMapEntry*, aHdr);
1260 const nsID *idp = NS_REINTERPRET_CAST(const nsID*, aKey);
1261
1262 return memcmp(&entry->mSlowID, idp, sizeof(nsID)) == 0;
1263}
1264
1265static const PLDHashTableOps idmap_DHashTableOps = {
1266 PL_DHashAllocTable,
1267 PL_DHashFreeTable,
1268 idmap_GetKey,
1269 idmap_HashKey,
1270 idmap_MatchEntry,
1271 PL_DHashMoveEntryStub,
1272 PL_DHashClearEntryStub,
1273 PL_DHashFinalizeStub,
1274 NULL
1275};
1276
1277nsresult
1278nsFastLoadFileWriter::MapID(const nsID& aSlowID, NSFastLoadID *aResult)
1279{
1280 nsIDMapEntry* entry =
1281 NS_STATIC_CAST(nsIDMapEntry*,
1282 PL_DHashTableOperate(&mIDMap, &aSlowID, PL_DHASH_ADD));
1283 if (!entry)
1284 return NS_ERROR_OUT_OF_MEMORY;
1285
1286 if (entry->mFastID == 0) {
1287 entry->mFastID = mIDMap.entryCount;
1288 entry->mSlowID = aSlowID;
1289 }
1290
1291 *aResult = entry->mFastID;
1292 return NS_OK;
1293}
1294
1295nsresult
1296nsFastLoadFileWriter::WriteHeader(nsFastLoadHeader *aHeader)
1297{
1298 nsresult rv;
1299 PRUint32 bytesWritten;
1300
1301 rv = Write(aHeader->mMagic, MFL_FILE_MAGIC_SIZE, &bytesWritten);
1302 if (NS_FAILED(rv))
1303 return rv;
1304
1305 if (bytesWritten != MFL_FILE_MAGIC_SIZE)
1306 return NS_ERROR_FAILURE;
1307
1308 rv = Write32(aHeader->mChecksum);
1309 if (NS_FAILED(rv))
1310 return rv;
1311
1312 rv = Write32(aHeader->mVersion);
1313 if (NS_FAILED(rv))
1314 return rv;
1315
1316 rv = Write32(aHeader->mFooterOffset);
1317 if (NS_FAILED(rv))
1318 return rv;
1319
1320 rv = Write32(aHeader->mFileSize);
1321 if (NS_FAILED(rv))
1322 return rv;
1323
1324 return NS_OK;
1325}
1326
1327// nsIFastLoadFileControl methods:
1328
1329NS_IMETHODIMP
1330nsFastLoadFileWriter::GetChecksum(PRUint32 *aChecksum)
1331{
1332 if (mHeader.mChecksum == 0)
1333 return NS_ERROR_NOT_AVAILABLE;
1334 *aChecksum = mHeader.mChecksum;
1335 return NS_OK;
1336}
1337
1338NS_IMETHODIMP
1339nsFastLoadFileWriter::SetChecksum(PRUint32 aChecksum)
1340{
1341 mHeader.mChecksum = aChecksum;
1342 return NS_OK;
1343}
1344
1345struct nsDocumentMapWriteEntry : public nsDocumentMapEntry {
1346 PRUint32 mCurrentSegmentOffset; // last written segment's offset
1347};
1348
1349// Fast mapping from URI object pointer back to spec-indexed document info.
1350// We also may need the slow mapping from mURISpec to nsDocumentMapWriteEntry,
1351// because the writer's mDocumentMap double hash table may grow "behind the
1352// back of" each mURIMap entry's mDocMapEntry member.
1353struct nsURIMapWriteEntry : public nsObjectMapEntry {
1354 nsDocumentMapWriteEntry* mDocMapEntry;
1355 PRUint32 mGeneration;
1356 const char* mURISpec;
1357};
1358
1359NS_IMETHODIMP
1360nsFastLoadFileWriter::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
1361{
1362 nsDocumentMapWriteEntry* docMapEntry =
1363 NS_STATIC_CAST(nsDocumentMapWriteEntry*,
1364 PL_DHashTableOperate(&mDocumentMap, aURISpec,
1365 PL_DHASH_LOOKUP));
1366
1367 *aResult = PL_DHASH_ENTRY_IS_BUSY(docMapEntry);
1368 return NS_OK;
1369}
1370
1371NS_IMETHODIMP
1372nsFastLoadFileWriter::StartMuxedDocument(nsISupports* aURI,
1373 const char* aURISpec)
1374{
1375 // Save mDocumentMap table generation and mCurrentDocumentMapEntry key in
1376 // case the hash table grows during the PL_DHASH_ADD operation.
1377 PRUint32 saveGeneration = mDocumentMap.generation;
1378 const char* saveURISpec = mCurrentDocumentMapEntry
1379 ? mCurrentDocumentMapEntry->mString
1380 : nsnull;
1381
1382 nsDocumentMapWriteEntry* docMapEntry =
1383 NS_STATIC_CAST(nsDocumentMapWriteEntry*,
1384 PL_DHashTableOperate(&mDocumentMap, aURISpec,
1385 PL_DHASH_ADD));
1386 if (!docMapEntry)
1387 return NS_ERROR_OUT_OF_MEMORY;
1388
1389 // If the generation number changed, refresh mCurrentDocumentMapEntry.
1390 if (mCurrentDocumentMapEntry && mDocumentMap.generation != saveGeneration) {
1391 mCurrentDocumentMapEntry =
1392 NS_STATIC_CAST(nsDocumentMapWriteEntry*,
1393 PL_DHashTableOperate(&mDocumentMap, saveURISpec,
1394 PL_DHASH_LOOKUP));
1395 NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(mCurrentDocumentMapEntry),
1396 "mCurrentDocumentMapEntry lost during table growth?!");
1397
1398 // Refresh saveGeneration for use below when initializing uriMapEntry.
1399 saveGeneration = mDocumentMap.generation;
1400 }
1401
1402 NS_ASSERTION(docMapEntry->mString == nsnull,
1403 "redundant multiplexed document?");
1404 if (docMapEntry->mString)
1405 return NS_ERROR_UNEXPECTED;
1406
1407 void* spec = nsMemory::Clone(aURISpec, strlen(aURISpec) + 1);
1408 if (!spec)
1409 return NS_ERROR_OUT_OF_MEMORY;
1410 docMapEntry->mString = NS_REINTERPRET_CAST(const char*, spec);
1411 docMapEntry->mURI = aURI;
1412 NS_ADDREF(docMapEntry->mURI);
1413
1414 nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
1415 nsURIMapWriteEntry* uriMapEntry =
1416 NS_STATIC_CAST(nsURIMapWriteEntry*,
1417 PL_DHashTableOperate(&mURIMap, key, PL_DHASH_ADD));
1418 if (!uriMapEntry)
1419 return NS_ERROR_OUT_OF_MEMORY;
1420
1421 NS_ASSERTION(uriMapEntry->mDocMapEntry == nsnull,
1422 "URI mapped to two different specs?");
1423 if (uriMapEntry->mDocMapEntry)
1424 return NS_ERROR_UNEXPECTED;
1425
1426 uriMapEntry->mObject = key;
1427 NS_ADDREF(uriMapEntry->mObject);
1428 uriMapEntry->mDocMapEntry = docMapEntry;
1429 uriMapEntry->mGeneration = saveGeneration;
1430 uriMapEntry->mURISpec = NS_REINTERPRET_CAST(const char*, spec);
1431 TRACE_MUX(('w', "start %p (%p) %s\n", aURI, key.get(), aURISpec));
1432 return NS_OK;
1433}
1434
1435NS_IMETHODIMP
1436nsFastLoadFileWriter::SelectMuxedDocument(nsISupports* aURI,
1437 nsISupports** aResult)
1438{
1439 // Avoid repeatedly QI'ing to nsISeekableStream as we tell and seek.
1440 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
1441
1442 // Capture the current file offset (XXXbe maintain our own via Write?)
1443 nsresult rv;
1444 PRInt64 currentSegmentOffset;
1445 rv = seekable->Tell(&currentSegmentOffset);
1446 if (NS_FAILED(rv))
1447 return rv;
1448
1449 PRUint32 currentSegmentOffset32 = currentSegmentOffset;
1450 // Look for an existing entry keyed by aURI, added by StartMuxedDocument.
1451 nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
1452 nsURIMapWriteEntry* uriMapEntry =
1453 NS_STATIC_CAST(nsURIMapWriteEntry*,
1454 PL_DHashTableOperate(&mURIMap, key, PL_DHASH_LOOKUP));
1455 NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(uriMapEntry),
1456 "SelectMuxedDocument without prior StartMuxedDocument?");
1457 if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry))
1458 return NS_ERROR_UNEXPECTED;
1459
1460 // Beware that uriMapEntry->mDocMapEntry may be stale, if an mDocumentMap
1461 // addition caused that table to grow. We save the mDocumentMap generation
1462 // in each uriMapEntry and compare it to the current generation, rehashing
1463 // uriMapEntry->mURISpec if necessary.
1464
1465 nsDocumentMapWriteEntry* docMapEntry = uriMapEntry->mDocMapEntry;
1466 if (uriMapEntry->mGeneration != mDocumentMap.generation) {
1467 docMapEntry =
1468 NS_STATIC_CAST(nsDocumentMapWriteEntry*,
1469 PL_DHashTableOperate(&mDocumentMap,
1470 uriMapEntry->mURISpec,
1471 PL_DHASH_LOOKUP));
1472 NS_ASSERTION(PL_DHASH_ENTRY_IS_BUSY(docMapEntry), "lost mDocMapEntry!?");
1473 uriMapEntry->mDocMapEntry = docMapEntry;
1474 uriMapEntry->mGeneration = mDocumentMap.generation;
1475 }
1476 docMapEntry = uriMapEntry->mDocMapEntry;
1477
1478 // If there is a muxed document segment open, close it now by setting its
1479 // length, stored in the second PRUint32 of the segment.
1480 nsDocumentMapWriteEntry* prevDocMapEntry = mCurrentDocumentMapEntry;
1481 if (prevDocMapEntry) {
1482 if (prevDocMapEntry == docMapEntry) {
1483 TRACE_MUX(('w', "select prev %s same as current!\n",
1484 prevDocMapEntry->mString));
1485 *aResult = docMapEntry->mURI;
1486 NS_ADDREF(*aResult);
1487 return NS_OK;
1488 }
1489
1490 PRUint32 prevSegmentOffset = prevDocMapEntry->mCurrentSegmentOffset;
1491 TRACE_MUX(('w', "select prev %s offset %lu\n",
1492 prevDocMapEntry->mString, prevSegmentOffset));
1493
1494 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1495 prevSegmentOffset + 4);
1496 if (NS_FAILED(rv))
1497 return rv;
1498
1499 // The length counts all bytes in the segment, including the header
1500 // that contains [nextSegmentOffset, length].
1501 rv = Write32(currentSegmentOffset32 - prevSegmentOffset);
1502 if (NS_FAILED(rv))
1503 return rv;
1504
1505 // Seek back to the current offset only if we are not going to seek
1506 // back to *this* entry's last "current" segment offset and write its
1507 // next segment offset at the first PRUint32 of the segment.
1508 if (!docMapEntry->mInitialSegmentOffset) {
1509 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1510 currentSegmentOffset);
1511 if (NS_FAILED(rv))
1512 return rv;
1513 }
1514 }
1515
1516 // If this entry was newly added, set its key and initial segment offset.
1517 // Otherwise, seek back to write the next segment offset of the previous
1518 // segment for this document in the multiplex.
1519 if (!docMapEntry->mInitialSegmentOffset) {
1520 docMapEntry->mInitialSegmentOffset = currentSegmentOffset32;
1521 } else {
1522 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1523 docMapEntry->mCurrentSegmentOffset);
1524 if (NS_FAILED(rv))
1525 return rv;
1526
1527 rv = Write32(currentSegmentOffset32);
1528 if (NS_FAILED(rv))
1529 return rv;
1530
1531 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1532 currentSegmentOffset);
1533 if (NS_FAILED(rv))
1534 return rv;
1535 }
1536
1537 // Update this document's current segment offset so we can later fix its
1538 // next segment offset (unless it is last, in which case we leave the zero
1539 // placeholder as a terminator).
1540 docMapEntry->mCurrentSegmentOffset = currentSegmentOffset32;
1541
1542 rv = Write32(0); // nextSegmentOffset placeholder
1543 if (NS_FAILED(rv))
1544 return rv;
1545
1546 rv = Write32(0); // length placeholder
1547 if (NS_FAILED(rv))
1548 return rv;
1549
1550 *aResult = prevDocMapEntry ? prevDocMapEntry->mURI : nsnull;
1551 NS_IF_ADDREF(*aResult);
1552
1553 mCurrentDocumentMapEntry = docMapEntry;
1554 TRACE_MUX(('w', "select %p (%p) offset %lu\n",
1555 aURI, key.get(), currentSegmentOffset));
1556 return NS_OK;
1557}
1558
1559NS_IMETHODIMP
1560nsFastLoadFileWriter::EndMuxedDocument(nsISupports* aURI)
1561{
1562 nsCOMPtr<nsISupports> key(do_QueryInterface(aURI));
1563 nsURIMapWriteEntry* uriMapEntry =
1564 NS_STATIC_CAST(nsURIMapWriteEntry*,
1565 PL_DHashTableOperate(&mURIMap, key, PL_DHASH_LOOKUP));
1566
1567 // If the URI isn't in the map, nsFastLoadFileWriter::StartMuxedDocument
1568 // must have been called with a redundant URI, *and* its caller must have
1569 // ignored the NS_ERROR_UNEXPECTED it returned in that case.
1570 if (PL_DHASH_ENTRY_IS_FREE(uriMapEntry)) {
1571 TRACE_MUX(('w', "bad end %p (%p)\n", aURI, key.get()));
1572 return NS_ERROR_UNEXPECTED;
1573 }
1574
1575 // Drop our ref to the URI object that was passed to StartMuxedDocument,
1576 // we no longer need it, and we do not want to extend its lifetime.
1577 if (uriMapEntry->mDocMapEntry)
1578 NS_RELEASE(uriMapEntry->mDocMapEntry->mURI);
1579
1580 // Shrink the table if half the entries are removed sentinels.
1581 PRUint32 size = PL_DHASH_TABLE_SIZE(&mURIMap);
1582 if (mURIMap.removedCount >= (size >> 2))
1583 PL_DHashTableOperate(&mURIMap, key, PL_DHASH_REMOVE);
1584 else
1585 PL_DHashTableRawRemove(&mURIMap, uriMapEntry);
1586
1587 TRACE_MUX(('w', "end %p (%p)\n", aURI, key.get()));
1588 return NS_OK;
1589}
1590
1591struct nsDependencyMapEntry : public nsStringMapEntry {
1592 PRInt64 mLastModified;
1593};
1594
1595NS_IMETHODIMP
1596nsFastLoadFileWriter::AddDependency(nsIFile* aFile)
1597{
1598 nsCAutoString path;
1599 nsresult rv = aFile->GetNativePath(path);
1600 if (NS_FAILED(rv))
1601 return rv;
1602
1603 nsDependencyMapEntry* entry =
1604 NS_STATIC_CAST(nsDependencyMapEntry*,
1605 PL_DHashTableOperate(&mDependencyMap, path.get(),
1606 PL_DHASH_ADD));
1607 if (!entry)
1608 return NS_ERROR_OUT_OF_MEMORY;
1609
1610 if (!entry->mString) {
1611 const char *tmp = ToNewCString(path);
1612 if (!tmp)
1613 return NS_ERROR_OUT_OF_MEMORY;
1614 entry->mString = tmp;
1615
1616 // If we can't get the last modified time from aFile, assume it does
1617 // not exist, or is otherwise inaccessible to us (due to permissions),
1618 // remove the dependency, and suppress the failure.
1619 //
1620 // Otherwise, we would end up aborting the fastload process due to a
1621 // missing .js or .xul or other file on every startup.
1622
1623 rv = aFile->GetLastModifiedTime(&entry->mLastModified);
1624 if (NS_FAILED(rv)) {
1625 PL_DHashTableOperate(&mDependencyMap, path.get(), PL_DHASH_REMOVE);
1626 rv = NS_OK;
1627 }
1628 }
1629 return rv;
1630}
1631
1632nsresult
1633nsFastLoadFileWriter::WriteFooterPrefix(const nsFastLoadFooterPrefix& aFooterPrefix)
1634{
1635 nsresult rv;
1636
1637 rv = Write32(aFooterPrefix.mNumIDs);
1638 if (NS_FAILED(rv))
1639 return rv;
1640
1641 rv = Write32(aFooterPrefix.mNumSharpObjects);
1642 if (NS_FAILED(rv))
1643 return rv;
1644
1645 rv = Write32(aFooterPrefix.mNumMuxedDocuments);
1646 if (NS_FAILED(rv))
1647 return rv;
1648
1649 rv = Write32(aFooterPrefix.mNumDependencies);
1650 if (NS_FAILED(rv))
1651 return rv;
1652
1653 return NS_OK;
1654}
1655
1656nsresult
1657nsFastLoadFileWriter::WriteSlowID(const nsID& aID)
1658{
1659 nsresult rv;
1660
1661 rv = Write32(aID.m0);
1662 if (NS_FAILED(rv))
1663 return rv;
1664
1665 rv = Write16(aID.m1);
1666 if (NS_FAILED(rv))
1667 return rv;
1668
1669 rv = Write16(aID.m2);
1670 if (NS_FAILED(rv))
1671 return rv;
1672
1673 PRUint32 bytesWritten;
1674 rv = Write(NS_REINTERPRET_CAST(const char*, aID.m3), sizeof aID.m3,
1675 &bytesWritten);
1676 if (NS_FAILED(rv))
1677 return rv;
1678
1679 if (bytesWritten != sizeof aID.m3)
1680 return NS_ERROR_FAILURE;
1681 return NS_OK;
1682}
1683
1684nsresult
1685nsFastLoadFileWriter::WriteFastID(NSFastLoadID aID)
1686{
1687 return Write32(aID ^ MFL_ID_XOR_KEY);
1688}
1689
1690nsresult
1691nsFastLoadFileWriter::WriteSharpObjectInfo(const nsFastLoadSharpObjectInfo& aInfo)
1692{
1693 nsresult rv;
1694
1695 NS_ASSERTION(aInfo.mCIDOffset != 0,
1696 "fastload writer: mCIDOffset cannot be zero!");
1697
1698 rv = Write32(aInfo.mCIDOffset);
1699 if (NS_FAILED(rv))
1700 return rv;
1701
1702 rv = Write16(aInfo.mStrongRefCnt);
1703 if (NS_FAILED(rv))
1704 return rv;
1705
1706 rv = Write16(aInfo.mWeakRefCnt);
1707 if (NS_FAILED(rv))
1708 return rv;
1709
1710 return NS_OK;
1711}
1712
1713nsresult
1714nsFastLoadFileWriter::WriteMuxedDocumentInfo(const nsFastLoadMuxedDocumentInfo& aInfo)
1715{
1716 nsresult rv;
1717
1718 rv = WriteStringZ(aInfo.mURISpec);
1719 if (NS_FAILED(rv))
1720 return rv;
1721
1722 rv = Write32(aInfo.mInitialSegmentOffset);
1723 if (NS_FAILED(rv))
1724 return rv;
1725
1726 return NS_OK;
1727}
1728
1729PLDHashOperator PR_CALLBACK
1730nsFastLoadFileWriter::IDMapEnumerate(PLDHashTable *aTable,
1731 PLDHashEntryHdr *aHdr,
1732 PRUint32 aNumber,
1733 void *aData)
1734{
1735 nsIDMapEntry* entry = NS_STATIC_CAST(nsIDMapEntry*, aHdr);
1736 PRUint32 index = entry->mFastID - 1;
1737 nsID* vector = NS_REINTERPRET_CAST(nsID*, aData);
1738
1739 NS_ASSERTION(index < aTable->entryCount, "bad nsIDMap index!");
1740 vector[index] = entry->mSlowID;
1741 return PL_DHASH_NEXT;
1742}
1743
1744struct nsSharpObjectMapEntry : public nsObjectMapEntry {
1745 NSFastLoadOID mOID;
1746 nsFastLoadSharpObjectInfo mInfo;
1747};
1748
1749PLDHashOperator PR_CALLBACK
1750nsFastLoadFileWriter::ObjectMapEnumerate(PLDHashTable *aTable,
1751 PLDHashEntryHdr *aHdr,
1752 PRUint32 aNumber,
1753 void *aData)
1754{
1755 nsSharpObjectMapEntry* entry = NS_STATIC_CAST(nsSharpObjectMapEntry*, aHdr);
1756 PRUint32 index = MFL_OID_TO_SHARP_INDEX(entry->mOID);
1757 nsFastLoadSharpObjectInfo* vector =
1758 NS_REINTERPRET_CAST(nsFastLoadSharpObjectInfo*, aData);
1759
1760 NS_ASSERTION(index < aTable->entryCount, "bad nsObjectMap index!");
1761 vector[index] = entry->mInfo;
1762
1763 NS_ASSERTION(entry->mInfo.mStrongRefCnt, "no strong ref in serialization!");
1764
1765 // Ignore tagged object ids stored as object pointer keys (the updater
1766 // code does this).
1767 if ((NS_PTR_TO_INT32(entry->mObject) & MFL_OBJECT_DEF_TAG) == 0)
1768 NS_RELEASE(entry->mObject);
1769
1770 return PL_DHASH_NEXT;
1771}
1772
1773PLDHashOperator PR_CALLBACK
1774nsFastLoadFileWriter::DocumentMapEnumerate(PLDHashTable *aTable,
1775 PLDHashEntryHdr *aHdr,
1776 PRUint32 aNumber,
1777 void *aData)
1778{
1779 nsFastLoadFileWriter* writer =
1780 NS_REINTERPRET_CAST(nsFastLoadFileWriter*, aTable->data);
1781 nsDocumentMapWriteEntry* entry =
1782 NS_STATIC_CAST(nsDocumentMapWriteEntry*, aHdr);
1783 nsresult* rvp = NS_REINTERPRET_CAST(nsresult*, aData);
1784
1785 nsFastLoadMuxedDocumentInfo info;
1786 info.mURISpec = entry->mString;
1787 info.mInitialSegmentOffset = entry->mInitialSegmentOffset;
1788 *rvp = writer->WriteMuxedDocumentInfo(info);
1789
1790 return NS_FAILED(*rvp) ? PL_DHASH_STOP : PL_DHASH_NEXT;
1791}
1792
1793PLDHashOperator PR_CALLBACK
1794nsFastLoadFileWriter::DependencyMapEnumerate(PLDHashTable *aTable,
1795 PLDHashEntryHdr *aHdr,
1796 PRUint32 aNumber,
1797 void *aData)
1798{
1799 nsFastLoadFileWriter* writer =
1800 NS_REINTERPRET_CAST(nsFastLoadFileWriter*, aTable->data);
1801 nsDependencyMapEntry* entry = NS_STATIC_CAST(nsDependencyMapEntry*, aHdr);
1802 nsresult* rvp = NS_REINTERPRET_CAST(nsresult*, aData);
1803
1804 *rvp = writer->WriteStringZ(entry->mString);
1805 if (NS_SUCCEEDED(*rvp))
1806 *rvp = writer->Write64(entry->mLastModified);
1807
1808 return NS_FAILED(*rvp) ? PL_DHASH_STOP :PL_DHASH_NEXT;
1809}
1810
1811nsresult
1812nsFastLoadFileWriter::WriteFooter()
1813{
1814 nsresult rv;
1815 PRUint32 i, count;
1816
1817 nsFastLoadFooterPrefix footerPrefix;
1818 footerPrefix.mNumIDs = mIDMap.entryCount;
1819 footerPrefix.mNumSharpObjects = mObjectMap.entryCount;
1820 footerPrefix.mNumMuxedDocuments = mDocumentMap.entryCount;
1821 footerPrefix.mNumDependencies = mDependencyMap.entryCount;
1822
1823 rv = WriteFooterPrefix(footerPrefix);
1824 if (NS_FAILED(rv))
1825 return rv;
1826
1827 // Enumerate mIDMap into a vector indexed by mFastID and write it.
1828 nsID* idvec = new nsID[footerPrefix.mNumIDs];
1829 if (!idvec)
1830 return NS_ERROR_OUT_OF_MEMORY;
1831
1832 count = PL_DHashTableEnumerate(&mIDMap, IDMapEnumerate, idvec);
1833 NS_ASSERTION(count == footerPrefix.mNumIDs, "bad mIDMap enumeration!");
1834 for (i = 0; i < count; i++) {
1835 rv = WriteSlowID(idvec[i]);
1836 if (NS_FAILED(rv)) break;
1837 }
1838
1839 delete[] idvec;
1840 if (NS_FAILED(rv))
1841 return rv;
1842
1843 // Enumerate mObjectMap into a vector indexed by mOID and write it.
1844 nsFastLoadSharpObjectInfo* objvec =
1845 new nsFastLoadSharpObjectInfo[footerPrefix.mNumSharpObjects];
1846 if (!objvec)
1847 return NS_ERROR_OUT_OF_MEMORY;
1848#ifdef NS_DEBUG
1849 memset(objvec, 0, footerPrefix.mNumSharpObjects *
1850 sizeof(nsFastLoadSharpObjectInfo));
1851#endif
1852
1853 count = PL_DHashTableEnumerate(&mObjectMap, ObjectMapEnumerate, objvec);
1854 NS_ASSERTION(count == footerPrefix.mNumSharpObjects,
1855 "bad mObjectMap enumeration!");
1856 for (i = 0; i < count; i++) {
1857 rv = WriteSharpObjectInfo(objvec[i]);
1858 if (NS_FAILED(rv)) break;
1859 }
1860
1861 delete[] objvec;
1862 if (NS_FAILED(rv))
1863 return rv;
1864
1865 // Enumerate mDocumentMap, writing nsFastLoadMuxedDocumentInfo records
1866 count = PL_DHashTableEnumerate(&mDocumentMap, DocumentMapEnumerate, &rv);
1867 if (NS_FAILED(rv))
1868 return rv;
1869
1870 NS_ASSERTION(count == footerPrefix.mNumMuxedDocuments,
1871 "bad mDocumentMap enumeration!");
1872
1873 // Write out make-like file dependencies.
1874 count = PL_DHashTableEnumerate(&mDependencyMap, DependencyMapEnumerate, &rv);
1875 if (NS_FAILED(rv))
1876 return rv;
1877
1878 return NS_OK;
1879}
1880
1881nsresult
1882nsFastLoadFileWriter::Init()
1883{
1884 if (!PL_DHashTableInit(&mIDMap, &idmap_DHashTableOps, (void *)this,
1885 sizeof(nsIDMapEntry), PL_DHASH_MIN_SIZE)) {
1886 mIDMap.ops = nsnull;
1887 return NS_ERROR_OUT_OF_MEMORY;
1888 }
1889
1890 if (!PL_DHashTableInit(&mObjectMap, &objmap_DHashTableOps, (void *)this,
1891 sizeof(nsSharpObjectMapEntry), PL_DHASH_MIN_SIZE)) {
1892 mObjectMap.ops = nsnull;
1893 return NS_ERROR_OUT_OF_MEMORY;
1894 }
1895
1896 if (!PL_DHashTableInit(&mDocumentMap, &strmap_DHashTableOps, (void *)this,
1897 sizeof(nsDocumentMapWriteEntry),
1898 PL_DHASH_MIN_SIZE)) {
1899 mDocumentMap.ops = nsnull;
1900 return NS_ERROR_OUT_OF_MEMORY;
1901 }
1902
1903 if (!PL_DHashTableInit(&mURIMap, &objmap_DHashTableOps, (void *)this,
1904 sizeof(nsURIMapWriteEntry), PL_DHASH_MIN_SIZE)) {
1905 mURIMap.ops = nsnull;
1906 return NS_ERROR_OUT_OF_MEMORY;
1907 }
1908
1909 if (!PL_DHashTableInit(&mDependencyMap, &strmap_DHashTableOps, (void *)this,
1910 sizeof(nsDependencyMapEntry), PL_DHASH_MIN_SIZE)) {
1911 mDependencyMap.ops = nsnull;
1912 return NS_ERROR_OUT_OF_MEMORY;
1913 }
1914
1915 return NS_OK;
1916}
1917
1918nsresult
1919nsFastLoadFileWriter::Open()
1920{
1921 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
1922 if (!seekable)
1923 return NS_ERROR_UNEXPECTED;
1924
1925 nsresult rv;
1926
1927 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1928 sizeof(nsFastLoadHeader));
1929 if (NS_FAILED(rv))
1930 return rv;
1931
1932 return Init();
1933}
1934
1935NS_IMETHODIMP
1936nsFastLoadFileWriter::Close()
1937{
1938 nsresult rv;
1939
1940 memcpy(mHeader.mMagic, magic, MFL_FILE_MAGIC_SIZE);
1941 mHeader.mChecksum = 0;
1942 mHeader.mVersion = MFL_FILE_VERSION;
1943
1944 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
1945
1946 PRInt64 footerOffset;
1947 rv = seekable->Tell(&footerOffset);
1948
1949 LL_L2UI(mHeader.mFooterOffset, footerOffset);
1950 if (NS_FAILED(rv))
1951 return rv;
1952
1953 // If there is a muxed document segment open, close it now by setting its
1954 // length, stored in the second PRUint32 of the segment.
1955 if (mCurrentDocumentMapEntry) {
1956 PRUint32 currentSegmentOffset =
1957 mCurrentDocumentMapEntry->mCurrentSegmentOffset;
1958 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1959 currentSegmentOffset + 4);
1960 if (NS_FAILED(rv))
1961 return rv;
1962
1963 rv = Write32(mHeader.mFooterOffset - currentSegmentOffset);
1964 if (NS_FAILED(rv))
1965 return rv;
1966
1967 // Seek back to the current offset to write the footer.
1968 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
1969 mHeader.mFooterOffset);
1970 if (NS_FAILED(rv))
1971 return rv;
1972
1973 mCurrentDocumentMapEntry = nsnull;
1974 }
1975
1976 rv = WriteFooter();
1977 if (NS_FAILED(rv))
1978 return rv;
1979 PRInt64 fileSize;
1980 rv = seekable->Tell(&fileSize);
1981 LL_L2UI(mHeader.mFileSize, fileSize);
1982 if (NS_FAILED(rv))
1983 return rv;
1984
1985 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1986 if (NS_FAILED(rv))
1987 return rv;
1988
1989 rv = WriteHeader(&mHeader);
1990 if (NS_FAILED(rv))
1991 return rv;
1992
1993 // Now compute the checksum, using mFileIO to get an input stream on the
1994 // underlying FastLoad file.
1995 if (mFileIO) {
1996 // Get the unbuffered output stream, which flushes the buffered header
1997 // so we can read and checksum it along with the rest of the file, and
1998 // which allows us to write the checksum directly.
1999 nsCOMPtr<nsIStreamBufferAccess>
2000 bufferAccess(do_QueryInterface(mOutputStream));
2001 nsCOMPtr<nsIOutputStream> output;
2002 rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(output));
2003 if (NS_FAILED(rv) || !output)
2004 return NS_ERROR_UNEXPECTED;
2005
2006 nsCOMPtr<nsIInputStream> input;
2007 rv = mFileIO->GetInputStream(getter_AddRefs(input));
2008 if (NS_FAILED(rv))
2009 return rv;
2010
2011 // Get the unbuffered input stream, to avoid copying overhead and to
2012 // keep our view of the file coherent with the writer -- we don't want
2013 // to hit a stale buffer in the reader's underlying stream.
2014 bufferAccess = do_QueryInterface(input);
2015 rv = bufferAccess->GetUnbufferedStream(getter_AddRefs(input));
2016 if (NS_FAILED(rv) || !input)
2017 return NS_ERROR_UNEXPECTED;
2018
2019 // Seek the input stream to offset 0, in case it's a reader who has
2020 // already been used to consume some of the FastLoad file.
2021 seekable = do_QueryInterface(input);
2022 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
2023 if (NS_FAILED(rv))
2024 return rv;
2025
2026 char buf[MFL_CHECKSUM_BUFSIZE];
2027 PRUint32 len, rem = 0;
2028 PRUint32 checksum = 0;
2029
2030 // Ok, we're finally ready to checksum the FastLoad file we just wrote!
2031 while (NS_SUCCEEDED(rv =
2032 input->Read(buf + rem, sizeof buf - rem, &len)) &&
2033 len) {
2034 len += rem;
2035 rem = NS_AccumulateFastLoadChecksum(&checksum,
2036 NS_REINTERPRET_CAST(PRUint8*,
2037 buf),
2038 len,
2039 PR_FALSE);
2040 if (rem)
2041 memcpy(buf, buf + len - rem, rem);
2042 }
2043 if (NS_FAILED(rv))
2044 return rv;
2045
2046 if (rem) {
2047 NS_AccumulateFastLoadChecksum(&checksum,
2048 NS_REINTERPRET_CAST(PRUint8*, buf),
2049 rem,
2050 PR_TRUE);
2051 }
2052
2053 // Store the checksum in the FastLoad file header and remember it via
2054 // mHeader.mChecksum, for GetChecksum.
2055 seekable = do_QueryInterface(output);
2056 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
2057 offsetof(nsFastLoadHeader, mChecksum));
2058 if (NS_FAILED(rv))
2059 return rv;
2060
2061 mHeader.mChecksum = checksum;
2062 checksum = NS_SWAP32(checksum);
2063 PRUint32 bytesWritten;
2064 rv = output->Write(NS_REINTERPRET_CAST(char*, &checksum),
2065 sizeof checksum,
2066 &bytesWritten);
2067 if (NS_FAILED(rv))
2068 return rv;
2069 if (bytesWritten != sizeof checksum)
2070 return NS_ERROR_FAILURE;
2071 }
2072
2073 return mOutputStream->Close();
2074}
2075
2076// Psuedo-tag used as flag between WriteSingleRefObject and WriteObjectCommon.
2077#define MFL_SINGLE_REF_PSEUDO_TAG PR_BIT(MFL_OBJECT_TAG_BITS)
2078
2079nsresult
2080nsFastLoadFileWriter::WriteObjectCommon(nsISupports* aObject,
2081 PRBool aIsStrongRef,
2082 PRUint32 aTags)
2083{
2084 nsrefcnt rc;
2085 nsresult rv;
2086
2087 NS_ASSERTION((NS_PTR_TO_INT32(aObject) & MFL_OBJECT_DEF_TAG) == 0,
2088 "odd nsISupports*, oh no!");
2089
2090 // Here be manual refcounting dragons!
2091 rc = aObject->AddRef();
2092 NS_ASSERTION(rc != 0, "bad refcnt when writing aObject!");
2093
2094 NSFastLoadOID oid;
2095 nsCOMPtr<nsIClassInfo> classInfo;
2096
2097 if (rc == 2 && (aTags & MFL_SINGLE_REF_PSEUDO_TAG)) {
2098 // Dull object: only one strong ref and no weak refs in serialization.
2099 // Conservative: we don't trust the caller if there are more than two
2100 // refs (one from the AddRef above, one from the data structure that's
2101 // being serialized).
2102 oid = MFL_DULL_OBJECT_OID;
2103 aObject->Release();
2104 } else {
2105 // Object is presumed to be multiply connected through some combo of
2106 // strong and weak refs. Hold onto it via mObjectMap.
2107 nsSharpObjectMapEntry* entry =
2108 NS_STATIC_CAST(nsSharpObjectMapEntry*,
2109 PL_DHashTableOperate(&mObjectMap, aObject,
2110 PL_DHASH_ADD));
2111 if (!entry) {
2112 aObject->Release();
2113 return NS_ERROR_OUT_OF_MEMORY;
2114 }
2115
2116 if (!entry->mObject) {
2117 // First time we've seen this object address: add it to mObjectMap
2118 // and serialize the object at the current stream offset.
2119 PRInt64 thisOffset;
2120 rv = Tell(&thisOffset);
2121 if (NS_FAILED(rv)) {
2122 aObject->Release();
2123 return rv;
2124 }
2125
2126 // NB: aObject was already held, and mObject is a raw nsISupports*.
2127 entry->mObject = aObject;
2128
2129 oid = (mObjectMap.entryCount << MFL_OBJECT_TAG_BITS);
2130 entry->mOID = oid;
2131
2132 // NB: the (32-bit, fast) CID and object data follow the OID.
2133 entry->mInfo.mCIDOffset = thisOffset + sizeof(oid);
2134 entry->mInfo.mStrongRefCnt = aIsStrongRef ? 1 : 0;
2135 entry->mInfo.mWeakRefCnt = aIsStrongRef ? 0 : 1;
2136
2137 // Record in oid the fact that we're defining this object in the
2138 // stream, and get the object's class info here, so we can take
2139 // note of singletons in order to avoid reserializing them when
2140 // updating after reading.
2141 oid |= MFL_OBJECT_DEF_TAG;
2142 classInfo = do_QueryInterface(aObject);
2143 if (!classInfo)
2144 return NS_ERROR_FAILURE;
2145
2146 PRUint32 flags;
2147 if (NS_SUCCEEDED(classInfo->GetFlags(&flags)) &&
2148 (flags & nsIClassInfo::SINGLETON)) {
2149 MFL_SET_SINGLETON_FLAG(&entry->mInfo);
2150 }
2151 } else {
2152 // Already serialized, recover oid and update the desired refcnt.
2153 oid = entry->mOID;
2154 if (aIsStrongRef) {
2155 ++entry->mInfo.mStrongRefCnt;
2156 NS_ASSERTION(entry->mInfo.mStrongRefCnt != 0,
2157 "mStrongRefCnt overflow");
2158 } else {
2159 MFL_BUMP_WEAK_REFCNT(&entry->mInfo);
2160 NS_ASSERTION(MFL_GET_WEAK_REFCNT(&entry->mInfo) != 0,
2161 "mWeakRefCnt overflow");
2162 }
2163
2164 aObject->Release();
2165 }
2166 }
2167
2168 if (!aIsStrongRef)
2169 oid |= MFL_WEAK_REF_TAG;
2170 oid |= (aTags & MFL_QUERY_INTERFACE_TAG);
2171
2172 rv = Write32(oid ^ MFL_OID_XOR_KEY);
2173 if (NS_FAILED(rv))
2174 return rv;
2175
2176 if (oid & MFL_OBJECT_DEF_TAG) {
2177 nsCOMPtr<nsISerializable> serializable(do_QueryInterface(aObject));
2178 if (!serializable)
2179 return NS_ERROR_FAILURE;
2180
2181 nsCID slowCID;
2182 rv = classInfo->GetClassIDNoAlloc(&slowCID);
2183 if (NS_FAILED(rv))
2184 return rv;
2185
2186 NSFastLoadID fastCID;
2187 rv = MapID(slowCID, &fastCID);
2188 if (NS_FAILED(rv))
2189 return rv;
2190
2191 rv = WriteFastID(fastCID);
2192 if (NS_FAILED(rv))
2193 return rv;
2194
2195 rv = serializable->Write(this);
2196 if (NS_FAILED(rv))
2197 return rv;
2198 }
2199
2200 return NS_OK;
2201}
2202
2203NS_IMETHODIMP
2204nsFastLoadFileWriter::WriteObject(nsISupports* aObject, PRBool aIsStrongRef)
2205{
2206#ifdef NS_DEBUG
2207 nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
2208
2209 NS_ASSERTION(rootObject.get() == aObject,
2210 "bad call to WriteObject -- call WriteCompoundObject!");
2211#endif
2212
2213 return WriteObjectCommon(aObject, aIsStrongRef, 0);
2214}
2215
2216NS_IMETHODIMP
2217nsFastLoadFileWriter::WriteSingleRefObject(nsISupports* aObject)
2218{
2219#ifdef NS_DEBUG
2220 nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
2221
2222 NS_ASSERTION(rootObject.get() == aObject,
2223 "bad call to WriteSingleRefObject -- call WriteCompoundObject!");
2224#endif
2225
2226 return WriteObjectCommon(aObject, PR_TRUE, MFL_SINGLE_REF_PSEUDO_TAG);
2227}
2228
2229NS_IMETHODIMP
2230nsFastLoadFileWriter::WriteCompoundObject(nsISupports* aObject,
2231 const nsIID& aIID,
2232 PRBool aIsStrongRef)
2233{
2234 nsresult rv;
2235 nsCOMPtr<nsISupports> rootObject(do_QueryInterface(aObject));
2236
2237#ifdef NS_DEBUG
2238 nsCOMPtr<nsISupports> roundtrip;
2239 rootObject->QueryInterface(aIID, getter_AddRefs(roundtrip));
2240
2241 NS_ASSERTION(rootObject.get() != aObject,
2242 "wasteful call to WriteCompoundObject -- call WriteObject!");
2243 NS_ASSERTION(roundtrip.get() == aObject,
2244 "bad aggregation or multiple inheritance detected by call to "
2245 "WriteCompoundObject!");
2246#endif
2247
2248 rv = WriteObjectCommon(rootObject, aIsStrongRef, MFL_QUERY_INTERFACE_TAG);
2249 if (NS_FAILED(rv))
2250 return rv;
2251
2252 NSFastLoadID iid;
2253 rv = MapID(aIID, &iid);
2254 if (NS_FAILED(rv))
2255 return rv;
2256
2257 return WriteFastID(iid);
2258}
2259
2260NS_IMETHODIMP
2261nsFastLoadFileWriter::WriteID(const nsID& aID)
2262{
2263 nsresult rv;
2264 NSFastLoadID fastID;
2265
2266 rv = MapID(aID, &fastID);
2267 if (NS_FAILED(rv))
2268 return rv;
2269
2270 return WriteFastID(fastID);
2271}
2272
2273NS_IMETHODIMP
2274nsFastLoadFileWriter::Seek(PRInt32 aWhence, PRInt64 aOffset)
2275{
2276 mCurrentDocumentMapEntry = nsnull;
2277 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
2278 return seekable->Seek(aWhence, aOffset);
2279}
2280
2281NS_IMETHODIMP
2282nsFastLoadFileWriter::Tell(PRInt64 *aResult)
2283{
2284 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
2285 return seekable->Tell(aResult);
2286}
2287
2288NS_IMETHODIMP
2289nsFastLoadFileWriter::SetEOF()
2290{
2291 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
2292 return seekable->SetEOF();
2293}
2294
2295NS_COM nsresult
2296NS_NewFastLoadFileWriter(nsIObjectOutputStream* *aResult,
2297 nsIOutputStream* aDestStream,
2298 nsIFastLoadFileIO* aFileIO)
2299{
2300 nsFastLoadFileWriter* writer =
2301 new nsFastLoadFileWriter(aDestStream, aFileIO);
2302 if (!writer)
2303 return NS_ERROR_OUT_OF_MEMORY;
2304
2305 // Stabilize writer's refcnt.
2306 nsCOMPtr<nsIObjectOutputStream> stream(writer);
2307
2308 nsresult rv = writer->Open();
2309 if (NS_FAILED(rv))
2310 return rv;
2311
2312 *aResult = stream;
2313 NS_ADDREF(*aResult);
2314 return NS_OK;
2315}
2316
2317// -------------------------- nsFastLoadFileUpdater --------------------------
2318
2319NS_IMPL_ISUPPORTS_INHERITED1(nsFastLoadFileUpdater,
2320 nsFastLoadFileWriter,
2321 nsIFastLoadFileIO)
2322
2323NS_IMETHODIMP
2324nsFastLoadFileUpdater::GetInputStream(nsIInputStream** aResult)
2325{
2326 *aResult = mInputStream;
2327 NS_IF_ADDREF(*aResult);
2328 return NS_OK;
2329}
2330
2331NS_IMETHODIMP
2332nsFastLoadFileUpdater::GetOutputStream(nsIOutputStream** aResult)
2333{
2334 *aResult = nsnull;
2335 return NS_OK;
2336}
2337
2338PLDHashOperator PR_CALLBACK
2339nsFastLoadFileUpdater::CopyReadDocumentMapEntryToUpdater(PLDHashTable *aTable,
2340 PLDHashEntryHdr *aHdr,
2341 PRUint32 aNumber,
2342 void *aData)
2343{
2344 nsDocumentMapReadEntry* readEntry =
2345 NS_STATIC_CAST(nsDocumentMapReadEntry*, aHdr);
2346 nsFastLoadFileUpdater* updater =
2347 NS_REINTERPRET_CAST(nsFastLoadFileUpdater*, aData);
2348
2349 void* spec = nsMemory::Clone(readEntry->mString,
2350 strlen(readEntry->mString) + 1);
2351 if (!spec)
2352 return PL_DHASH_STOP;
2353
2354 nsDocumentMapWriteEntry* writeEntry =
2355 NS_STATIC_CAST(nsDocumentMapWriteEntry*,
2356 PL_DHashTableOperate(&updater->mDocumentMap, spec,
2357 PL_DHASH_ADD));
2358 if (!writeEntry) {
2359 nsMemory::Free(spec);
2360 return PL_DHASH_STOP;
2361 }
2362
2363 writeEntry->mString = NS_REINTERPRET_CAST(const char*, spec);
2364 writeEntry->mURI = nsnull;
2365 writeEntry->mInitialSegmentOffset = readEntry->mInitialSegmentOffset;
2366 writeEntry->mCurrentSegmentOffset = 0;
2367 return PL_DHASH_NEXT;
2368}
2369
2370nsresult
2371nsFastLoadFileUpdater::Open(nsFastLoadFileReader* aReader)
2372{
2373 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mOutputStream));
2374 if (!seekable)
2375 return NS_ERROR_UNEXPECTED;
2376
2377 nsresult rv;
2378 rv = nsFastLoadFileWriter::Init();
2379 if (NS_FAILED(rv))
2380 return rv;
2381
2382 PRUint32 i, n;
2383
2384 // Map from dense, zero-based, uint32 NSFastLoadID in reader to 16-byte
2385 // nsID in updater.
2386 nsID* readIDMap = aReader->mFooter.mIDMap;
2387 for (i = 0, n = aReader->mFooter.mNumIDs; i < n; i++) {
2388 NSFastLoadID fastID;
2389 rv = MapID(readIDMap[i], &fastID);
2390 NS_ASSERTION(fastID == i + 1, "huh?");
2391 if (NS_FAILED(rv))
2392 return rv;
2393 }
2394
2395 // Map from reader dense, zero-based MFL_OID_TO_SHARP_INDEX(oid) to sharp
2396 // object offset and refcnt information in updater.
2397 nsFastLoadFileReader::nsObjectMapEntry* readObjectMap =
2398 aReader->mFooter.mObjectMap;
2399
2400 // Prepare to save aReader state in case we need to seek back and read a
2401 // singleton object that might otherwise get written by this updater.
2402 nsDocumentMapReadEntry* saveDocMapEntry = nsnull;
2403 nsCOMPtr<nsISeekableStream> inputSeekable;
2404 PRInt64 saveOffset = 0;
2405
2406 for (i = 0, n = aReader->mFooter.mNumSharpObjects; i < n; i++) {
2407 nsFastLoadFileReader::nsObjectMapEntry* readEntry = &readObjectMap[i];
2408
2409 NS_ASSERTION(readEntry->mCIDOffset != 0,
2410 "fastload updater: mCIDOffset cannot be zero!");
2411
2412 // If the reader didn't read this object but it's a singleton, we must
2413 // "deserialize" it now, to discover its one and only root nsISupports
2414 // address. The object already exists in memory if it was created at
2415 // startup without resort to the FastLoad file. The canonical example
2416 // is the system principal object held by all XUL JS scripts.
2417
2418 nsISupports* obj = readEntry->mReadObject;
2419 if (!obj && MFL_GET_SINGLETON_FLAG(readEntry)) {
2420 if (!saveDocMapEntry) {
2421 inputSeekable = do_QueryInterface(aReader->mInputStream);
2422 rv = inputSeekable->Tell(&saveOffset);
2423 if (NS_FAILED(rv))
2424 return rv;
2425
2426 saveDocMapEntry = aReader->mCurrentDocumentMapEntry;
2427 aReader->mCurrentDocumentMapEntry = nsnull;
2428 }
2429
2430 rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET,
2431 readEntry->mCIDOffset);
2432 if (NS_FAILED(rv))
2433 return rv;
2434
2435 rv = aReader
2436 ->DeserializeObject(getter_AddRefs(readEntry->mReadObject));
2437 if (NS_FAILED(rv))
2438 return rv;
2439 obj = readEntry->mReadObject;
2440
2441 // Don't forget to set mSkipOffset in case someone calls the reader
2442 // to "deserialize" (yet again) the object we just read.
2443 //
2444 // Say the singleton is the system principal, and the FastLoad file
2445 // contains data for navigator.xul including scripts and functions.
2446 // If we update the FastLoad file to contain data for messenger.xul
2447 // in a separate session started via mozilla -mail, *and during the
2448 // same FastLoad episode in this session* race to open a navigator
2449 // window, we will attempt to read all objects serialized in the
2450 // navigator.xul portion of the FastLoad file.
2451 //
2452 // mSkipOffset must be set in such a case so the reader can skip
2453 // the system principal's serialized data, because the updater for
2454 // messenger.xul being opened here has already read it.
2455
2456 rv = inputSeekable->Tell(&readEntry->mSkipOffset);
2457 if (NS_FAILED(rv))
2458 return rv;
2459 }
2460
2461 NSFastLoadOID oid = MFL_SHARP_INDEX_TO_OID(i);
2462 void* key = obj
2463 ? NS_REINTERPRET_CAST(void*, obj)
2464 : NS_REINTERPRET_CAST(void*, (oid | MFL_OBJECT_DEF_TAG));
2465
2466 nsSharpObjectMapEntry* writeEntry =
2467 NS_STATIC_CAST(nsSharpObjectMapEntry*,
2468 PL_DHashTableOperate(&mObjectMap, key,
2469 PL_DHASH_ADD));
2470 if (!writeEntry)
2471 return NS_ERROR_OUT_OF_MEMORY;
2472
2473 // Hold the object if there is one, so that objmap_ClearEntry can
2474 // release the reference.
2475 NS_IF_ADDREF(obj);
2476 writeEntry->mObject = NS_REINTERPRET_CAST(nsISupports*, key);
2477 writeEntry->mOID = oid;
2478 writeEntry->mInfo.mCIDOffset = readEntry->mCIDOffset;
2479 writeEntry->mInfo.mStrongRefCnt = readEntry->mSaveStrongRefCnt;
2480 writeEntry->mInfo.mWeakRefCnt = readEntry->mSaveWeakRefCnt;
2481 }
2482
2483 // If we had to read any singletons, restore aReader's saved state.
2484 if (saveDocMapEntry) {
2485 rv = inputSeekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
2486 if (NS_FAILED(rv))
2487 return rv;
2488
2489 aReader->mCurrentDocumentMapEntry = saveDocMapEntry;
2490 }
2491
2492 // Copy URI spec string and initial segment offset in FastLoad file from
2493 // nsDocumentMapReadEntry in reader to nsDocumentMapWriteEntry in updater.
2494 // If we didn't enumerate all entries, we ran out of memory.
2495 n = PL_DHashTableEnumerate(&aReader->mFooter.mDocumentMap,
2496 CopyReadDocumentMapEntryToUpdater,
2497 this);
2498 if (n != aReader->mFooter.mDocumentMap.entryCount)
2499 return NS_ERROR_OUT_OF_MEMORY;
2500
2501 // Copy source filename dependencies from reader to updater.
2502 nsISupportsArray* readDeps = aReader->mFooter.mDependencies;
2503 rv = readDeps->Count(&n);
2504 if (NS_FAILED(rv))
2505 return rv;
2506
2507 for (i = 0; i < n; i++) {
2508 nsCOMPtr<nsIFile> file;
2509 rv = readDeps->GetElementAt(i, getter_AddRefs(file));
2510 if (NS_FAILED(rv))
2511 return rv;
2512
2513 rv = AddDependency(file);
2514 if (NS_FAILED(rv))
2515 return rv;
2516 }
2517
2518 // Seek to the reader's footer offset so we overwrite the footer. First,
2519 // update the header to have a zero mFooterOffset, which will invalidate
2520 // the FastLoad file on next startup read attempt, should we crash before
2521 // completing this update.
2522 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
2523 offsetof(nsFastLoadHeader, mFooterOffset));
2524 if (NS_FAILED(rv))
2525 return rv;
2526
2527 rv = Write32(0);
2528 if (NS_FAILED(rv))
2529 return rv;
2530
2531 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
2532 aReader->mHeader.mFooterOffset);
2533 if (NS_FAILED(rv))
2534 return rv;
2535
2536 // Avoid creating yet another object by implementing nsIFastLoadFileIO on
2537 // this updater, and save aReader's input stream so it can be returned by
2538 // GetInputStream called from nsFastLoadFileWriter::Close. This requires
2539 // that we override Close to break the resulting zero-length cycle.
2540 mFileIO = this;
2541 mInputStream = aReader->mInputStream;
2542 return NS_OK;
2543}
2544
2545NS_IMETHODIMP
2546nsFastLoadFileUpdater::Close()
2547{
2548 // Call base-class Close implementation, which uses mFileIO.
2549 nsresult rv = nsFastLoadFileWriter::Close();
2550
2551 // Break degenerate cycle from this->mFileIO to this.
2552 mFileIO = nsnull;
2553 return rv;
2554}
2555
2556NS_COM nsresult
2557NS_NewFastLoadFileUpdater(nsIObjectOutputStream* *aResult,
2558 nsIOutputStream* aOutputStream,
2559 nsIObjectInputStream* aReaderAsStream)
2560{
2561 // Make sure that aReaderAsStream is an nsFastLoadFileReader.
2562 nsCOMPtr<nsIFastLoadFileReader> reader(do_QueryInterface(aReaderAsStream));
2563 if (!reader)
2564 return NS_ERROR_UNEXPECTED;
2565
2566 nsFastLoadFileUpdater* updater = new nsFastLoadFileUpdater(aOutputStream);
2567 if (!updater)
2568 return NS_ERROR_OUT_OF_MEMORY;
2569
2570 // Stabilize updater's refcnt.
2571 nsCOMPtr<nsIObjectOutputStream> stream(updater);
2572
2573 nsresult rv = updater->Open(NS_STATIC_CAST(nsFastLoadFileReader*,
2574 aReaderAsStream));
2575 if (NS_FAILED(rv))
2576 return rv;
2577
2578 *aResult = stream;
2579 NS_ADDREF(*aResult);
2580 return NS_OK;
2581}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette