/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* * nsDiskModule * * Gagan Saksena 02/02/98 * */ #include "prtypes.h" #include "prmem.h" #include "plstr.h" #include "prlog.h" #include "prclist.h" #include "prio.h" #include "nsDiskModule.h" #include "nsCacheObject.h" #include "nsCacheManager.h" #include "nsFileStream.h" #include "nsCachePref.h" #include "mcom_db.h" #define ENSURE_INIT \ if (!m_pDB) \ { \ nsDiskModule* pThis = (nsDiskModule*) this; \ PRBool res = pThis->InitDB(); \ PR_ASSERT(res); \ } struct recentlyUsedObject { PRCList link; nsCacheObject* cacheObject; }; /* Find pointer to recentlyUsedObject struct * from the list linkaged embedded in it */ #define OBJECT_PTR(_link) \ ((recentlyUsedObject*) ((char*) (_link) - offsetof(recentlyUsedObject,link))) //File static list of recently used cache objects static PRCList g_RecentlyUsedList; static char* g_FullFilename=0; const static int MAX_FILENAME_LEN = 512; // Every time we cleanup we cleanup to 75% of the max available size. // This should ideally change to a more const number than a percentage. // Since someone may have a bigger cache size, so we don't really want to // be cleaning up 5 megs if some one has a 20 megs cache size. // I will revisit this issue once the basic architecture is up and running. #define CLEANUP_FACTOR 0.75 char* FullFilename(const char* i_Filename); // // Constructor: nsDiskModule // nsDiskModule::nsDiskModule(const PRUint32 size): nsCacheModule(size), m_pDB(0) { PR_INIT_CLIST(&g_RecentlyUsedList); } nsDiskModule::~nsDiskModule() { if (m_pDB) { (*m_pDB->sync)(m_pDB, 0); (*m_pDB->close)(m_pDB); m_pDB = 0; } /* Clean up the recently used list */ recentlyUsedObject* obj; while (!PR_CLIST_IS_EMPTY(&g_RecentlyUsedList)) { obj = (recentlyUsedObject*) PR_LIST_HEAD(&g_RecentlyUsedList); if (obj->cacheObject) delete obj->cacheObject; PR_REMOVE_LINK(&obj->link); } PR_ASSERT(PR_CLIST_IS_EMPTY(&g_RecentlyUsedList)); if (g_FullFilename) { delete[] g_FullFilename; g_FullFilename = 0; } } PRBool nsDiskModule::AddObject(nsCacheObject* io_pObject) { ENSURE_INIT; if (!m_pDB || !io_pObject) { // Set some error state TODO return PR_FALSE; } if (io_pObject->Address()) { MonitorLocker ml(this); // TODO optimize these further- make static - Gagan DBT* key = PR_NEW(DBT); DBT* data = PR_NEW(DBT); io_pObject->Module(nsCacheManager::DISK); key->data = (void*)io_pObject->Address(); /* Later on change this to include post data- io_pObject->KeyData() */ key->size = PL_strlen(io_pObject->Address()); data->data = io_pObject->Info(); data->size = io_pObject->InfoSize(); int status = (*m_pDB->put)(m_pDB, key, data, 0); if (status == 0) { // if (m_Sync == EVERYTIME) status = (*m_pDB->sync)(m_pDB, 0); m_Entries++; m_SizeInUse += io_pObject->Size(); } PR_Free(key); PR_Free(data); return (status == 0); } return PR_FALSE; } PRBool nsDiskModule::Contains(nsCacheObject* io_pObject) const { ENSURE_INIT; if (!m_pDB || !io_pObject) return PR_FALSE; nsCacheObject* pTemp = GetObject(io_pObject->Address()); if (pTemp) { PR_ASSERT(io_pObject == pTemp); // until I do a copyFrom function return PR_TRUE; } return PR_FALSE; } PRBool nsDiskModule::Contains(const char* i_url) const { ENSURE_INIT; if (!m_pDB || !i_url || !*i_url) return PR_FALSE; DBT key, data; key.data = (void*) i_url; key.size = PL_strlen(i_url); int status = (*m_pDB->get)(m_pDB, &key, &data, 0); return (status == 0); } void nsDiskModule::GarbageCollect(void) { MonitorLocker ml(this); ReduceSizeTo((PRUint32)(CLEANUP_FACTOR*m_Size)); // TODO // if the recentlyusedlist has grown too big, trim some objects from there as well } nsCacheObject* nsDiskModule::GetObject(const PRUint32 i_index) const { ENSURE_INIT; if (!m_pDB) return 0; //todo return 0; } nsCacheObject* nsDiskModule::GetObject(const char* i_url) const { ENSURE_INIT; MonitorLocker ml((nsDiskModule*)this); if (!m_pDB || !i_url || !*i_url) return 0; /* Check amongst recently used objects */ recentlyUsedObject* obj; PRCList* list = &g_RecentlyUsedList; if (!PR_CLIST_IS_EMPTY(&g_RecentlyUsedList)) { list = g_RecentlyUsedList.next; nsCacheObject* pObj; while (list != &g_RecentlyUsedList) { obj = OBJECT_PTR(list); pObj = obj->cacheObject; if (0 == PL_strcasecmp(i_url, pObj->Address())) //todo also validate return pObj; list = list->next; } } DBT key, data; key.data = (void*) i_url; key.size = PL_strlen(i_url); if (0 == (*m_pDB->get)(m_pDB, &key, &data, 0)) { nsCacheObject* pTemp = new nsCacheObject(); pTemp->Info(data.data); recentlyUsedObject* pNode = PR_NEWZAP(recentlyUsedObject); PR_APPEND_LINK(&pNode->link, &g_RecentlyUsedList); pNode->cacheObject = pTemp; return pTemp; } return 0; } nsStream* nsDiskModule::GetStreamFor(const nsCacheObject* i_pObject) { ENSURE_INIT; MonitorLocker ml(this); if (i_pObject) { if (Contains((nsCacheObject*)i_pObject)) { nsStream* pStream = i_pObject->Stream(); if (pStream) return pStream; } PR_ASSERT(*i_pObject->Filename()); char* fullname = FullFilename(i_pObject->Filename()); // Set up a new stream for this object PRFileDesc* pFD = PR_Open( fullname ? fullname : i_pObject->Filename(), PR_CREATE_FILE | PR_RDWR, 600);// Read and write by owner only if (pFD) { return new nsFileStream(pFD); } return 0; } return 0; } PRBool nsDiskModule::InitDB(void) { MonitorLocker ml(this); if (m_pDB) return PR_TRUE; HASHINFO hash_info = { 16*1024, /* bucket size */ 0, /* fill factor */ 0, /* number of elements */ 0, /* bytes to cache */ 0, /* hash function */ 0}; /* byte order */ m_pDB = dbopen( nsCachePref::GetInstance()->DiskCacheDBFilename(), O_RDWR | O_CREAT, 0600, DB_HASH, &hash_info); if (!m_pDB) return PR_FALSE; /* Open and read in the number of existing entries */ m_Entries = 0; int status; DBT key, data; if(!(status = (*m_pDB->seq)(m_pDB, &key, &data, R_FIRST))) { while(!(status = (*m_pDB->seq) (m_pDB, &key, &data, R_NEXT))) { /* Also validate the corresponding file here */ //TODO m_Entries++; } } if (status < 0) return PR_FALSE; return PR_TRUE; } PRBool nsDiskModule::ReduceSizeTo(const PRUint32 i_NewSize) { return PR_FALSE; //TODO Fix this. PRInt32 needToFree = m_SizeInUse - i_NewSize; if (needToFree > 0) { PRUint32 avg = AverageSize(); if (avg==0) return PR_FALSE; PRUint32 nObjectsToFree = needToFree/AverageSize(); if (nObjectsToFree < 1) nObjectsToFree = 1; //TODO /* DBT key, data; key.data = (void*) i_url; key.size = PL_strlen(i_url); if (0 == (*m_pDB->get)(m_pDB, &key, &data, 0)) { nsCacheObject* pTemp = new nsCacheObject(); pTemp->Info(data.data); recentlyUsedObject* pNode = PR_NEWZAP(recentlyUsedObject); PR_APPEND_LINK(&pNode->link, &g_RecentlyUsedList); pNode->cacheObject = pTemp; return pTemp; } */ return PR_TRUE; } return PR_FALSE; } PRBool nsDiskModule::Remove(const char* i_url) { ENSURE_INIT; nsCacheObject* pObject = GetObject(i_url); if (pObject) { return Remove(pObject); } return PR_FALSE; } PRBool nsDiskModule::Remove(nsCacheObject* pObject) { MonitorLocker ml(this); if (!pObject) return PR_FALSE; //PR_ASSERT(Contains(pObject); //Remove it from the index DBT key; key.data = (void*) pObject->Address(); key.size = PL_strlen(pObject->Address()); if (0 == (*m_pDB->del)(m_pDB, &key, 0)) { --m_Entries; m_SizeInUse -= pObject->Size(); } //Remove it from the recently used list //TODO till done I am not removing it from here //Remove it from the disk //Remove file FullFilename(pObject->Filename()); //Finally delete it //delete pObject; //Currently recentlyused will delete these. //pObject = 0; return PR_TRUE; } PRBool nsDiskModule::Remove(const PRUint32 i_index) { //This will probably go away. ENSURE_INIT; //TODO // Also remove the file corresponding to this item. return PR_FALSE; } PRBool nsDiskModule::Revalidate(void) { ENSURE_INIT; //TODO - This will add a dependency on HTTP lib return PR_FALSE; } void nsDiskModule::SetSize(const PRUint32 i_Size) { MonitorLocker ml(this); m_Size = i_Size; if (m_Size >0) { ReduceSizeTo((PRUint32)(CLEANUP_FACTOR*m_Size)); } else { RemoveAll(); } } char* FullFilename(const char* i_Filename) { if (0 == g_FullFilename) { g_FullFilename = new char[MAX_FILENAME_LEN]; *g_FullFilename = '\0'; if (0 == g_FullFilename) return 0; } PL_strcpy(g_FullFilename, nsCachePref::GetInstance()->DiskCacheFolder()); PL_strcat(g_FullFilename, i_Filename); return g_FullFilename; } #undef ENSURE_INIT