Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / xpcom / io / nsFastLoadService.cpp
blob4ad093dab6fe22ef0d328635024d2710c5ae6074
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 "prtypes.h"
40 #include "prio.h"
41 #include "prtime.h"
42 #include "pldhash.h"
44 #include "nsAppDirectoryServiceDefs.h"
45 #include "nsAutoLock.h"
46 #include "nsCOMPtr.h"
47 #include "nsFastLoadFile.h"
48 #include "nsFastLoadService.h"
49 #include "nsString.h"
51 #include "nsIComponentManager.h"
52 #include "nsIEnumerator.h"
53 #include "nsIFastLoadFileControl.h"
54 #include "nsIFile.h"
55 #include "nsIObjectInputStream.h"
56 #include "nsIObjectOutputStream.h"
57 #include "nsISeekableStream.h"
58 #include "nsISupports.h"
60 NS_IMPL_THREADSAFE_ISUPPORTS1(nsFastLoadService, nsIFastLoadService)
62 nsFastLoadService::nsFastLoadService()
63 : mLock(nsnull),
64 mFastLoadPtrMap(nsnull),
65 mDirection(0)
69 nsFastLoadService::~nsFastLoadService()
71 if (mInputStream)
72 mInputStream->Close();
73 if (mOutputStream)
74 mOutputStream->Close();
76 if (mFastLoadPtrMap)
77 PL_DHashTableDestroy(mFastLoadPtrMap);
78 if (mLock)
79 PR_DestroyLock(mLock);
82 NS_IMETHODIMP
83 nsFastLoadService::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
85 *aResult = nsnull;
86 if (aOuter)
87 return NS_ERROR_NO_AGGREGATION;
89 nsFastLoadService* fastLoadService = new nsFastLoadService();
90 if (!fastLoadService)
91 return NS_ERROR_OUT_OF_MEMORY;
93 fastLoadService->mLock = PR_NewLock();
94 if (!fastLoadService->mLock) {
95 delete fastLoadService;
96 return NS_ERROR_OUT_OF_MEMORY;
99 NS_ADDREF(fastLoadService);
100 nsresult rv = fastLoadService->QueryInterface(aIID, aResult);
101 NS_RELEASE(fastLoadService);
102 return rv;
105 nsresult
106 nsFastLoadService::NewFastLoadFile(const char* aBaseName, nsIFile* *aResult)
108 nsresult rv;
110 // Try "ProfDS" first, so that we can get the profile directory
111 // during startup.
112 nsCOMPtr<nsIFile> profFile;
113 rv = NS_GetSpecialDirectory("ProfDS",
114 getter_AddRefs(profFile));
115 if (NS_FAILED(rv)) {
116 // The directory service doesn't know about "ProfDS", so just ask
117 // for the regular profile directory key. Note, however, that this
118 // will fail if a profile hasn't yet been selected.
119 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
120 getter_AddRefs(profFile));
121 if (NS_FAILED(rv)) {
122 return rv;
126 // Try "ProfLDS" first, so that we can get the local profile directory
127 // during startup.
128 nsCOMPtr<nsIFile> file;
129 rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(file));
130 if (NS_FAILED(rv)) {
131 // The directory service doesn't know about "ProfLDS", so just ask
132 // for the regular local profile directory key. Note, however, that
133 // this will fail if a profile hasn't yet been selected.
134 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
135 getter_AddRefs(file));
137 if (NS_FAILED(rv))
138 file = profFile;
140 PRBool sameDir;
141 rv = file->Equals(profFile, &sameDir);
142 if (NS_FAILED(rv))
143 return rv;
145 nsCAutoString name(aBaseName);
146 name += PLATFORM_FASL_SUFFIX;
147 rv = file->AppendNative(name);
148 if (NS_FAILED(rv))
149 return rv;
151 if (!sameDir) {
152 // Cleanup any pre-existing fastload files that may live in the profile
153 // directory from previous versions of the code that didn't store them
154 // in the profile temp directory.
155 rv = profFile->AppendNative(name);
156 if (NS_SUCCEEDED(rv))
157 profFile->Remove(PR_FALSE); // OK if this fails
160 *aResult = file;
161 NS_ADDREF(*aResult);
162 return NS_OK;
165 NS_IMETHODIMP
166 nsFastLoadService::NewInputStream(nsIInputStream* aSrcStream,
167 nsIObjectInputStream* *aResult)
169 nsAutoLock lock(mLock);
171 nsCOMPtr<nsIObjectInputStream> stream;
172 nsresult rv = NS_NewFastLoadFileReader(getter_AddRefs(stream), aSrcStream);
173 if (NS_FAILED(rv))
174 return rv;
176 *aResult = stream;
177 NS_ADDREF(*aResult);
178 return NS_OK;
181 NS_IMETHODIMP
182 nsFastLoadService::NewOutputStream(nsIOutputStream* aDestStream,
183 nsIObjectOutputStream* *aResult)
185 nsAutoLock lock(mLock);
187 return NS_NewFastLoadFileWriter(aResult, aDestStream, mFileIO);
190 NS_IMETHODIMP
191 nsFastLoadService::GetInputStream(nsIObjectInputStream* *aResult)
193 NS_IF_ADDREF(*aResult = mInputStream);
194 return NS_OK;
197 NS_IMETHODIMP
198 nsFastLoadService::SetInputStream(nsIObjectInputStream* aStream)
200 nsAutoLock lock(mLock);
201 mInputStream = aStream;
202 return NS_OK;
205 NS_IMETHODIMP
206 nsFastLoadService::GetOutputStream(nsIObjectOutputStream* *aResult)
208 NS_IF_ADDREF(*aResult = mOutputStream);
209 return NS_OK;
212 NS_IMETHODIMP
213 nsFastLoadService::SetOutputStream(nsIObjectOutputStream* aStream)
215 nsAutoLock lock(mLock);
216 mOutputStream = aStream;
217 return NS_OK;
220 NS_IMETHODIMP
221 nsFastLoadService::GetFileIO(nsIFastLoadFileIO* *aResult)
223 NS_IF_ADDREF(*aResult = mFileIO);
224 return NS_OK;
227 NS_IMETHODIMP
228 nsFastLoadService::SetFileIO(nsIFastLoadFileIO* aFileIO)
230 nsAutoLock lock(mLock);
231 mFileIO = aFileIO;
232 return NS_OK;
235 NS_IMETHODIMP
236 nsFastLoadService::GetDirection(PRInt32 *aResult)
238 *aResult = mDirection;
239 return NS_OK;
242 NS_IMETHODIMP
243 nsFastLoadService::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
245 nsresult rv = NS_ERROR_NOT_AVAILABLE;
246 nsCOMPtr<nsIFastLoadFileControl> control;
248 *aResult = PR_FALSE;
249 nsAutoLock lock(mLock);
251 if (mInputStream) {
252 control = do_QueryInterface(mInputStream);
253 if (control)
254 rv = control->HasMuxedDocument(aURISpec, aResult);
257 if (! *aResult && mOutputStream) {
258 control = do_QueryInterface(mOutputStream);
259 if (control)
260 rv = control->HasMuxedDocument(aURISpec, aResult);
263 return rv;
266 NS_IMETHODIMP
267 nsFastLoadService::StartMuxedDocument(nsISupports* aURI, const char* aURISpec,
268 PRInt32 aDirectionFlags)
270 nsresult rv = NS_ERROR_NOT_AVAILABLE;
271 nsCOMPtr<nsIFastLoadFileControl> control;
272 nsAutoLock lock(mLock);
274 // Try for an input stream first, in case aURISpec's data is multiplexed
275 // in the current FastLoad file.
276 if ((aDirectionFlags & NS_FASTLOAD_READ) && mInputStream) {
277 control = do_QueryInterface(mInputStream);
278 if (control) {
279 // If aURISpec is not in the multiplex, control->StartMuxedDocument
280 // will return NS_ERROR_NOT_AVAILABLE.
281 rv = control->StartMuxedDocument(aURI, aURISpec);
282 if (NS_SUCCEEDED(rv) || rv != NS_ERROR_NOT_AVAILABLE)
283 return rv;
285 // Ok, aURISpec is not in the existing mux. If we have no output
286 // stream yet, wrap the reader with a FastLoad file updater.
287 if (!mOutputStream && mFileIO) {
288 nsCOMPtr<nsIOutputStream> output;
289 rv = mFileIO->GetOutputStream(getter_AddRefs(output));
290 if (NS_FAILED(rv))
291 return rv;
293 // NB: mInputStream must be an nsFastLoadFileReader!
294 rv = NS_NewFastLoadFileUpdater(getter_AddRefs(mOutputStream),
295 output,
296 mInputStream);
297 if (NS_FAILED(rv))
298 return rv;
301 if (aDirectionFlags == NS_FASTLOAD_READ) {
302 // Tell our caller to re-start multiplexing, rather than attempt
303 // to select and deserialize now.
304 return NS_ERROR_NOT_AVAILABLE;
309 if ((aDirectionFlags & NS_FASTLOAD_WRITE) && mOutputStream) {
310 control = do_QueryInterface(mOutputStream);
311 if (control)
312 rv = control->StartMuxedDocument(aURI, aURISpec);
314 return rv;
317 NS_IMETHODIMP
318 nsFastLoadService::SelectMuxedDocument(nsISupports* aURI, nsISupports** aResult)
320 nsresult rv = NS_ERROR_NOT_AVAILABLE;
321 nsCOMPtr<nsIFastLoadFileControl> control;
322 nsAutoLock lock(mLock);
324 // Try to select the reader, if any; then only if the URI was not in the
325 // file already, select the writer/updater.
326 if (mInputStream) {
327 control = do_QueryInterface(mInputStream);
328 if (control) {
329 rv = control->SelectMuxedDocument(aURI, aResult);
330 if (NS_SUCCEEDED(rv))
331 mDirection = NS_FASTLOAD_READ;
335 if (rv == NS_ERROR_NOT_AVAILABLE && mOutputStream) {
336 control = do_QueryInterface(mOutputStream);
337 if (control) {
338 rv = control->SelectMuxedDocument(aURI, aResult);
339 if (NS_SUCCEEDED(rv))
340 mDirection = NS_FASTLOAD_WRITE;
344 return rv;
347 NS_IMETHODIMP
348 nsFastLoadService::EndMuxedDocument(nsISupports* aURI)
350 nsresult rv = NS_ERROR_NOT_AVAILABLE;
351 nsCOMPtr<nsIFastLoadFileControl> control;
352 nsAutoLock lock(mLock);
354 // Try to end the document identified by aURI in the reader, if any; then
355 // only if the URI was not in the file already, end the writer/updater.
356 if (mInputStream) {
357 control = do_QueryInterface(mInputStream);
358 if (control)
359 rv = control->EndMuxedDocument(aURI);
362 if (rv == NS_ERROR_NOT_AVAILABLE && mOutputStream) {
363 control = do_QueryInterface(mOutputStream);
364 if (control)
365 rv = control->EndMuxedDocument(aURI);
368 mDirection = 0;
369 return rv;
372 NS_IMETHODIMP
373 nsFastLoadService::AddDependency(nsIFile* aFile)
375 nsAutoLock lock(mLock);
377 nsCOMPtr<nsIFastLoadWriteControl> control(do_QueryInterface(mOutputStream));
378 if (!control)
379 return NS_ERROR_NOT_AVAILABLE;
381 return control->AddDependency(aFile);
384 NS_IMETHODIMP
385 nsFastLoadService::ComputeChecksum(nsIFile* aFile,
386 nsIFastLoadReadControl* aControl,
387 PRUint32 *aChecksum)
389 nsCAutoString path;
390 nsresult rv = aFile->GetNativePath(path);
391 if (NS_FAILED(rv))
392 return rv;
394 nsCStringKey key(path);
395 PRUint32 checksum = NS_PTR_TO_INT32(mChecksumTable.Get(&key));
396 if (checksum) {
397 *aChecksum = checksum;
398 return NS_OK;
401 rv = aControl->ComputeChecksum(&checksum);
402 if (NS_FAILED(rv))
403 return rv;
405 mChecksumTable.Put(&key, NS_INT32_TO_PTR(checksum));
406 *aChecksum = checksum;
407 return NS_OK;
410 NS_IMETHODIMP
411 nsFastLoadService::CacheChecksum(nsIFile* aFile, nsIObjectOutputStream *aStream)
413 nsCOMPtr<nsIFastLoadFileControl> control(do_QueryInterface(aStream));
414 if (!control)
415 return NS_ERROR_FAILURE;
417 PRUint32 checksum;
418 nsresult rv = control->GetChecksum(&checksum);
419 if (NS_FAILED(rv))
420 return rv;
422 nsCAutoString path;
423 rv = aFile->GetNativePath(path);
424 if (NS_FAILED(rv))
425 return rv;
427 nsCStringKey key(path);
428 mChecksumTable.Put(&key, NS_INT32_TO_PTR(checksum));
429 return NS_OK;
432 struct nsFastLoadPtrEntry : public PLDHashEntryHdr {
433 nsISupports** mPtrAddr; // key, must come first for PL_DHashGetStubOps
434 PRUint32 mOffset;
437 NS_IMETHODIMP
438 nsFastLoadService::GetFastLoadReferent(nsISupports* *aPtrAddr)
440 NS_ASSERTION(*aPtrAddr == nsnull,
441 "aPtrAddr doesn't point to null nsFastLoadPtr<T>::mRawAddr?");
443 nsAutoLock lock(mLock);
444 if (!mFastLoadPtrMap || !mInputStream)
445 return NS_OK;
447 nsFastLoadPtrEntry* entry =
448 static_cast<nsFastLoadPtrEntry*>
449 (PL_DHashTableOperate(mFastLoadPtrMap, aPtrAddr,
450 PL_DHASH_LOOKUP));
451 if (PL_DHASH_ENTRY_IS_FREE(entry))
452 return NS_OK;
454 nsresult rv;
455 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
457 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, entry->mOffset);
458 if (NS_FAILED(rv))
459 return rv;
461 rv = mInputStream->ReadObject(PR_TRUE, aPtrAddr);
462 if (NS_FAILED(rv))
463 return rv;
465 // Shrink the table if half the entries are removed sentinels.
466 PRUint32 size = PL_DHASH_TABLE_SIZE(mFastLoadPtrMap);
467 if (mFastLoadPtrMap->removedCount >= (size >> 2))
468 PL_DHashTableOperate(mFastLoadPtrMap, entry, PL_DHASH_REMOVE);
469 else
470 PL_DHashTableRawRemove(mFastLoadPtrMap, entry);
472 return NS_OK;
475 NS_IMETHODIMP
476 nsFastLoadService::ReadFastLoadPtr(nsIObjectInputStream* aInputStream,
477 nsISupports* *aPtrAddr)
479 // nsFastLoadPtrs self-construct to null, so if we have a non-null value
480 // in our inout parameter, we must have been read already, alright!
481 if (*aPtrAddr)
482 return NS_OK;
484 nsresult rv;
485 PRUint32 nextOffset;
486 nsAutoLock lock(mLock);
488 rv = aInputStream->Read32(&nextOffset);
489 if (NS_FAILED(rv))
490 return rv;
492 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(aInputStream));
493 if (!seekable)
494 return NS_ERROR_FAILURE;
496 PRInt64 thisOffset;
497 rv = seekable->Tell(&thisOffset);
498 if (NS_FAILED(rv))
499 return rv;
501 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, nextOffset);
502 if (NS_FAILED(rv))
503 return rv;
505 if (!mFastLoadPtrMap) {
506 mFastLoadPtrMap = PL_NewDHashTable(PL_DHashGetStubOps(), this,
507 sizeof(nsFastLoadPtrEntry),
508 PL_DHASH_MIN_SIZE);
509 if (!mFastLoadPtrMap)
510 return NS_ERROR_OUT_OF_MEMORY;
513 nsFastLoadPtrEntry* entry =
514 static_cast<nsFastLoadPtrEntry*>
515 (PL_DHashTableOperate(mFastLoadPtrMap, aPtrAddr,
516 PL_DHASH_ADD));
517 NS_ASSERTION(entry->mPtrAddr == nsnull, "duplicate nsFastLoadPtr?!");
519 entry->mPtrAddr = aPtrAddr;
521 LL_L2UI(entry->mOffset, thisOffset);
522 return NS_OK;
525 NS_IMETHODIMP
526 nsFastLoadService::WriteFastLoadPtr(nsIObjectOutputStream* aOutputStream,
527 nsISupports* aObject)
529 NS_ASSERTION(aObject != nsnull, "writing an unread nsFastLoadPtr?!");
530 if (!aObject)
531 return NS_ERROR_UNEXPECTED;
533 nsresult rv;
534 nsAutoLock lock(mLock); // serialize writes to aOutputStream
536 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(aOutputStream));
537 if (!seekable)
538 return NS_ERROR_FAILURE;
540 PRInt64 saveOffset;
541 rv = seekable->Tell(&saveOffset);
542 if (NS_FAILED(rv))
543 return rv;
545 rv = aOutputStream->Write32(0); // nextOffset placeholder
546 if (NS_FAILED(rv))
547 return rv;
549 rv = aOutputStream->WriteObject(aObject, PR_TRUE);
550 if (NS_FAILED(rv))
551 return rv;
553 PRInt64 nextOffset;
554 rv = seekable->Tell(&nextOffset);
555 if (NS_FAILED(rv))
556 return rv;
558 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
559 if (NS_FAILED(rv))
560 return rv;
562 rv = aOutputStream->Write32(nextOffset);
563 if (NS_FAILED(rv))
564 return rv;
566 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, nextOffset);
567 if (NS_FAILED(rv))
568 return rv;
570 return NS_OK;