Back to index

lightning-sunbird  0.9+nobinonly
nsFastLoadService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla FastLoad code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Brendan Eich <brendan@mozilla.org> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "prtypes.h"
00040 #include "prio.h"
00041 #include "prtime.h"
00042 #include "pldhash.h"
00043 
00044 #include "nsAppDirectoryServiceDefs.h"
00045 #include "nsAutoLock.h"
00046 #include "nsCOMPtr.h"
00047 #include "nsFastLoadFile.h"
00048 #include "nsFastLoadPtr.h"
00049 #include "nsFastLoadService.h"
00050 #include "nsString.h"
00051 
00052 #include "nsIComponentManager.h"
00053 #include "nsIEnumerator.h"
00054 #include "nsIFastLoadFileControl.h"
00055 #include "nsIFile.h"
00056 #include "nsIObjectInputStream.h"
00057 #include "nsIObjectOutputStream.h"
00058 #include "nsISeekableStream.h"
00059 #include "nsISupports.h"
00060 
00061 PR_IMPLEMENT_DATA(nsIFastLoadService*) gFastLoadService_ = nsnull;
00062 
00063 NS_IMPL_THREADSAFE_ISUPPORTS1(nsFastLoadService, nsIFastLoadService)
00064 
00065 nsFastLoadService::nsFastLoadService()
00066   : mLock(nsnull),
00067     mFastLoadPtrMap(nsnull),
00068     mDirection(0)
00069 {
00070     NS_ASSERTION(gFastLoadService_ == nsnull, "double FastLoadService init?");
00071     gFastLoadService_ = this;
00072 }
00073 
00074 nsFastLoadService::~nsFastLoadService()
00075 {
00076     gFastLoadService_ = nsnull;
00077 
00078     if (mInputStream)
00079         mInputStream->Close();
00080     if (mOutputStream)
00081         mOutputStream->Close();
00082 
00083     if (mFastLoadPtrMap)
00084         PL_DHashTableDestroy(mFastLoadPtrMap);
00085     if (mLock)
00086         PR_DestroyLock(mLock);
00087 }
00088 
00089 NS_IMETHODIMP
00090 nsFastLoadService::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00091 {
00092     *aResult = nsnull;
00093     if (aOuter)
00094         return NS_ERROR_NO_AGGREGATION;
00095 
00096     nsFastLoadService* fastLoadService = new nsFastLoadService();
00097     if (!fastLoadService)
00098         return NS_ERROR_OUT_OF_MEMORY;
00099 
00100     fastLoadService->mLock = PR_NewLock();
00101     if (!fastLoadService->mLock) {
00102         delete fastLoadService;
00103         return NS_ERROR_OUT_OF_MEMORY;
00104     }
00105 
00106     NS_ADDREF(fastLoadService);
00107     nsresult rv = fastLoadService->QueryInterface(aIID, aResult);
00108     NS_RELEASE(fastLoadService);
00109     return rv;
00110 }
00111 
00112 nsresult
00113 nsFastLoadService::NewFastLoadFile(const char* aBaseName, nsIFile* *aResult)
00114 {
00115     nsresult rv;
00116 
00117     // Try "ProfDS" first, so that we can get the profile directory
00118     // during startup.
00119     nsCOMPtr<nsIFile> profFile;
00120     rv = NS_GetSpecialDirectory("ProfDS",
00121                                 getter_AddRefs(profFile));
00122     if (NS_FAILED(rv)) {
00123         // The directory service doesn't know about "ProfDS", so just ask
00124         // for the regular profile directory key.  Note, however, that this
00125         // will fail if a profile hasn't yet been selected.
00126         rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
00127                                     getter_AddRefs(profFile));
00128         if (NS_FAILED(rv)) {
00129             return rv;
00130         }
00131     }
00132 
00133     // Try "ProfLDS" first, so that we can get the local profile directory
00134     // during startup.
00135     nsCOMPtr<nsIFile> file;
00136     rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(file));
00137     if (NS_FAILED(rv)) {
00138         // The directory service doesn't know about "ProfLDS", so just ask
00139         // for the regular local profile directory key.  Note, however, that
00140         // this will fail if a profile hasn't yet been selected.
00141         rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
00142                                     getter_AddRefs(file));
00143     }
00144     if (NS_FAILED(rv))
00145         file = profFile;
00146 
00147     PRBool sameDir;
00148     rv = file->Equals(profFile, &sameDir);
00149     if (NS_FAILED(rv))
00150         return rv;
00151 
00152     nsCAutoString name(aBaseName);
00153     name += PLATFORM_FASL_SUFFIX;
00154     rv = file->AppendNative(name);
00155     if (NS_FAILED(rv))
00156         return rv;
00157 
00158     if (!sameDir) {
00159         // Cleanup any pre-existing fastload files that may live in the profile
00160         // directory from previous versions of the code that didn't store them
00161         // in the profile temp directory.
00162         rv = profFile->AppendNative(name);
00163         if (NS_SUCCEEDED(rv))
00164             profFile->Remove(PR_FALSE);  // OK if this fails
00165     }
00166 
00167     *aResult = file;
00168     NS_ADDREF(*aResult);
00169     return NS_OK;
00170 }
00171 
00172 NS_IMETHODIMP
00173 nsFastLoadService::NewInputStream(nsIInputStream* aSrcStream,
00174                                   nsIObjectInputStream* *aResult)
00175 {
00176     nsAutoLock lock(mLock);
00177 
00178     nsCOMPtr<nsIObjectInputStream> stream;
00179     nsresult rv = NS_NewFastLoadFileReader(getter_AddRefs(stream), aSrcStream);
00180     if (NS_FAILED(rv))
00181         return rv;
00182 
00183     *aResult = stream;
00184     NS_ADDREF(*aResult);
00185     return NS_OK;
00186 }
00187 
00188 NS_IMETHODIMP
00189 nsFastLoadService::NewOutputStream(nsIOutputStream* aDestStream,
00190                                    nsIObjectOutputStream* *aResult)
00191 {
00192     nsAutoLock lock(mLock);
00193 
00194     return NS_NewFastLoadFileWriter(aResult, aDestStream, mFileIO);
00195 }
00196 
00197 NS_IMETHODIMP
00198 nsFastLoadService::GetInputStream(nsIObjectInputStream* *aResult)
00199 {
00200     NS_IF_ADDREF(*aResult = mInputStream);
00201     return NS_OK;
00202 }
00203 
00204 NS_IMETHODIMP
00205 nsFastLoadService::SetInputStream(nsIObjectInputStream* aStream)
00206 {
00207     nsAutoLock lock(mLock);
00208     mInputStream = aStream;
00209     return NS_OK;
00210 }
00211 
00212 NS_IMETHODIMP
00213 nsFastLoadService::GetOutputStream(nsIObjectOutputStream* *aResult)
00214 {
00215     NS_IF_ADDREF(*aResult = mOutputStream);
00216     return NS_OK;
00217 }
00218 
00219 NS_IMETHODIMP
00220 nsFastLoadService::SetOutputStream(nsIObjectOutputStream* aStream)
00221 {
00222     nsAutoLock lock(mLock);
00223     mOutputStream = aStream;
00224     return NS_OK;
00225 }
00226 
00227 NS_IMETHODIMP
00228 nsFastLoadService::GetFileIO(nsIFastLoadFileIO* *aResult)
00229 {
00230     NS_IF_ADDREF(*aResult = mFileIO);
00231     return NS_OK;
00232 }
00233 
00234 NS_IMETHODIMP
00235 nsFastLoadService::SetFileIO(nsIFastLoadFileIO* aFileIO)
00236 {
00237     nsAutoLock lock(mLock);
00238     mFileIO = aFileIO;
00239     return NS_OK;
00240 }
00241 
00242 NS_IMETHODIMP
00243 nsFastLoadService::GetDirection(PRInt32 *aResult)
00244 {
00245     *aResult = mDirection;
00246     return NS_OK;
00247 }
00248 
00249 NS_IMETHODIMP
00250 nsFastLoadService::HasMuxedDocument(const char* aURISpec, PRBool *aResult)
00251 {
00252     nsresult rv = NS_ERROR_NOT_AVAILABLE;
00253     nsCOMPtr<nsIFastLoadFileControl> control;
00254 
00255     *aResult = PR_FALSE;
00256     nsAutoLock lock(mLock);
00257 
00258     if (mInputStream) {
00259         control = do_QueryInterface(mInputStream);
00260         if (control)
00261             rv = control->HasMuxedDocument(aURISpec, aResult);
00262     }
00263 
00264     if (! *aResult && mOutputStream) {
00265         control = do_QueryInterface(mOutputStream);
00266         if (control)
00267             rv = control->HasMuxedDocument(aURISpec, aResult);
00268     }
00269 
00270     return rv;
00271 }
00272 
00273 NS_IMETHODIMP
00274 nsFastLoadService::StartMuxedDocument(nsISupports* aURI, const char* aURISpec,
00275                                       PRInt32 aDirectionFlags)
00276 {
00277     nsresult rv = NS_ERROR_NOT_AVAILABLE;
00278     nsCOMPtr<nsIFastLoadFileControl> control;
00279     nsAutoLock lock(mLock);
00280 
00281     // Try for an input stream first, in case aURISpec's data is multiplexed
00282     // in the current FastLoad file.
00283     if ((aDirectionFlags & NS_FASTLOAD_READ) && mInputStream) {
00284         control = do_QueryInterface(mInputStream);
00285         if (control) {
00286             // If aURISpec is not in the multiplex, control->StartMuxedDocument
00287             // will return NS_ERROR_NOT_AVAILABLE.
00288             rv = control->StartMuxedDocument(aURI, aURISpec);
00289             if (NS_SUCCEEDED(rv) || rv != NS_ERROR_NOT_AVAILABLE)
00290                 return rv;
00291 
00292             // Ok, aURISpec is not in the existing mux.  If we have no output
00293             // stream yet, wrap the reader with a FastLoad file updater.
00294             if (!mOutputStream && mFileIO) {
00295                 nsCOMPtr<nsIOutputStream> output;
00296                 rv = mFileIO->GetOutputStream(getter_AddRefs(output));
00297                 if (NS_FAILED(rv))
00298                     return rv;
00299 
00300                 // NB: mInputStream must be an nsFastLoadFileReader!
00301                 rv = NS_NewFastLoadFileUpdater(getter_AddRefs(mOutputStream),
00302                                                output,
00303                                                mInputStream);
00304                 if (NS_FAILED(rv))
00305                     return rv;
00306             }
00307 
00308             if (aDirectionFlags == NS_FASTLOAD_READ) {
00309                 // Tell our caller to re-start multiplexing, rather than attempt
00310                 // to select and deserialize now.
00311                 return NS_ERROR_NOT_AVAILABLE;
00312             }
00313         }
00314     }
00315 
00316     if ((aDirectionFlags & NS_FASTLOAD_WRITE) && mOutputStream) {
00317         control = do_QueryInterface(mOutputStream);
00318         if (control)
00319             rv = control->StartMuxedDocument(aURI, aURISpec);
00320     }
00321     return rv;
00322 }
00323 
00324 NS_IMETHODIMP
00325 nsFastLoadService::SelectMuxedDocument(nsISupports* aURI, nsISupports** aResult)
00326 {
00327     nsresult rv = NS_ERROR_NOT_AVAILABLE;
00328     nsCOMPtr<nsIFastLoadFileControl> control;
00329     nsAutoLock lock(mLock);
00330 
00331     // Try to select the reader, if any; then only if the URI was not in the
00332     // file already, select the writer/updater.
00333     if (mInputStream) {
00334         control = do_QueryInterface(mInputStream);
00335         if (control) {
00336             rv = control->SelectMuxedDocument(aURI, aResult);
00337             if (NS_SUCCEEDED(rv))
00338                 mDirection = NS_FASTLOAD_READ;
00339         }
00340     }
00341 
00342     if (rv == NS_ERROR_NOT_AVAILABLE && mOutputStream) {
00343         control = do_QueryInterface(mOutputStream);
00344         if (control) {
00345             rv = control->SelectMuxedDocument(aURI, aResult);
00346             if (NS_SUCCEEDED(rv))
00347                 mDirection = NS_FASTLOAD_WRITE;
00348         }
00349     }
00350 
00351     return rv;
00352 }
00353 
00354 NS_IMETHODIMP
00355 nsFastLoadService::EndMuxedDocument(nsISupports* aURI)
00356 {
00357     nsresult rv = NS_ERROR_NOT_AVAILABLE;
00358     nsCOMPtr<nsIFastLoadFileControl> control;
00359     nsAutoLock lock(mLock);
00360 
00361     // Try to end the document identified by aURI in the reader, if any; then
00362     // only if the URI was not in the file already, end the writer/updater.
00363     if (mInputStream) {
00364         control = do_QueryInterface(mInputStream);
00365         if (control)
00366             rv = control->EndMuxedDocument(aURI);
00367     }
00368 
00369     if (rv == NS_ERROR_NOT_AVAILABLE && mOutputStream) {
00370         control = do_QueryInterface(mOutputStream);
00371         if (control)
00372             rv = control->EndMuxedDocument(aURI);
00373     }
00374 
00375     mDirection = 0;
00376     return rv;
00377 }
00378 
00379 NS_IMETHODIMP
00380 nsFastLoadService::AddDependency(nsIFile* aFile)
00381 {
00382     nsAutoLock lock(mLock);
00383 
00384     nsCOMPtr<nsIFastLoadWriteControl> control(do_QueryInterface(mOutputStream));
00385     if (!control)
00386         return NS_ERROR_NOT_AVAILABLE;
00387 
00388     return control->AddDependency(aFile);
00389 }
00390 
00391 NS_IMETHODIMP
00392 nsFastLoadService::ComputeChecksum(nsIFile* aFile,
00393                                    nsIFastLoadReadControl* aControl,
00394                                    PRUint32 *aChecksum)
00395 {
00396     nsCAutoString path;
00397     nsresult rv = aFile->GetNativePath(path);
00398     if (NS_FAILED(rv))
00399         return rv;
00400 
00401     nsCStringKey key(path);
00402     PRUint32 checksum = NS_PTR_TO_INT32(mChecksumTable.Get(&key));
00403     if (checksum) {
00404         *aChecksum = checksum;
00405         return NS_OK;
00406     }
00407 
00408     rv = aControl->ComputeChecksum(&checksum);
00409     if (NS_FAILED(rv))
00410         return rv;
00411 
00412     mChecksumTable.Put(&key, NS_INT32_TO_PTR(checksum));
00413     *aChecksum = checksum;
00414     return NS_OK;
00415 }
00416 
00417 NS_IMETHODIMP
00418 nsFastLoadService::CacheChecksum(nsIFile* aFile, nsIObjectOutputStream *aStream)
00419 {
00420     nsCOMPtr<nsIFastLoadFileControl> control(do_QueryInterface(aStream));
00421     if (!control)
00422         return NS_ERROR_FAILURE;
00423 
00424     PRUint32 checksum;
00425     nsresult rv = control->GetChecksum(&checksum);
00426     if (NS_FAILED(rv))
00427         return rv;
00428 
00429     nsCAutoString path;
00430     rv = aFile->GetNativePath(path);
00431     if (NS_FAILED(rv))
00432         return rv;
00433 
00434     nsCStringKey key(path);
00435     mChecksumTable.Put(&key, NS_INT32_TO_PTR(checksum));
00436     return NS_OK;
00437 }
00438 
00439 struct nsFastLoadPtrEntry : public PLDHashEntryHdr {
00440     nsISupports** mPtrAddr;     // key, must come first for PL_DHashGetStubOps
00441     PRUint32      mOffset;
00442 };
00443 
00444 NS_IMETHODIMP
00445 nsFastLoadService::GetFastLoadReferent(nsISupports* *aPtrAddr)
00446 {
00447     NS_ASSERTION(*aPtrAddr == nsnull,
00448                  "aPtrAddr doesn't point to null nsFastLoadPtr<T>::mRawAddr?");
00449 
00450     nsAutoLock lock(mLock);
00451     if (!mFastLoadPtrMap || !mInputStream)
00452         return NS_OK;
00453 
00454     nsFastLoadPtrEntry* entry =
00455         NS_STATIC_CAST(nsFastLoadPtrEntry*,
00456                        PL_DHashTableOperate(mFastLoadPtrMap, aPtrAddr,
00457                                             PL_DHASH_LOOKUP));
00458     if (PL_DHASH_ENTRY_IS_FREE(entry))
00459         return NS_OK;
00460 
00461     nsresult rv;
00462     nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(mInputStream));
00463 
00464     rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, entry->mOffset);
00465     if (NS_FAILED(rv))
00466         return rv;
00467 
00468     rv = mInputStream->ReadObject(PR_TRUE, aPtrAddr);
00469     if (NS_FAILED(rv))
00470         return rv;
00471 
00472     // Shrink the table if half the entries are removed sentinels.
00473     PRUint32 size = PL_DHASH_TABLE_SIZE(mFastLoadPtrMap);
00474     if (mFastLoadPtrMap->removedCount >= (size >> 2))
00475         PL_DHashTableOperate(mFastLoadPtrMap, entry, PL_DHASH_REMOVE);
00476     else
00477         PL_DHashTableRawRemove(mFastLoadPtrMap, entry);
00478 
00479     return NS_OK;
00480 }
00481 
00482 NS_IMETHODIMP
00483 nsFastLoadService::ReadFastLoadPtr(nsIObjectInputStream* aInputStream,
00484                                    nsISupports* *aPtrAddr)
00485 {
00486     // nsFastLoadPtrs self-construct to null, so if we have a non-null value
00487     // in our inout parameter, we must have been read already, alright!
00488     if (*aPtrAddr)
00489         return NS_OK;
00490 
00491     nsresult rv;
00492     PRUint32 nextOffset;
00493     nsAutoLock lock(mLock);
00494 
00495     rv = aInputStream->Read32(&nextOffset);
00496     if (NS_FAILED(rv))
00497         return rv;
00498 
00499     nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(aInputStream));
00500     if (!seekable)
00501         return NS_ERROR_FAILURE;
00502 
00503     PRInt64 thisOffset;
00504     rv = seekable->Tell(&thisOffset);
00505     if (NS_FAILED(rv))
00506         return rv;
00507 
00508     rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, nextOffset);
00509     if (NS_FAILED(rv))
00510         return rv;
00511 
00512     if (!mFastLoadPtrMap) {
00513         mFastLoadPtrMap = PL_NewDHashTable(PL_DHashGetStubOps(), this,
00514                                            sizeof(nsFastLoadPtrEntry),
00515                                            PL_DHASH_MIN_SIZE);
00516         if (!mFastLoadPtrMap)
00517             return NS_ERROR_OUT_OF_MEMORY;
00518     }
00519 
00520     nsFastLoadPtrEntry* entry =
00521         NS_STATIC_CAST(nsFastLoadPtrEntry*,
00522                        PL_DHashTableOperate(mFastLoadPtrMap, aPtrAddr,
00523                                             PL_DHASH_ADD));
00524     NS_ASSERTION(entry->mPtrAddr == nsnull, "duplicate nsFastLoadPtr?!");
00525 
00526     entry->mPtrAddr = aPtrAddr;
00527 
00528     LL_L2UI(entry->mOffset, thisOffset);
00529     return NS_OK;
00530 }
00531 
00532 NS_IMETHODIMP
00533 nsFastLoadService::WriteFastLoadPtr(nsIObjectOutputStream* aOutputStream,
00534                                     nsISupports* aObject)
00535 {
00536     NS_ASSERTION(aObject != nsnull, "writing an unread nsFastLoadPtr?!");
00537     if (!aObject)
00538         return NS_ERROR_UNEXPECTED;
00539 
00540     nsresult rv;
00541     nsAutoLock lock(mLock);     // serialize writes to aOutputStream
00542 
00543     nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(aOutputStream));
00544     if (!seekable)
00545         return NS_ERROR_FAILURE;
00546 
00547     PRInt64 saveOffset;
00548     rv = seekable->Tell(&saveOffset);
00549     if (NS_FAILED(rv))
00550         return rv;
00551 
00552     rv = aOutputStream->Write32(0);       // nextOffset placeholder
00553     if (NS_FAILED(rv))
00554         return rv;
00555 
00556     rv = aOutputStream->WriteObject(aObject, PR_TRUE);
00557     if (NS_FAILED(rv))
00558         return rv;
00559 
00560     PRInt64 nextOffset;
00561     rv = seekable->Tell(&nextOffset);
00562     if (NS_FAILED(rv))
00563         return rv;
00564 
00565     rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, saveOffset);
00566     if (NS_FAILED(rv))
00567         return rv;
00568 
00569     rv = aOutputStream->Write32(nextOffset);
00570     if (NS_FAILED(rv))
00571         return rv;
00572 
00573     rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, nextOffset);
00574     if (NS_FAILED(rv))
00575         return rv;
00576 
00577     return NS_OK;
00578 }