summaryrefslogtreecommitdiff
path: root/plugins/Dbx_tree/src/BlockManager.cpp
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-10-12 09:56:01 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-10-12 09:56:01 +0000
commit69fe21d6e2262d6fb2b14278b7a20364468cbdcf (patch)
treed7a5c3805ba838e98e1c5288fe06962a3201c694 /plugins/Dbx_tree/src/BlockManager.cpp
parentbcc2cdbf65af07f2593577a4c59f93d25c2e3de9 (diff)
Dbx_tree: folders restructurization
git-svn-id: http://svn.miranda-ng.org/main/trunk@1883 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/Dbx_tree/src/BlockManager.cpp')
-rw-r--r--plugins/Dbx_tree/src/BlockManager.cpp965
1 files changed, 965 insertions, 0 deletions
diff --git a/plugins/Dbx_tree/src/BlockManager.cpp b/plugins/Dbx_tree/src/BlockManager.cpp
new file mode 100644
index 0000000000..b52298602f
--- /dev/null
+++ b/plugins/Dbx_tree/src/BlockManager.cpp
@@ -0,0 +1,965 @@
+/*
+
+dbx_tree: tree database driver for Miranda IM
+
+Copyright 2007-2010 Michael "Protogenes" Kunz,
+
+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 "BlockManager.h"
+#include "Logger.h"
+
+CBlockManager::CBlockManager(
+ CFileAccess & FileAccess,
+ CEncryptionManager & EncryptionManager
+ )
+: m_BlockSync(),
+ m_FileAccess(FileAccess),
+ m_EncryptionManager(EncryptionManager),
+ m_BlockTable(1024),
+ m_FreeBlocks()
+{
+ m_Optimize.Thread = NULL;
+ m_Optimize.Source = 0;
+ m_Optimize.Dest = 0;
+
+ m_CacheInfo.Growth = 0;
+ m_CacheInfo.Size = 0;
+ m_CacheInfo.LastPurge = time(NULL);
+
+ m_PendingHead = NULL;
+ m_PendingTail = NULL;
+ m_PendingLast = NULL;
+ m_LastFlush = time(NULL);
+ m_BytesPending = 20;
+ m_FirstFreeIndex = 0;
+
+ m_SaveMode = true;
+ m_ReadOnly = m_FileAccess.ReadOnly();
+
+ memset(m_Cache, 0, sizeof(m_Cache));
+}
+
+CBlockManager::~CBlockManager()
+{
+ m_BlockSync.BeginWrite();
+ if (m_Optimize.Thread)
+ {
+ m_Optimize.Thread->FreeOnTerminate(false);
+ m_Optimize.Thread->Terminate();
+
+ m_BlockSync.EndWrite();
+ m_Optimize.Thread->WaitFor();
+
+ delete m_Optimize.Thread;
+ } else {
+ m_BlockSync.EndWrite();
+ }
+
+ _PendingFlush(true);
+
+ for (uint32_t buddy = 0; buddy < cCacheBuddyCount; buddy++)
+ {
+ TCacheEntry * i = m_Cache[buddy];
+ while (i)
+ {
+ free(i->Cache);
+
+ TCacheEntry * tmp = i;
+ i = i->Next;
+ free(tmp);
+ }
+ }
+}
+
+// Optimize File Size
+void CBlockManager::ExecuteOptimize()
+{ /*
+ TBlockHeadFree h = {0,0};
+ uint8_t * buf = (uint8_t*)malloc(1 << 18); // 256kb
+ uint32_t bufuse = 0;
+ uint32_t bufsize = 1 << 18;
+ uint32_t lastflush = 0;
+
+ {
+ int i = 0;
+ while (!m_Optimize.Thread->Terminated() && (i < 600))
+ {
+ ++i;
+ Sleep(100); // wait for Miranda to start
+ }
+ }
+
+ TransactionBeginWrite();
+
+ while (!m_Optimize.Thread->Terminated() && (m_Optimize.Source < m_FileAccess.Size()) && !m_ReadOnly)
+ {
+ m_FileAccess.Read(&h, m_Optimize.Source, sizeof(h));
+ if (h.ID == cFreeBlockID)
+ {
+ _RemoveFreeBlock(m_Optimize.Source, h.Size);
+
+ m_Optimize.Source += h.Size;
+ } else {
+
+ if (bufsize < bufuse + h.Size)
+ {
+ buf = (uint8_t*)realloc(buf, bufuse + h.Size);
+ bufsize = bufuse + h.Size;
+ }
+ m_FileAccess.Read(buf + bufuse, m_Optimize.Source, h.Size);
+
+ m_BlockTable[h.ID >> 2].Addr = (m_Optimize.Dest + bufuse) >> 2;
+
+ m_Optimize.Source += h.Size;
+ bufuse += h.Size;
+ }
+
+ if ((m_BlockSync.Waiting() > 0)
+ || (bufuse + 1024 >= bufsize)
+ || (m_Optimize.Source >= m_FileAccess.Size())) // buffer is nearly full or EOF
+ {
+ if (m_Optimize.Dest != m_Optimize.Source) // move blocks
+ {
+ TBlockHeadFree h = {cFreeBlockID, m_Optimize.Source - m_Optimize.Dest};
+ TBlockTailFree t = {m_Optimize.Source - m_Optimize.Dest, cFreeBlockID};
+
+ m_FileAccess.Write(buf, m_Optimize.Dest, bufuse);
+
+ m_FileAccess.Write(&h, m_Optimize.Dest + bufuse, sizeof(h));
+ m_FileAccess.Invalidate(m_Optimize.Dest + bufuse + sizeof(h), m_Optimize.Source - m_Optimize.Dest - bufuse - sizeof(h) - sizeof(t));
+ m_FileAccess.Write(&t, m_Optimize.Dest + bufuse - sizeof(t), sizeof(t));
+
+ if (m_SaveMode)
+ {
+ m_FileAccess.CloseTransaction();
+ m_FileAccess.Flush();
+ m_FileAccess.UseJournal(false);
+
+ m_FileAccess.Write(buf, m_Optimize.Dest, bufuse);
+
+ m_FileAccess.Write(&h, m_Optimize.Dest + bufuse, sizeof(h));
+ m_FileAccess.Invalidate(m_Optimize.Dest + bufuse + sizeof(h), m_Optimize.Source - m_Optimize.Dest - bufuse - sizeof(h) - sizeof(t));
+ m_FileAccess.Write(&t, m_Optimize.Dest + bufuse - sizeof(t), sizeof(t));
+
+ m_FileAccess.Flush();
+ m_FileAccess.CleanJournal();
+ m_FileAccess.UseJournal(true);
+ }
+
+ m_Optimize.Dest += bufuse;
+ bufuse = 0;
+ }
+
+ if (m_BlockSync.Waiting() > 0)
+ {
+ unsigned int w = m_BlockSync.Waiting();
+ m_BlockSync.EndWrite();
+ Sleep(w * 64 + 1);
+ m_BlockSync.BeginWrite();
+ m_FileAccess.UseJournal(m_SaveMode);
+ }
+ }
+ }
+
+ if (m_Optimize.Source >= m_FileAccess.Size())
+ m_FileAccess.Size(m_Optimize.Dest);
+
+ m_Optimize.Thread = NULL;
+ m_Optimize.Source = 0;
+ m_Optimize.Dest = 0;
+ if (m_SaveMode)
+ TransactionEndWrite();
+ else
+ m_BlockSync.EndWrite();
+
+ free(buf); */
+
+ m_Optimize.Thread = NULL;
+}
+
+inline void CBlockManager::_PendingAdd(uint32_t BlockID, uint32_t Addr, uint32_t Size, TCacheEntry * Cache)
+{
+ TPendingOperation * p = NULL;
+ if (BlockID == cFreeBlockID)
+ {
+ p = (TPendingOperation*)malloc(sizeof(TPendingOperation));
+
+ p->BlockID = cFreeBlockID;
+ p->Addr = Addr;
+ p->Size = Size;
+ p->CacheEntry = NULL;
+ p->EncryptionBuffer = NULL;
+
+ m_BytesPending += 24 + sizeof(TBlockHeadFree) + sizeof(TBlockTailFree);
+ if (Addr & cPendingInvalidate)
+ m_BytesPending += 12;
+
+ } else {
+ if (Cache->Pending)
+ {
+ p = Cache->Pending;
+ _PendingRemove(Cache->Pending, false);
+ } else {
+ p = (TPendingOperation*)malloc(sizeof(TPendingOperation));
+ }
+
+ p->BlockID = BlockID;
+ p->Addr = Addr;
+ p->Size = Size;
+ p->CacheEntry = Cache;
+ p->EncryptionBuffer = NULL;
+
+ m_BytesPending += 12 + Size;
+
+ Cache->Pending = p;
+ }
+
+ p->Next = NULL;
+ p->Prev = m_PendingTail;
+ if (m_PendingTail)
+ m_PendingTail->Next = p;
+
+ m_PendingTail = p;
+ if (!m_PendingHead)
+ m_PendingHead = p;
+}
+
+inline void CBlockManager::_PendingRemove(TPendingOperation * Pending, bool Free)
+{
+ if (Pending->Prev)
+ Pending->Prev->Next = Pending->Next;
+ else
+ m_PendingHead = Pending->Next;
+
+ if (Pending->Next)
+ Pending->Next->Prev = Pending->Prev;
+ else
+ m_PendingTail = Pending->Prev;
+
+ if (Pending->EncryptionBuffer)
+ free(Pending->EncryptionBuffer);
+
+ if (m_PendingLast == Pending)
+ m_PendingLast = Pending->Prev;
+
+ Pending->CacheEntry->Pending = NULL;
+ if (Free)
+ free(Pending);
+}
+
+inline void CBlockManager::_PendingFlush(bool FullFlush)
+{
+ TPendingOperation * i = NULL;
+
+ if (m_ReadOnly)
+ return;
+
+ if (FullFlush)
+ {
+ if (m_SaveMode)
+ {
+ _PendingFlush(false); // write to journal
+ m_FileAccess.Flush();
+ m_FileAccess.UseJournal(false);
+ m_FileAccess.Size(m_FileAccess.Size()); // resize real file
+ } else {
+ m_FileAccess.UseJournal(false);
+ }
+
+ i = m_PendingHead;
+ } else if (m_PendingLast)
+ {
+ i = m_PendingLast->Next;
+ m_FileAccess.UseJournal(m_SaveMode);
+ } else {
+ i = m_PendingHead;
+ m_FileAccess.UseJournal(m_SaveMode);
+ }
+
+ while (i)
+ {
+ if (i->BlockID == cFreeBlockID)
+ {
+ uint32_t addr = i->Addr & ~cPendingInvalidate;
+ if (addr + i->Size <= m_FileAccess.Size())
+ {
+ TBlockHeadFree h = {cFreeBlockID, i->Size};
+ TBlockTailFree t = {i->Size, cFreeBlockID};
+
+ m_FileAccess.Write(&h, addr, sizeof(h));
+ if (i->Addr & cPendingInvalidate)
+ m_FileAccess.Invalidate(addr + sizeof(h), i->Size - sizeof(h) - sizeof(t));
+ m_FileAccess.Write(&t, addr + i->Size - sizeof(t), sizeof(t));
+ }
+
+ } else {
+
+ if (i->BlockID && !i->EncryptionBuffer && m_EncryptionManager.IsEncrypted(i->BlockID))
+ {
+ i->EncryptionBuffer = (TBlockHeadOcc*) malloc(i->Size);
+ memcpy(i->EncryptionBuffer, i->CacheEntry->Cache, i->Size);
+ m_EncryptionManager.Encrypt(i->EncryptionBuffer + 1, i->Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc), i->BlockID, 0);
+ }
+
+ if (i->EncryptionBuffer)
+ {
+ m_FileAccess.Write(i->EncryptionBuffer, i->Addr, i->Size);
+ } else {
+ m_FileAccess.Write(i->CacheEntry->Cache, i->Addr, i->Size);
+ }
+ }
+
+ i = i->Next;
+ } // while
+
+ if (FullFlush)
+ {
+ m_FileAccess.Flush();
+ if (m_SaveMode)
+ m_FileAccess.CleanJournal();
+
+ m_BytesPending = 20;
+ m_LastFlush = time(NULL);
+
+ i = m_PendingHead;
+ while (i)
+ {
+ if (i->EncryptionBuffer)
+ free(i->EncryptionBuffer);
+
+ if (i->CacheEntry)
+ i->CacheEntry->Pending = NULL;
+
+ TPendingOperation * tmp = i;
+ i = i->Next;
+ free(tmp);
+ }
+ m_PendingHead = NULL;
+ m_PendingTail = NULL;
+ m_PendingLast = NULL;
+ } else {
+ m_PendingLast = m_PendingTail;
+ m_FileAccess.CloseTransaction();
+ }
+}
+
+inline CBlockManager::TCacheEntry * CBlockManager::_CacheInsert(uint32_t Idx, TBlockHeadOcc * Cache, bool Virtual)
+{
+ TCacheEntry * res;
+ uint32_t myidx = ROR_32(Idx, cCacheBuddyBits);
+
+ res = (TCacheEntry *)malloc(sizeof(TCacheEntry));
+ res->Cache = Cache;
+ res->Pending = NULL;
+ res->Idx = myidx;
+ res->Forced = Virtual;
+
+ TCacheEntry * volatile * last = &m_Cache[Idx % cCacheBuddyCount];
+ TCacheEntry * i;
+ do {
+ i = *last;
+
+ while (i && (i->Idx < myidx))
+ {
+ last = &i->Next;
+ i = i->Next;
+ }
+
+ if (i && (i->Idx == myidx))
+ {
+ free(res);
+ free(Cache);
+
+ i->LastUse = time(NULL) >> 2;
+ return i;
+ }
+
+ res->Next = i;
+
+ } while (i != CMPXCHG_Ptr(*last, res, i));
+
+ res->LastUse = time(NULL) >> 2;
+
+ m_BlockTable[Idx].InCache = true;
+ if (!Virtual)
+ XADD_32(m_CacheInfo.Growth, res->Cache->Size);
+
+ return res;
+}
+
+inline CBlockManager::TCacheEntry * CBlockManager::_CacheFind(uint32_t Idx)
+{
+ TCacheEntry * i = m_Cache[Idx % cCacheBuddyCount];
+ uint32_t myidx = ROR_32(Idx, cCacheBuddyBits);
+ while (i && (i->Idx < myidx))
+ i = i->Next;
+
+ if (i && (i->Idx == myidx))
+ return i;
+ else
+ return NULL;
+}
+
+inline void CBlockManager::_CacheErase(uint32_t Idx)
+{
+ TCacheEntry * i = m_Cache[Idx % cCacheBuddyCount];
+ TCacheEntry * volatile * l = &m_Cache[Idx % cCacheBuddyCount];
+
+ uint32_t myidx = ROR_32(Idx, cCacheBuddyBits);
+
+ while (i->Idx < myidx)
+ {
+ l = &i->Next;
+ i = i->Next;
+ }
+ *l = i->Next;
+
+ if (i->Cache)
+ free(i->Cache);
+ free(i);
+}
+
+inline void CBlockManager::_CachePurge()
+{
+ _PendingFlush(true);
+
+ uint32_t ts = time(NULL);
+ if (m_CacheInfo.Size + m_CacheInfo.Growth > cCachePurgeSize) {
+ ts = ts - (ts - m_CacheInfo.LastPurge) * cCachePurgeSize / (m_CacheInfo.Size + 2 * m_CacheInfo.Growth);
+ } else if (m_CacheInfo.Growth > m_CacheInfo.Size)
+ {
+ ts = ts - (ts - m_CacheInfo.LastPurge) * m_CacheInfo.Size / m_CacheInfo.Growth;
+ } else if (m_CacheInfo.Size > m_CacheInfo.Growth)
+ {
+ ts = ts - (ts - m_CacheInfo.LastPurge) * m_CacheInfo.Growth / m_CacheInfo.Size;
+ } else {
+ ts = m_CacheInfo.LastPurge;
+ }
+
+ m_CacheInfo.Size += m_CacheInfo.Growth;
+ m_CacheInfo.Growth = 0;
+ m_CacheInfo.LastPurge = time(NULL);
+
+ for (uint32_t buddy = 0; buddy < cCacheBuddyCount; buddy++)
+ {
+ TCacheEntry * i = m_Cache[buddy];
+ TCacheEntry * volatile * l = &m_Cache[buddy];
+
+ while (i)
+ {
+ if (!i->Forced && !i->KeepInCache && i->Idx && ((i->LastUse << 2) < ts))
+ {
+ uint32_t idx = ROL_32(i->Idx, cCacheBuddyBits);
+ m_CacheInfo.Size -= i->Cache->Size;
+ m_BlockTable[idx].InCache = false;
+ free(i->Cache);
+
+ *l = i->Next;
+ TCacheEntry * tmp = i;
+ i = i->Next;
+ free(tmp);
+
+ } else {
+ l = &i->Next;
+ i = i->Next;
+ }
+ }
+ }
+}
+
+inline uint32_t CBlockManager::_GetAvailableIndex()
+{
+ uint32_t id;
+ if (m_FirstFreeIndex)
+ {
+ id = m_FirstFreeIndex;
+ m_FirstFreeIndex = m_BlockTable[id].Addr;
+ TBlockTableEntry b = {false, false, 0};
+ m_BlockTable[id] = b;
+ } else {
+ id = static_cast<uint32_t>(m_BlockTable.size());
+ if (id > (1 << 12))
+ m_BlockTable.resize(id + (1 << 12));
+ else
+ m_BlockTable.resize(id * 2);
+
+ for (uint32_t i = static_cast<uint32_t>(m_BlockTable.size() - 1); i > id; --i)
+ {
+ TBlockTableEntry b = {true, true, m_FirstFreeIndex};
+ m_BlockTable[i] = b;
+ m_FirstFreeIndex = i;
+ }
+ }
+ return id;
+}
+
+inline void CBlockManager::_InsertFreeBlock(uint32_t Addr, uint32_t Size, bool InvalidateData, bool Reuse)
+{
+ if (Addr + Size == m_FileAccess.Size())
+ {
+ if (Reuse) // in FindFreePosition we would want to use that block
+ m_FileAccess.Size(Addr);
+ } else {
+
+ if (Reuse)
+ m_FreeBlocks.insert(std::make_pair(Size, Addr));
+
+ if (!m_ReadOnly)
+ _PendingAdd(cFreeBlockID, InvalidateData ? Addr | cPendingInvalidate : Addr, Size, NULL);
+
+ }
+}
+
+inline void CBlockManager::_RemoveFreeBlock(uint32_t Addr, uint32_t Size)
+{
+ TFreeBlockMap::iterator i = m_FreeBlocks.find(Size);
+ while ((i != m_FreeBlocks.end()) && (i->first == Size))
+ {
+ if (i->second == Addr)
+ {
+ m_FreeBlocks.erase(i);
+ i = m_FreeBlocks.end();
+ } else {
+ ++i;
+ }
+ }
+}
+
+inline uint32_t CBlockManager::_FindFreePosition(uint32_t Size)
+{
+ // try to find free block
+ TFreeBlockMap::iterator f;
+ TFreeBlockMap::iterator e = m_FreeBlocks.end();
+
+ if (m_FreeBlocks.size())
+ {
+ f = m_FreeBlocks.find(Size);
+ if (f == e)
+ {
+ if (m_FreeBlocks.rbegin()->first > Size * 2)
+ {
+ f = m_FreeBlocks.end();
+ --f;
+ }
+ }
+ } else {
+ f = e;
+ }
+
+ uint32_t addr = 0;
+
+ if (f == e) // no block found - expand file
+ {
+ addr = m_FileAccess.Size();
+ m_FileAccess.Size(addr + Size);
+
+ } else {
+ addr = f->second;
+
+ if (f->first != Size)
+ {
+ _InsertFreeBlock(addr + Size, f->first - Size, false, true);
+ }
+ _InsertFreeBlock(addr, Size, false, false);
+
+ m_FreeBlocks.erase(f);
+ }
+
+ return addr;
+}
+
+inline bool CBlockManager::_InitOperation(uint32_t BlockID, uint32_t & Addr, TCacheEntry * & Cache)
+{
+ if (!BlockID || (BlockID & 3) || ((BlockID >> 2) >= m_BlockTable.size()))
+ return false;
+
+ uint32_t idx = BlockID >> 2;
+ TBlockTableEntry dat = m_BlockTable[idx];
+
+ if (dat.Deleted) // deleted or FreeIDList item
+ return false;
+
+ Addr = dat.Addr << 2;
+ if (dat.InCache)
+ {
+ Cache = _CacheFind(idx);
+ } else if (Addr)
+ {
+ TBlockHeadOcc h;
+
+ m_FileAccess.Read(&h, Addr, sizeof(h));
+
+ TBlockHeadOcc * block = (TBlockHeadOcc *) malloc(h.Size);
+ m_FileAccess.Read(block, Addr, h.Size);
+
+ m_EncryptionManager.Decrypt(block + 1, h.Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc), BlockID, 0);
+
+ Cache = _CacheInsert(idx, block, false);
+ } else {
+ return false;
+ }
+
+ return Cache != NULL;
+}
+
+inline void CBlockManager::_UpdateBlock(uint32_t BlockID, TCacheEntry * CacheEntry, uint32_t Addr)
+{
+ CacheEntry->KeepInCache = m_ReadOnly;
+
+ if (!CacheEntry->Forced)
+ {
+ if (!m_ReadOnly)
+ _PendingAdd(BlockID, Addr, CacheEntry->Cache->Size, CacheEntry);
+ }
+}
+
+
+uint32_t CBlockManager::ScanFile(uint32_t FirstBlockStart, uint32_t HeaderSignature, uint32_t FileSize)
+{
+ TBlockHeadOcc h, lasth = {0, 0, 0};
+ uint32_t p;
+ uint32_t res = 0;
+ bool invalidateblock = false;
+
+ p = FirstBlockStart;
+ m_FirstBlockStart = FirstBlockStart;
+ m_Optimize.Source = 0;
+ m_Optimize.Dest = 0;
+
+ { // insert header cache element
+ void * header = malloc(FirstBlockStart);
+ m_FileAccess.Read(header, 0, FirstBlockStart);
+ _CacheInsert(0, (TBlockHeadOcc*)header, false);
+ }
+
+ TransactionBeginWrite();
+
+ while (p < FileSize)
+ {
+ m_FileAccess.Read(&h, p, sizeof(h));
+ if (CLogger::Instance().Level() >= CLogger::logERROR || !h.Size)
+ {
+ LOG(logCRITICAL, _T("Block-structure of file is corrupt!"));
+ return 0;
+ }
+
+ if (h.ID == cFreeBlockID)
+ {
+ if (m_Optimize.Dest == 0)
+ m_Optimize.Dest = p;
+
+ if (lasth.ID == cFreeBlockID)
+ {
+ lasth.Size += h.Size;
+ invalidateblock = true;
+ } else {
+ lasth = h;
+ }
+
+ } else {
+
+ if (lasth.ID == cFreeBlockID)
+ {
+ if (m_Optimize.Source == 0)
+ m_Optimize.Source = p;
+
+ _InsertFreeBlock(p - lasth.Size, lasth.Size, invalidateblock, true);
+ }
+ lasth = h;
+ invalidateblock = false;
+
+ while ((h.ID >> 2) >= m_BlockTable.size())
+ m_BlockTable.resize(m_BlockTable.size() << 1);
+
+ m_BlockTable[h.ID >> 2].Addr = p >> 2;
+
+ if (h.Signature == HeaderSignature)
+ res = h.ID;
+ }
+
+ p = p + h.Size;
+ }
+
+ m_FirstFreeIndex = 0;
+ for (uint32_t i = static_cast<uint32_t>(m_BlockTable.size() - 1); i > 0; --i)
+ {
+ if (m_BlockTable[i].Addr == 0)
+ {
+ TBlockTableEntry b = {true, true, m_FirstFreeIndex};
+ m_BlockTable[i] = b;
+ m_FirstFreeIndex = i;
+ }
+ }
+
+ TransactionEndWrite();
+
+ if (m_Optimize.Source && !m_FileAccess.ReadOnly())
+ {
+ m_Optimize.Thread = new COptimizeThread(*this);
+ m_Optimize.Thread->Priority(CThread::tpLowest);
+ m_Optimize.Thread->FreeOnTerminate(true);
+ m_Optimize.Thread->Resume();
+ }
+
+ return res;
+}
+
+void * CBlockManager::_CreateBlock(uint32_t & BlockID, const uint32_t Signature, uint32_t Size)
+{
+ uint32_t idx = _GetAvailableIndex();
+ BlockID = idx << 2;
+
+ Size = m_EncryptionManager.AlignSize(BlockID, (Size + 3) & 0xfffffffc); // align on cipher after we aligned on 4 bytes
+
+ TBlockHeadOcc h = {BlockID, Size + sizeof(TBlockHeadOcc) + sizeof(TBlockTailOcc), Signature};
+ TBlockTailOcc t = {BlockID};
+
+ TBlockHeadOcc * block = (TBlockHeadOcc*) malloc(Size + sizeof(h) + sizeof(t));
+ *block = h;
+ memset(block + 1, 0, Size);
+ *(TBlockTailOcc*)(((uint8_t*)(block + 1)) + Size) = t;
+
+ TCacheEntry * ce = _CacheInsert(idx, block, false);
+
+ if (m_ReadOnly)
+ {
+ TBlockTableEntry b = {false, true, 0};
+ m_BlockTable[idx] = b;
+ } else {
+ uint32_t addr = _FindFreePosition(Size + sizeof(h) + sizeof(t));
+ TBlockTableEntry b = {false, true, addr >> 2 };
+ m_BlockTable[idx] = b;
+
+ _UpdateBlock(BlockID, ce, addr);
+ }
+
+ return ce->Cache + 1;
+}
+
+void * CBlockManager::_CreateBlockVirtual(uint32_t & BlockID, const uint32_t Signature, uint32_t Size)
+{
+ uint32_t idx = _GetAvailableIndex();
+ BlockID = idx << 2;
+
+ Size = m_EncryptionManager.AlignSize(BlockID, (Size + 3) & 0xfffffffc); // align on cipher after we aligned on 4 bytes
+
+ TBlockHeadOcc h = {BlockID, Size + sizeof(TBlockHeadOcc) + sizeof(TBlockTailOcc), Signature};
+ TBlockTailOcc t = {BlockID};
+
+ TBlockHeadOcc * block = (TBlockHeadOcc*) malloc(Size + sizeof(h) + sizeof(t));
+ *block = h;
+ memset(block + 1, 0, Size);
+ *(TBlockTailOcc*)(((uint8_t*)(block + 1)) + Size) = t;
+
+ return _CacheInsert(idx, block, true)->Cache + 1;
+}
+
+bool CBlockManager::DeleteBlock(uint32_t BlockID)
+{
+ uint32_t idx = BlockID >> 2;
+ uint32_t addr;
+ uint32_t size;
+ TCacheEntry * ce;
+ if (!_InitOperation(BlockID, addr, ce))
+ return false;
+
+ if (!ce->Forced)
+ XADD_32(m_CacheInfo.Size, 0 - ce->Cache->Size);
+
+ if (ce->Pending)
+ _PendingRemove(ce->Pending, true);
+
+ size = ce->Cache->Size;
+ _CacheErase(idx);
+
+ if (addr == 0) // Block in memory only
+ {
+ TBlockTableEntry b = {false, false, 0};
+ m_BlockTable[idx] = b;
+
+ } else if (m_ReadOnly)
+ {
+ m_BlockTable[idx].Deleted = true;
+ m_BlockTable[idx].InCache = false;
+ } else {
+ _InsertFreeBlock(addr, size, true, true);
+
+ TBlockTableEntry b = {false, false, 0};
+ m_BlockTable[idx] = b;
+ }
+
+ return true;
+}
+
+uint32_t CBlockManager::_ResizeBlock(uint32_t BlockID, void * & Buffer, uint32_t Size)
+{
+ uint32_t idx = BlockID >> 2;
+ uint32_t addr;
+ TCacheEntry * ce;
+
+ if (Size == 0)
+ return 0;
+
+ if (!_InitOperation(BlockID, addr, ce))
+ return 0;
+
+ Size = m_EncryptionManager.AlignSize(BlockID, (Size + 3) & 0xfffffffc); // align on cipher after we aligned on 4 bytes
+
+ uint32_t os = ce->Cache->Size;
+ uint32_t ns = Size + sizeof(TBlockHeadOcc) + sizeof(TBlockTailOcc);
+ if (ns == ce->Cache->Size)
+ {
+ Buffer = ce->Cache + 1;
+ return Size;
+ }
+
+ ce->Cache = (TBlockHeadOcc*) realloc(ce->Cache, ns);
+ ce->Cache->Size = ns;
+ TBlockTailOcc t = {BlockID};
+ *(TBlockTailOcc*)(((uint8_t*)(ce->Cache + 1)) + Size) = t;
+ XADD_32(m_CacheInfo.Size, ns - os);
+
+ Buffer = ce->Cache + 1;
+ ce->KeepInCache = m_ReadOnly;
+
+ if (!m_ReadOnly && addr)
+ {
+ _InsertFreeBlock(addr, os, true, true);
+ addr = _FindFreePosition(ns);
+ m_BlockTable[idx].Addr = addr >> 2;
+
+ _UpdateBlock(BlockID, ce, addr); // write down
+ }
+
+ return Size;
+}
+
+bool CBlockManager::IsForcedVirtual(uint32_t BlockID)
+{
+ TCacheEntry * ce = _CacheFind(BlockID >> 2);
+ return ce && ce->Forced;
+}
+
+bool CBlockManager::WriteBlockToDisk(uint32_t BlockID)
+{
+ uint32_t addr;
+ TCacheEntry * ce;
+
+ if (!_InitOperation(BlockID, addr, ce))
+ return false;
+
+ if (!ce->Forced)
+ return true;
+
+ ce->Forced = false;
+ XADD_32(m_CacheInfo.Size, ce->Cache->Size);
+
+ if (!m_ReadOnly)
+ {
+ addr = _FindFreePosition(ce->Cache->Size);
+ m_BlockTable[BlockID >> 2].Addr = addr >> 2;
+ _UpdateBlock(BlockID, ce, addr);
+ }
+ return true;
+}
+
+bool CBlockManager::MakeBlockVirtual(uint32_t BlockID)
+{
+ uint32_t addr;
+ TCacheEntry * ce;
+
+ if (!_InitOperation(BlockID, addr, ce))
+ return false;
+
+ if (ce->Forced)
+ return true;
+
+ if (ce->Pending) // don't write down, we kill it anyway
+ _PendingRemove(ce->Pending, true);
+
+ ce->Forced = true;
+ XADD_32(m_CacheInfo.Size, 0 - ce->Cache->Size);
+
+ if (!m_ReadOnly)
+ {
+ _InsertFreeBlock(addr, ce->Cache->Size, true, true);
+ m_BlockTable[BlockID >> 2].Addr = 0;
+ }
+ return true;
+}
+
+void * CBlockManager::_ReadBlock(uint32_t BlockID, uint32_t & Size, uint32_t & Signature)
+{
+ uint32_t addr;
+ TCacheEntry * ce;
+
+ if ((BlockID == 0) && (Signature == -1))
+ {
+ Size = m_FirstBlockStart;
+ return m_Cache[0]->Cache;
+ }
+
+ if (!_InitOperation(BlockID, addr, ce))
+ return NULL;
+
+ if ((Signature != 0) && (Signature != ce->Cache->Signature))
+ {
+ Signature = ce->Cache->Signature;
+ return NULL;
+ }
+ Signature = ce->Cache->Signature;
+
+ if ((Size != 0) && (Size != ce->Cache->Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc)))
+ {
+ Size = ce->Cache->Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc);
+ return NULL;
+ }
+ Size = ce->Cache->Size - sizeof(TBlockHeadOcc) - sizeof(TBlockTailOcc);
+
+ return ce->Cache + 1;
+}
+
+bool CBlockManager::UpdateBlock(uint32_t BlockID, uint32_t Signature)
+{
+ uint32_t addr;
+ TCacheEntry * ce;
+
+ if ((BlockID == 0) && (Signature == -1))
+ {
+ if (!m_ReadOnly)
+ _PendingAdd(0, 0, m_FirstBlockStart, m_Cache[0]);
+
+ return true;
+ }
+
+ if (!_InitOperation(BlockID, addr, ce))
+ return false;
+
+ if (Signature)
+ ce->Cache->Signature = Signature;
+
+ _UpdateBlock(BlockID, ce, addr);
+
+ return true;
+}
+
+// make file writeable:
+// write all cached blocks to file and check for the old addresses
+// remove virtual only from file
+// update m_FreeBlocks along the way
+