Reduce C style casts
[TortoiseGit.git] / src / TortoiseProc / GitLogCache.cpp
blob8b8887df9a289d281b7dd1fed33aa51e12535034
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "GitLogCache.h"
21 #include "registry.h"
23 int static Compare(const void *p1, const void*p2)
25 return memcmp(p1, p2, GIT_HASH_SIZE);
28 CLogCache::CLogCache()
30 m_IndexFile = INVALID_HANDLE_VALUE;
31 m_IndexFileMap = INVALID_HANDLE_VALUE;
32 m_pCacheIndex = nullptr;
34 m_DataFile = INVALID_HANDLE_VALUE;
35 m_DataFileMap = INVALID_HANDLE_VALUE;
36 m_pCacheData = nullptr;
37 m_DataFileLength = 0;
38 m_bEnabled = CRegDWORD(_T("Software\\TortoiseGit\\EnableLogCache"), TRUE);
41 void CLogCache::CloseDataHandles()
43 if(m_pCacheData)
45 UnmapViewOfFile(m_pCacheData);
46 m_pCacheData = nullptr;
48 if (m_DataFileMap != INVALID_HANDLE_VALUE)
50 CloseHandle(m_DataFileMap);
51 m_DataFileMap=INVALID_HANDLE_VALUE;
53 if (m_DataFile != INVALID_HANDLE_VALUE)
55 CloseHandle(m_DataFile);
56 m_DataFile = INVALID_HANDLE_VALUE;
60 void CLogCache::CloseIndexHandles()
62 if(m_pCacheIndex)
64 UnmapViewOfFile(m_pCacheIndex);
65 m_pCacheIndex = nullptr;
68 if (m_IndexFileMap != INVALID_HANDLE_VALUE)
70 CloseHandle(m_IndexFileMap);
71 m_IndexFileMap = INVALID_HANDLE_VALUE;
74 //this->m_IndexFile.Close();
75 //this->m_DataFile.Close();
76 if (m_IndexFile != INVALID_HANDLE_VALUE)
78 CloseHandle(m_IndexFile);
79 m_IndexFile=INVALID_HANDLE_VALUE;
82 CLogCache::~CLogCache()
84 CloseIndexHandles();
85 CloseDataHandles();
88 GitRevLoglist* CLogCache::GetCacheData(const CGitHash& hash)
90 m_HashMap[hash].m_CommitHash=hash;
91 return &m_HashMap[hash];
94 ULONGLONG CLogCache::GetOffset(const CGitHash& hash, SLogCacheIndexFile* pData)
96 if (!pData)
97 pData = m_pCacheIndex;
99 if (!pData)
100 return 0;
102 SLogCacheIndexItem* p=reinterpret_cast<SLogCacheIndexItem*>(bsearch(hash.m_hash, pData->m_Item,
103 pData->m_Header.m_ItemCount,
104 sizeof(SLogCacheIndexItem),
105 Compare));
107 if(p)
108 return p->m_Offset;
109 else
110 return 0;
113 int CLogCache::FetchCacheIndex(CString GitDir)
115 if (!m_bEnabled)
116 return 0;
118 if (!GitAdminDir::GetAdminDirPath(GitDir, m_GitDir))
119 return -1;
121 int ret = -1;
124 if( m_IndexFile == INVALID_HANDLE_VALUE)
126 CString file = m_GitDir + INDEX_FILE_NAME;
127 m_IndexFile = CreateFile(file,
128 GENERIC_READ,
129 FILE_SHARE_READ | FILE_SHARE_DELETE,
130 nullptr,
131 OPEN_EXISTING,
132 FILE_ATTRIBUTE_NORMAL,
133 nullptr);
135 if( m_IndexFile == INVALID_HANDLE_VALUE)
136 break;
139 if( m_IndexFileMap == INVALID_HANDLE_VALUE)
141 m_IndexFileMap = CreateFileMapping(m_IndexFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
142 if( m_IndexFileMap == INVALID_HANDLE_VALUE)
143 break;
146 if (!m_pCacheIndex)
148 m_pCacheIndex = reinterpret_cast<SLogCacheIndexFile*>(MapViewOfFile(m_IndexFileMap, FILE_MAP_READ, 0, 0, 0));
149 if (!m_pCacheIndex)
150 break;
153 DWORD indexFileLength = GetFileSize(m_IndexFile, nullptr);
154 if (indexFileLength == INVALID_FILE_SIZE || indexFileLength < sizeof(SLogCacheIndexHeader))
155 break;
157 if( !CheckHeader(&m_pCacheIndex->m_Header))
158 break;
160 if (indexFileLength < sizeof(SLogCacheIndexHeader) + m_pCacheIndex->m_Header.m_ItemCount * sizeof(SLogCacheIndexItem))
161 break;
163 if( m_DataFile == INVALID_HANDLE_VALUE )
165 CString file = m_GitDir + DATA_FILE_NAME;
166 m_DataFile = CreateFile(file,
167 GENERIC_READ,
168 FILE_SHARE_READ | FILE_SHARE_DELETE,
169 nullptr,
170 OPEN_EXISTING,
171 FILE_ATTRIBUTE_NORMAL,
172 nullptr);
174 if(m_DataFile == INVALID_HANDLE_VALUE)
175 break;
178 if( m_DataFileMap == INVALID_HANDLE_VALUE)
180 m_DataFileMap = CreateFileMapping(m_DataFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
181 if( m_DataFileMap == INVALID_HANDLE_VALUE)
182 break;
184 m_DataFileLength = GetFileSize(m_DataFile, nullptr);
185 if (m_DataFileLength == INVALID_FILE_SIZE || m_DataFileLength < sizeof(SLogCacheDataFileHeader))
186 break;
188 if (!m_pCacheData)
190 m_pCacheData = (BYTE*)MapViewOfFile(m_DataFileMap,FILE_MAP_READ,0,0,0);
191 if (!m_pCacheData)
192 break;
195 if (!CheckHeader(reinterpret_cast<SLogCacheDataFileHeader*>(m_pCacheData)))
196 break;
198 if (m_DataFileLength < sizeof(SLogCacheDataFileHeader) + m_pCacheIndex->m_Header.m_ItemCount * sizeof(SLogCacheDataFileHeader))
199 break;
201 ret = 0;
202 }while(0);
204 if(ret)
206 CloseIndexHandles();
207 CloseDataHandles();
208 ::DeleteFile(m_GitDir + INDEX_FILE_NAME);
209 ::DeleteFile(m_GitDir + DATA_FILE_NAME);
211 return ret;
214 int CLogCache::SaveOneItem(const GitRevLoglist& Rev, LONG offset)
216 if(!Rev.m_IsDiffFiles)
217 return -1;
219 SetFilePointer(this->m_DataFile, offset,0, 0);
221 SLogCacheRevItemHeader header;
223 header.m_Magic=LOG_DATA_ITEM_MAGIC;
224 header.m_Version=LOG_INDEX_VERSION;
225 header.m_FileCount=Rev.m_Files.GetCount();
227 DWORD num;
229 if(!WriteFile(this->m_DataFile,&header, sizeof(header),&num,0))
230 return -1;
232 CString stat;
233 CString name,oldname;
234 for (int i = 0; i < Rev.m_Files.GetCount(); ++i)
236 SLogCacheRevFileHeader revfileheader;
237 revfileheader.m_Magic = LOG_DATA_FILE_MAGIC;
238 revfileheader.m_Version = LOG_INDEX_VERSION;
239 revfileheader.m_Action = Rev.m_Files[i].m_Action;
240 revfileheader.m_Stage = Rev.m_Files[i].m_Stage;
241 revfileheader.m_ParentNo = Rev.m_Files[i].m_ParentNo;
242 name = Rev.m_Files[i].GetGitPathString();
243 revfileheader.m_FileNameSize = name.GetLength();
244 oldname = Rev.m_Files[i].GetGitOldPathString();
245 revfileheader.m_OldFileNameSize = oldname.GetLength();
247 stat = Rev.m_Files[i].m_StatAdd;
248 revfileheader.m_Add = (stat == _T("-")) ? 0xFFFFFFFF : _tstol(stat);
249 stat = Rev.m_Files[i].m_StatDel;
250 revfileheader.m_Del = (stat == _T("-")) ? 0xFFFFFFFF : _tstol(stat);
252 if(!WriteFile(this->m_DataFile, &revfileheader, sizeof(revfileheader) - sizeof(TCHAR), &num, 0))
253 return -1;
255 if(!name.IsEmpty())
257 if (!WriteFile(this->m_DataFile, name, name.GetLength() * sizeof(TCHAR), &num, 0))
258 return -1;
260 if(!oldname.IsEmpty())
262 if (!WriteFile(this->m_DataFile, oldname, oldname.GetLength() * sizeof(TCHAR), &num, 0))
263 return -1;
267 return 0;
270 int CLogCache::LoadOneItem(GitRevLoglist& Rev,ULONGLONG offset)
272 if (!m_pCacheData)
273 return -1;
275 if (offset + sizeof(SLogCacheRevItemHeader) > m_DataFileLength)
276 return -2;
278 SLogCacheRevItemHeader* header = reinterpret_cast<SLogCacheRevItemHeader*>(m_pCacheData + offset);
280 if( !CheckHeader(header))
281 return -2;
283 Rev.m_Action = 0;
284 SLogCacheRevFileHeader *fileheader;
286 offset += sizeof(SLogCacheRevItemHeader);
287 fileheader = reinterpret_cast<SLogCacheRevFileHeader*>(m_pCacheData + offset);
289 for (DWORD i = 0; i < header->m_FileCount; ++i)
291 CTGitPath path;
292 CString oldfile;
294 if (offset + sizeof(SLogCacheRevFileHeader) > m_DataFileLength)
296 Rev.m_Action = 0;
297 Rev.m_Files.Clear();
298 return -2;
301 if(!CheckHeader(fileheader))
303 Rev.m_Action = 0;
304 Rev.m_Files.Clear();
305 return -2;
308 CString file(fileheader->m_FileName, fileheader->m_FileNameSize);
309 if(fileheader->m_OldFileNameSize)
310 oldfile = CString(fileheader->m_FileName + fileheader->m_FileNameSize, fileheader->m_OldFileNameSize);
311 path.SetFromGit(file,&oldfile);
313 path.m_ParentNo = fileheader ->m_ParentNo;
314 path.m_Stage = fileheader ->m_Stage;
315 path.m_Action = fileheader->m_Action & ~(CTGitPath::LOGACTIONS_HIDE | CTGitPath::LOGACTIONS_GRAY);
316 Rev.m_Action |= path.m_Action;
318 if(fileheader->m_Add == 0xFFFFFFFF)
319 path.m_StatAdd=_T("-");
320 else
321 path.m_StatAdd.Format(_T("%d"),fileheader->m_Add);
323 if(fileheader->m_Del == 0xFFFFFFFF)
324 path.m_StatDel=_T("-");
325 else
326 path.m_StatDel.Format(_T("%d"), fileheader->m_Del);
328 Rev.m_Files.AddPath(path);
330 offset += sizeof(*fileheader) + fileheader->m_OldFileNameSize*sizeof(TCHAR) + fileheader->m_FileNameSize*sizeof(TCHAR) - sizeof(TCHAR);
331 fileheader = reinterpret_cast<SLogCacheRevFileHeader*>(m_pCacheData + offset);
333 return 0;
335 int CLogCache::RebuildCacheFile()
337 SLogCacheIndexHeader Indexheader;
339 Indexheader.m_Magic = LOG_INDEX_MAGIC;
340 Indexheader.m_Version = LOG_INDEX_VERSION;
341 Indexheader.m_ItemCount =0;
343 SLogCacheDataFileHeader dataheader;
345 dataheader.m_Magic = LOG_DATA_MAGIC;
346 dataheader.m_Version = LOG_INDEX_VERSION;
348 ::SetFilePointer(m_IndexFile,0,0,0);
349 ::SetFilePointer(m_DataFile,0,0,0);
350 SetEndOfFile(this->m_IndexFile);
351 SetEndOfFile(this->m_DataFile);
353 DWORD num;
354 WriteFile(this->m_IndexFile,&Indexheader,sizeof(SLogCacheIndexHeader),&num,0);
355 SetEndOfFile(this->m_IndexFile);
356 WriteFile(this->m_DataFile,&dataheader,sizeof(SLogCacheDataFileHeader),&num,0);
357 SetEndOfFile(this->m_DataFile);
358 return 0;
360 int CLogCache::SaveCache()
362 if (!m_bEnabled)
363 return 0;
365 BOOL bIsRebuild=false;
367 if (this->m_HashMap.empty()) // is not sufficient, because "working copy changes" are always included
368 return 0;
370 if( this->m_GitDir.IsEmpty())
371 return 0;
373 if (this->m_pCacheIndex && m_pCacheIndex->m_Header.m_ItemCount == 0) // check for empty log list (issue #915)
374 return 0;
376 SLogCacheIndexFile* pIndex = nullptr;
377 if(this->m_pCacheIndex)
379 pIndex = reinterpret_cast<SLogCacheIndexFile*>(malloc(sizeof(SLogCacheIndexFile) + sizeof(SLogCacheIndexItem) * (m_pCacheIndex->m_Header.m_ItemCount)));
380 if (!pIndex)
381 return -1;
383 memcpy(pIndex,this->m_pCacheIndex,
384 sizeof(SLogCacheIndexFile) + sizeof(SLogCacheIndexItem) *( m_pCacheIndex->m_Header.m_ItemCount-1)
388 this->CloseDataHandles();
389 this->CloseIndexHandles();
391 SLogCacheIndexHeader header;
392 CString file = this->m_GitDir + INDEX_FILE_NAME;
393 int ret = -1;
396 m_IndexFile = CreateFile(file,
397 GENERIC_READ|GENERIC_WRITE,
399 nullptr,
400 OPEN_ALWAYS,
401 FILE_ATTRIBUTE_NORMAL,
402 nullptr);
404 if(m_IndexFile == INVALID_HANDLE_VALUE)
405 break;
407 file = m_GitDir + DATA_FILE_NAME;
409 m_DataFile = CreateFile(file,
410 GENERIC_READ|GENERIC_WRITE,
412 nullptr,
413 OPEN_ALWAYS,
414 FILE_ATTRIBUTE_NORMAL,
415 nullptr);
417 if(m_DataFile == INVALID_HANDLE_VALUE)
418 break;
421 memset(&header,0,sizeof(SLogCacheIndexHeader));
422 DWORD num=0;
423 if ((!ReadFile(m_IndexFile, &header, sizeof(SLogCacheIndexHeader), &num, 0)) || num != sizeof(SLogCacheIndexHeader) ||
424 !CheckHeader(&header)
427 RebuildCacheFile();
428 bIsRebuild =true;
431 if(!bIsRebuild)
433 SLogCacheDataFileHeader datafileheader;
434 DWORD num=0;
435 if ((!ReadFile(m_DataFile, &datafileheader, sizeof(SLogCacheDataFileHeader), &num, 0) || num != sizeof(SLogCacheDataFileHeader) ||
436 !CheckHeader(&datafileheader)))
438 RebuildCacheFile();
439 bIsRebuild=true;
443 if(bIsRebuild)
444 header.m_ItemCount=0;
446 SetFilePointer(m_DataFile,0,0,2);
447 SetFilePointer(m_IndexFile,0,0,2);
449 for (auto i = m_HashMap.cbegin(); i != m_HashMap.cend(); ++i)
451 if(this->GetOffset((*i).second.m_CommitHash,pIndex) ==0 || bIsRebuild)
453 if((*i).second.m_IsDiffFiles && !(*i).second.m_CommitHash.IsEmpty())
455 LARGE_INTEGER offset;
456 offset.LowPart=0;
457 offset.HighPart=0;
458 LARGE_INTEGER start;
459 start.QuadPart = 0;
460 SetFilePointerEx(this->m_DataFile,start,&offset,1);
461 if (this->SaveOneItem((*i).second, (LONG)offset.QuadPart))
463 TRACE(_T("Save one item error"));
464 SetFilePointerEx(this->m_DataFile,offset, &offset,0);
465 continue;
468 SLogCacheIndexItem item;
469 item.m_Hash = (*i).second.m_CommitHash;
470 item.m_Offset=offset.QuadPart;
472 DWORD num;
473 WriteFile(m_IndexFile,&item,sizeof(SLogCacheIndexItem),&num,0);
474 ++header.m_ItemCount;
478 FlushFileBuffers(m_IndexFile);
480 m_IndexFileMap = CreateFileMapping(m_IndexFile, nullptr, PAGE_READWRITE, 0, 0, nullptr);
481 if(m_IndexFileMap == INVALID_HANDLE_VALUE)
482 break;
484 m_pCacheIndex = reinterpret_cast<SLogCacheIndexFile*>(MapViewOfFile(m_IndexFileMap, FILE_MAP_WRITE, 0, 0, 0));
485 if (!m_pCacheIndex)
486 break;
488 m_pCacheIndex->m_Header.m_ItemCount = header.m_ItemCount;
489 Sort();
490 FlushViewOfFile(m_pCacheIndex,0);
491 ret = 0;
492 }while(0);
494 this->CloseDataHandles();
495 this->CloseIndexHandles();
497 free(pIndex);
498 return ret;
501 void CLogCache::Sort()
503 if(this->m_pCacheIndex)
504 qsort(m_pCacheIndex->m_Item, m_pCacheIndex->m_Header.m_ItemCount,sizeof(SLogCacheIndexItem), Compare);
507 int CLogCache::ClearAllParent()
509 for (auto i = m_HashMap.begin(); i != m_HashMap.end(); ++i)
511 (*i).second.m_ParentHash.clear();
512 (*i).second.m_Lanes.clear();
514 return 0;
517 void CLogCache::ClearAllLanes()
519 for (auto i = m_HashMap.begin(); i != m_HashMap.end(); ++i)
520 (*i).second.m_Lanes.clear();