1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is Mozilla FastLoad code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 2001
20
* the Initial Developer. All Rights Reserved.
23
* Brendan Eich <brendan@mozilla.org> (original author)
25
* Alternatively, the contents of this file may be used under the terms of
26
* either of the GNU General Public License Version 2 or later (the "GPL"),
27
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
* in which case the provisions of the GPL or the LGPL are applicable instead
29
* of those above. If you wish to allow use of your version of this file only
30
* under the terms of either the GPL or the LGPL, and not to allow others to
31
* use your version of this file under the terms of the MPL, indicate your
32
* decision by deleting the provisions above and replace them with the notice
33
* and other provisions required by the GPL or the LGPL. If you do not delete
34
* the provisions above, a recipient may use your version of this file under
35
* the terms of any one of the MPL, the GPL or the LGPL.
37
* ***** END LICENSE BLOCK ***** */
44
#include "nsAppDirectoryServiceDefs.h"
45
#include "nsAutoLock.h"
47
#include "nsFastLoadFile.h"
48
#include "nsFastLoadPtr.h"
49
#include "nsFastLoadService.h"
52
#include "nsIComponentManager.h"
53
#include "nsIEnumerator.h"
54
#include "nsIFastLoadFileControl.h"
56
#include "nsIObjectInputStream.h"
57
#include "nsIObjectOutputStream.h"
58
#include "nsISeekableStream.h"
59
#include "nsISupports.h"
61
PR_IMPLEMENT_DATA(nsIFastLoadService*) gFastLoadService_ = nsnull;
63
NS_IMPL_THREADSAFE_ISUPPORTS1(nsFastLoadService, nsIFastLoadService)
65
nsFastLoadService::nsFastLoadService()
67
mFastLoadPtrMap(nsnull),
70
NS_ASSERTION(gFastLoadService_ == nsnull, "double FastLoadService init?");
71
gFastLoadService_ = this;
74
nsFastLoadService::~nsFastLoadService()
76
gFastLoadService_ = nsnull;
79
mInputStream->Close();
81
mOutputStream->Close();
84
PL_DHashTableDestroy(mFastLoadPtrMap);
86
PR_DestroyLock(mLock);
90
nsFastLoadService::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
94
return NS_ERROR_NO_AGGREGATION;
96
nsFastLoadService* fastLoadService = new nsFastLoadService();
98
return NS_ERROR_OUT_OF_MEMORY;
100
fastLoadService->mLock = PR_NewLock();
101
if (!fastLoadService->mLock) {
102
delete fastLoadService;
103
return NS_ERROR_OUT_OF_MEMORY;
106
NS_ADDREF(fastLoadService);
107
nsresult rv = fastLoadService->QueryInterface(aIID, aResult);
108
NS_RELEASE(fastLoadService);
114
// Mac format: "<Basename> FastLoad File" with <basename> capitalized.
117
# define MASSAGE_BASENAME(bn) (bn.SetCharAt(nsCRT::ToUpper(bn.CharAt(0)), 0))
118
# define PLATFORM_FASL_SUFFIX " FastLoad File"
120
#elif defined(XP_UNIX) || defined(XP_BEOS)
122
// Unix format: "<basename>.mfasl".
123
# define MASSAGE_BASENAME(bn) /* nothing */
124
# define PLATFORM_FASL_SUFFIX ".mfasl"
126
#elif defined(XP_WIN) || defined(XP_OS2)
128
// Windows format: "<basename>.mfl".
129
# define MASSAGE_BASENAME(bn) /* nothing */
130
# define PLATFORM_FASL_SUFFIX ".mfl"
135
nsFastLoadService::NewFastLoadFile(const char* aBaseName, nsIFile* *aResult)
138
nsCOMPtr<nsIFile> file;
140
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
141
getter_AddRefs(file));
145
nsCAutoString name(aBaseName);
146
MASSAGE_BASENAME(name);
147
name += PLATFORM_FASL_SUFFIX;
148
rv = file->AppendNative(name);
158
nsFastLoadService::NewInputStream(nsIInputStream* aSrcStream,
159
nsIObjectInputStream* *aResult)
161
nsAutoLock lock(mLock);
163
nsCOMPtr<nsIObjectInputStream> stream;
164
nsresult rv = NS_NewFastLoadFileReader(getter_AddRefs(stream), aSrcStream);
174
nsFastLoadService::NewOutputStream(nsIOutputStream* aDestStream,
175
nsIObjectOutputStream* *aResult)
177
nsAutoLock lock(mLock);
179
return NS_NewFastLoadFileWriter(aResult, aDestStream, mFileIO);
183
nsFastLoadService::GetInputStream(nsIObjectInputStream* *aResult)
185
NS_IF_ADDREF(*aResult = mInputStream);
190
nsFastLoadService::SetInputStream(nsIObjectInputStream* aStream)
192
nsAutoLock lock(mLock);
193
mInputStream = aStream;
198
nsFastLoadService::GetOutputStream(nsIObjectOutputStream* *aResult)
200
NS_IF_ADDREF(*aResult = mOutputStream);
205
nsFastLoadService::SetOutputStream(nsIObjectOutputStream* aStream)
207
nsAutoLock lock(mLock);
208
mOutputStream = aStream;
213
nsFastLoadService::GetFileIO(nsIFastLoadFileIO* *aResult)
215
NS_IF_ADDREF(*aResult = mFileIO);
220
nsFastLoadService::SetFileIO(nsIFastLoadFileIO* aFileIO)
222
nsAutoLock lock(mLock);
228
nsFastLoadService::GetDirection(PRInt32 *aResult)
230
*aResult = mDirection;
235
nsFastLoadService::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
237
nsresult rv = NS_ERROR_NOT_AVAILABLE;
238
nsCOMPtr<nsIFastLoadFileControl> control;
241
nsAutoLock lock(mLock);
244
control = do_QueryInterface(mInputStream);
246
rv = control->HasMuxedDocument(aURISpec, aResult);
249
if (! *aResult && mOutputStream) {
250
control = do_QueryInterface(mOutputStream);
252
rv = control->HasMuxedDocument(aURISpec, aResult);
259
nsFastLoadService::StartMuxedDocument(nsISupports* aURI, const char* aURISpec,
260
PRInt32 aDirectionFlags)
262
nsresult rv = NS_ERROR_NOT_AVAILABLE;
263
nsCOMPtr<nsIFastLoadFileControl> control;
264
nsAutoLock lock(mLock);
266
// Try for an input stream first, in case aURISpec's data is multiplexed
267
// in the current FastLoad file.
268
if ((aDirectionFlags & NS_FASTLOAD_READ) && mInputStream) {
269
control = do_QueryInterface(mInputStream);
271
// If aURISpec is not in the multiplex, control->StartMuxedDocument
272
// will return NS_ERROR_NOT_AVAILABLE.
273
rv = control->StartMuxedDocument(aURI, aURISpec);
274
if (NS_SUCCEEDED(rv) || rv != NS_ERROR_NOT_AVAILABLE)
277
// Ok, aURISpec is not in the existing mux. If we have no output
278
// stream yet, wrap the reader with a FastLoad file updater.
279
if (!mOutputStream && mFileIO) {
280
nsCOMPtr<nsIOutputStream> output;
281
rv = mFileIO->GetOutputStream(getter_AddRefs(output));
285
// NB: mInputStream must be an nsFastLoadFileReader!
286
rv = NS_NewFastLoadFileUpdater(getter_AddRefs(mOutputStream),
293
if (aDirectionFlags == NS_FASTLOAD_READ) {
294
// Tell our caller to re-start multiplexing, rather than attempt
295
// to select and deserialize now.
296
return NS_ERROR_NOT_AVAILABLE;
301
if ((aDirectionFlags & NS_FASTLOAD_WRITE) && mOutputStream) {
302
control = do_QueryInterface(mOutputStream);
304
rv = control->StartMuxedDocument(aURI, aURISpec);
310
nsFastLoadService::SelectMuxedDocument(nsISupports* aURI, nsISupports** aResult)
312
nsresult rv = NS_ERROR_NOT_AVAILABLE;
313
nsCOMPtr<nsIFastLoadFileControl> control;
314
nsAutoLock lock(mLock);
316
// Try to select the reader, if any; then only if the URI was not in the
317
// file already, select the writer/updater.
319
control = do_QueryInterface(mInputStream);
321
rv = control->SelectMuxedDocument(aURI, aResult);
322
if (NS_SUCCEEDED(rv))
323
mDirection = NS_FASTLOAD_READ;
327
if (rv == NS_ERROR_NOT_AVAILABLE && mOutputStream) {
328
control = do_QueryInterface(mOutputStream);
330
rv = control->SelectMuxedDocument(aURI, aResult);
331
if (NS_SUCCEEDED(rv))
332
mDirection = NS_FASTLOAD_WRITE;
340
nsFastLoadService::EndMuxedDocument(nsISupports* aURI)
342
nsresult rv = NS_ERROR_NOT_AVAILABLE;
343
nsCOMPtr<nsIFastLoadFileControl> control;
344
nsAutoLock lock(mLock);
346
// Try to end the document identified by aURI in the reader, if any; then
347
// only if the URI was not in the file already, end the writer/updater.
349
control = do_QueryInterface(mInputStream);
351
rv = control->EndMuxedDocument(aURI);
354
if (rv == NS_ERROR_NOT_AVAILABLE && mOutputStream) {
355
control = do_QueryInterface(mOutputStream);
357
rv = control->EndMuxedDocument(aURI);
365
nsFastLoadService::AddDependency(nsIFile* aFile)
367
nsAutoLock lock(mLock);
369
nsCOMPtr<nsIFastLoadWriteControl> control(do_QueryInterface(mOutputStream));
371
return NS_ERROR_NOT_AVAILABLE;
373
return control->AddDependency(aFile);
377
nsFastLoadService::ComputeChecksum(nsIFile* aFile,
378
nsIFastLoadReadControl* aControl,
382
nsresult rv = aFile->GetNativePath(path);
386
nsCStringKey key(path);
387
PRUint32 checksum = NS_PTR_TO_INT32(mChecksumTable.Get(&key));
389
*aChecksum = checksum;
393
rv = aControl->ComputeChecksum(&checksum);
397
mChecksumTable.Put(&key, NS_INT32_TO_PTR(checksum));
398
*aChecksum = checksum;
403
nsFastLoadService::CacheChecksum(nsIFile* aFile, nsIObjectOutputStream *aStream)
405
nsCOMPtr<nsIFastLoadFileControl> control(do_QueryInterface(aStream));
407
return NS_ERROR_FAILURE;
410
nsresult rv = control->GetChecksum(&checksum);
415
rv = aFile->GetNativePath(path);
419
nsCStringKey key(path);
420
mChecksumTable.Put(&key, NS_INT32_TO_PTR(checksum));
424
struct nsFastLoadPtrEntry : public PLDHashEntryHdr {
425
nsISupports** mPtrAddr; // key, must come first for PL_DHashGetStubOps
430
nsFastLoadService::GetFastLoadReferent(nsISupports* *aPtrAddr)
432
NS_ASSERTION(*aPtrAddr == nsnull,
433
"aPtrAddr doesn't point to null nsFastLoadPtr<T>::mRawAddr?");
435
nsAutoLock lock(mLock);
436
if (!mFastLoadPtrMap || !mInputStream)
439
nsFastLoadPtrEntry* entry =
440
NS_STATIC_CAST(nsFastLoadPtrEntry*,
441
PL_DHashTableOperate(mFastLoadPtrMap, aPtrAddr,
443
if (PL_DHASH_ENTRY_IS_FREE(entry))
447
nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
449
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, entry->mOffset);
453
rv = mInputStream->ReadObject(PR_TRUE, aPtrAddr);
457
// Shrink the table if half the entries are removed sentinels.
458
PRUint32 size = PL_DHASH_TABLE_SIZE(mFastLoadPtrMap);
459
if (mFastLoadPtrMap->removedCount >= (size >> 2))
460
PL_DHashTableOperate(mFastLoadPtrMap, entry, PL_DHASH_REMOVE);
462
PL_DHashTableRawRemove(mFastLoadPtrMap, entry);
468
nsFastLoadService::ReadFastLoadPtr(nsIObjectInputStream* aInputStream,
469
nsISupports* *aPtrAddr)
471
// nsFastLoadPtrs self-construct to null, so if we have a non-null value
472
// in our inout parameter, we must have been read already, alright!
478
nsAutoLock lock(mLock);
480
rv = aInputStream->Read32(&nextOffset);
484
nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(aInputStream));
486
return NS_ERROR_FAILURE;
489
rv = seekable->Tell(&thisOffset);
493
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, nextOffset);
497
if (!mFastLoadPtrMap) {
498
mFastLoadPtrMap = PL_NewDHashTable(PL_DHashGetStubOps(), this,
499
sizeof(nsFastLoadPtrEntry),
501
if (!mFastLoadPtrMap)
502
return NS_ERROR_OUT_OF_MEMORY;
505
nsFastLoadPtrEntry* entry =
506
NS_STATIC_CAST(nsFastLoadPtrEntry*,
507
PL_DHashTableOperate(mFastLoadPtrMap, aPtrAddr,
509
NS_ASSERTION(entry->mPtrAddr == nsnull, "duplicate nsFastLoadPtr?!");
511
entry->mPtrAddr = aPtrAddr;
513
LL_L2UI(entry->mOffset, thisOffset);
518
nsFastLoadService::WriteFastLoadPtr(nsIObjectOutputStream* aOutputStream,
519
nsISupports* aObject)
521
NS_ASSERTION(aObject != nsnull, "writing an unread nsFastLoadPtr?!");
523
return NS_ERROR_UNEXPECTED;
526
nsAutoLock lock(mLock); // serialize writes to aOutputStream
528
nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(aOutputStream));
530
return NS_ERROR_FAILURE;
533
rv = seekable->Tell(&saveOffset);
537
rv = aOutputStream->Write32(0); // nextOffset placeholder
541
rv = aOutputStream->WriteObject(aObject, PR_TRUE);
546
rv = seekable->Tell(&nextOffset);
550
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
554
rv = aOutputStream->Write32(nextOffset);
558
rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, nextOffset);