/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2003 Miranda ICQ/IM project, all portions of this codebase are copyrighted to the people listed in contributors.txt. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "commonheaders.h" int CDb3x::FindSectionForOffset(const DWORD ofs) { for (int i = 0; i < CACHESECTIONCOUNT; i++) if (ofs >= cacheSectionInfo[i].ofsBase && ofs<cacheSectionInfo[i].ofsBase + CACHESECTIONSIZE) return i; return -1; } int CDb3x::FindLRUSection(void) { int lru = 0; DWORD lowestLastUse = cacheSectionInfo[0].lastUsed; for (int i = 1; i < CACHESECTIONCOUNT; i++) if (cacheSectionInfo[i].lastUsed < lowestLastUse) { lru = i; lowestLastUse = cacheSectionInfo[i].lastUsed; } return lru; } void CDb3x::LoadSection(const int i,DWORD ofs) { cacheSectionInfo[i].ofsBase = ofs - ofs%CACHESECTIONSIZE; log1("readsect %08x",ofs); SetFilePointer(m_hDbFile,cacheSectionInfo[i].ofsBase,NULL,FILE_BEGIN); ReadFile(m_hDbFile,m_pDbCache+i*CACHESECTIONSIZE,CACHESECTIONSIZE,&ofs,NULL); } void CDb3x::MoveSection(int *sectId,int dest) { CopyMemory(m_pDbCache+dest*CACHESECTIONSIZE,m_pDbCache+(*sectId)*CACHESECTIONSIZE,CACHESECTIONSIZE); cacheSectionInfo[dest].ofsBase = cacheSectionInfo[*sectId].ofsBase; *sectId = dest; } //we are assumed to be in a mutex here PBYTE CDb3x::DBRead(DWORD ofs,int bytesRequired,int *bytesAvail) { int part1sect = FindSectionForOffset(ofs); if (ofs%CACHESECTIONSIZE+bytesRequired<CACHESECTIONSIZE) { //only one section required if (part1sect == -1) { part1sect = FindLRUSection(); LoadSection(part1sect,ofs); } cacheSectionInfo[part1sect].lastUsed = ++m_lastUseCounter; if (bytesAvail!= NULL) *bytesAvail = cacheSectionInfo[part1sect].ofsBase+CACHESECTIONSIZE-ofs; return m_pDbCache+part1sect*CACHESECTIONSIZE+(ofs-cacheSectionInfo[part1sect].ofsBase); } //two sections are required int part2sect = FindSectionForOffset(ofs+CACHESECTIONSIZE); if (part1sect != -1) { if (part2sect == -1) { //first part in cache, but not second part if (part1sect == CACHESECTIONCOUNT-1) MoveSection(&part1sect,0); LoadSection(part1sect+1,ofs+CACHESECTIONSIZE); } else if (part2sect!= part1sect+1) { //both parts are in cache, but not already consecutive if (part1sect == CACHESECTIONCOUNT-1) { //first part is at end, move to before second part if (part2sect == 0) //second part is at start: need to move both MoveSection(&part2sect,1); MoveSection(&part1sect,part2sect-1); } else //move second part to after first part MoveSection(&part2sect,part1sect+1); } } else { if (part2sect == -1) { //neither section is in cache part1sect = 0; part2sect = 1; LoadSection(part1sect,ofs); LoadSection(part2sect,ofs+CACHESECTIONSIZE); } else { //part 2 is in cache, but not part 1 if (part2sect == 0) MoveSection(&part2sect,1); part1sect = part2sect-1; LoadSection(part1sect,ofs); } } //both sections are now consecutive, starting at part1sect cacheSectionInfo[part1sect].lastUsed = ++m_lastUseCounter; cacheSectionInfo[part1sect+1].lastUsed = ++m_lastUseCounter; if (bytesAvail!= NULL) *bytesAvail = cacheSectionInfo[part1sect+1].ofsBase+CACHESECTIONSIZE-ofs; return m_pDbCache+part1sect*CACHESECTIONSIZE+(ofs-cacheSectionInfo[part1sect].ofsBase); } //we are assumed to be in a mutex here void CDb3x::DBWrite(DWORD ofs,PVOID pData,int bytes) { //write direct, and rely on Windows' write caching log2("write %d@%08x", bytes, ofs); SetFilePointer(m_hDbFile, ofs, NULL, FILE_BEGIN); DWORD bytesWritten; if ( WriteFile(m_hDbFile, pData, bytes, &bytesWritten, NULL) == 0) DatabaseCorruption( _T("%s (Write error)")); logg(); //check if any of the cache sections contain this bit for(int i = 0; i < CACHESECTIONCOUNT; i++) { if (ofs+bytes >= cacheSectionInfo[i].ofsBase && ofs<cacheSectionInfo[i].ofsBase+CACHESECTIONSIZE) { if (ofs<cacheSectionInfo[i].ofsBase) { //don't start at beginning if (ofs+bytes >= cacheSectionInfo[i].ofsBase+CACHESECTIONSIZE) //don't finish at end CopyMemory(m_pDbCache+i*CACHESECTIONSIZE,(PBYTE)pData+cacheSectionInfo[i].ofsBase-ofs,CACHESECTIONSIZE); else CopyMemory(m_pDbCache+i*CACHESECTIONSIZE,(PBYTE)pData+cacheSectionInfo[i].ofsBase-ofs,bytes-(cacheSectionInfo[i].ofsBase-ofs)); } else { //start at beginning if (ofs+bytes >= cacheSectionInfo[i].ofsBase+CACHESECTIONSIZE) //don't finish at end CopyMemory(m_pDbCache+i*CACHESECTIONSIZE+ofs-cacheSectionInfo[i].ofsBase,pData,cacheSectionInfo[i].ofsBase+CACHESECTIONSIZE-ofs); else CopyMemory(m_pDbCache+i*CACHESECTIONSIZE+ofs-cacheSectionInfo[i].ofsBase,pData,bytes); } } } } void CDb3x::DBMoveChunk(DWORD ofsDest,DWORD ofsSource,int bytes) { DWORD bytesRead; PBYTE buf; log3("move %d %08x->%08x",bytes,ofsSource,ofsDest); buf = (PBYTE)mir_alloc(bytes); SetFilePointer(m_hDbFile,ofsSource,NULL,FILE_BEGIN); ReadFile(m_hDbFile,buf,bytes,&bytesRead,NULL); DBWrite(ofsDest,buf,bytes); mir_free(buf); logg(); } static VOID CALLBACK DoBufferFlushTimerProc(HWND hwnd,UINT message,UINT_PTR idEvent,DWORD dwTime) { for (int i=0; i < g_Dbs.getCount(); i++) { CDb3x* db = g_Dbs[i]; KillTimer(NULL, db->m_flushBuffersTimerId); log0("tflush1"); FlushFileBuffers(db->getFile()); log0("tflush2"); } } void CDb3x::DBFlush(int setting) { if (!setting) { log0("nflush1"); if (m_safetyMode) FlushFileBuffers(m_hDbFile); log0("nflush2"); return; } KillTimer(NULL,m_flushBuffersTimerId); m_flushBuffersTimerId = SetTimer(NULL,m_flushBuffersTimerId,50,DoBufferFlushTimerProc); } void CDb3x::DBFill(DWORD ofs,int bytes) { } int CDb3x::InitCache(void) { m_pDbCache = (PBYTE)mir_alloc(CACHESECTIONSIZE*CACHESECTIONCOUNT); m_lastUseCounter = CACHESECTIONCOUNT; for(int i = 0; i < CACHESECTIONCOUNT; i++) { cacheSectionInfo[i].ofsBase = 0; cacheSectionInfo[i].lastUsed = i; SetFilePointer(m_hDbFile,cacheSectionInfo[i].ofsBase,NULL,FILE_BEGIN); DWORD bytesRead; ReadFile(m_hDbFile,m_pDbCache+i*CACHESECTIONSIZE,CACHESECTIONSIZE,&bytesRead,NULL); } return 0; }