summaryrefslogtreecommitdiff
path: root/plugins/Dbx_tree/BlockManager.h
blob: dcccd4f0b97c46a912d85131f14261d2395025fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/*

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.

*/

#pragma once

#include <windows.h>
#include <map>
#include <vector>

#include "stdint.h"
#include "FileAccess.h"
#include "EncryptionManager.h"
#include "MREWSync.h"
#include "Thread.h"
#include "intrinsics.h"

class CBlockManager
{
protected:
	static const uint32_t cFreeBlockID = 0xFFFFFFFF;

	static const uint32_t cJournalFlushBytes = (1 << 20) - 2048; // flush before reserved journal-space is exhausted
	static const uint32_t cJournalFlushTimeout = 300; // journal flush every 5 minutes

	static const uint32_t cCacheBuddyBits  = 10;
	static const uint32_t cCacheBuddyCount = 1 << cCacheBuddyBits; // count of static allocated buddy nodes
	static const uint32_t cCacheBuddyCheck = 0xffffffff << cCacheBuddyBits;

	static const uint32_t cCacheMinimumTimeout = 2; // purge less than every n seconds (high priority)
	static const uint32_t cCacheMaximumTimeout = 600; // purge every 10 minutes (high priority)
	static const uint32_t cCachePurgeSize = 1 << 21; // cache up to 2MB
	static const uint32_t cCacheMinimumGrowthForPurge = 1 << 19; // cache only when 512kb were added
	
	#pragma pack(push, 1)  // push current alignment to stack, set alignment to 1 byte boundary

	typedef struct TBlockHeadFree {
		uint32_t ID;
		uint32_t Size;
	} TBlockHeadFree;
	typedef struct TBlockHeadOcc {
		uint32_t ID;
		uint32_t Size;
		uint32_t Signature; /// if occupied block
	}	TBlockHeadOcc;

	typedef struct TBlockTailOcc {
		uint32_t ID;
	} TBlockTailOcc;

	typedef struct TBlockTailFree {
		uint32_t Size; /// if free block
		uint32_t ID;
	} TBlockTailFree;

	#pragma pack(pop)

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>	Block table entry. </summary>
	///
	/// Addr   Deleted   InCache   Meaning
	///    0         0         0   successfully deleted block
	///    0         0         1   virtual only block (either forced virtual or created on a read-only file)
	///    0         1         0   invalid
	///    0         1         1   FreeID list (last entry)
	///  set         0         0   Normal in-file block
	///  set         0         1   in file and cache (normal cache which could differ on a read-only file or forced virtual out of a read-only file - check TCacheEntry)
	///  set         1         0   deleted block or a read-only file
	///  set         1         1   FreeID list entry
	///   
	/// <remarks>	Michael "Protogenes" Kunz, 07.09.2010. </remarks>
	////////////////////////////////////////////////////////////////////////////////////////////////////
	typedef struct TBlockTableEntry {
		uint32_t Deleted : 1;	///< Flag is set if the block was deleted but can't be removed, because the file is read-only
		uint32_t InCache : 1;	///< Flag is set if block is in the cache (either forced virtual or from the file)
		uint32_t Addr : 30;	///< The Offset in the file div 4, so we can address files up to 4GB
	} TBlockTableEntry;
	std::vector<TBlockTableEntry> m_BlockTable;
	
	struct TPendingOperation;
	typedef struct TCacheEntry {
		TCacheEntry * volatile Next;
		TBlockHeadOcc * volatile Cache;
		TPendingOperation * Pending;

		uint32_t Idx;
		uint32_t Forced : 1;
		uint32_t KeepInCache : 1;
		uint32_t LastUse : 30;
	} TCacheEntry;
	
	TCacheEntry * m_Cache[cCacheBuddyCount];

	struct {
		uint32_t volatile Size;
		uint32_t volatile Growth;
		uint32_t volatile LastPurge;
	} m_CacheInfo;

	CFileAccess & m_FileAccess;
	CEncryptionManager & m_EncryptionManager;
	CMultiReadExclusiveWriteSynchronizer m_BlockSync;

	uint32_t m_FirstBlockStart;
	bool m_SaveMode;
	bool m_ReadOnly;

	typedef std::multimap<uint32_t, uint32_t> TFreeBlockMap;
	TFreeBlockMap m_FreeBlocks;
	uint32_t m_FirstFreeIndex;

	static const uint32_t cPendingInvalidate = 0x00000001;
	typedef struct TPendingOperation {
		TPendingOperation * Next;	///< The next
		TPendingOperation * Prev;	///< The previous
		uint32_t BlockID;	///< Identifier for the block
		uint32_t Addr;	///< The address in the file
		uint32_t Size;	///< The size of the block
		TCacheEntry * CacheEntry;	///< The cache entry
		TBlockHeadOcc * EncryptionBuffer;	///< Buffer for encrypted block
	} TPendingOperation;
	
	TPendingOperation * m_PendingHead;	///< The double linked list head
	TPendingOperation * m_PendingTail;	///< The double linked list tail
	TPendingOperation * m_PendingLast;	///< The last processed item

	uint32_t m_LastFlush;	///< The last flush timestamp
	uint32_t m_BytesPending;	///< The bytes pending for write

	class COptimizeThread : public CThread
	{
		protected:
			CBlockManager & m_Owner;
			void Execute() { m_Owner.ExecuteOptimize(); };
		public:
			COptimizeThread(CBlockManager & Owner) : CThread(true), m_Owner(Owner) {};
			~COptimizeThread() {};
	};

	struct {
		uint32_t Source;
		uint32_t Dest;
		COptimizeThread * Thread;
	} m_Optimize;
	void ExecuteOptimize();
		
	uint32_t _GetAvailableIndex();
	void _InsertFreeBlock(uint32_t Addr, uint32_t Size, bool InvalidateData, bool Reuse);
	void _RemoveFreeBlock(uint32_t Addr, uint32_t Size);
	uint32_t _FindFreePosition(uint32_t Size);

	bool _InitOperation(uint32_t BlockID, uint32_t & Addr, TCacheEntry * & Cache);
	void _UpdateBlock(uint32_t BlockID, TCacheEntry * CacheEntry, uint32_t Addr);

	void * _ReadBlock(uint32_t BlockID, uint32_t & Size, uint32_t & Signature);
	void * _CreateBlock(uint32_t & BlockID, const uint32_t Signature, uint32_t Size);
	void * _CreateBlockVirtual(uint32_t & BlockID, const uint32_t Signature, uint32_t Size);
	uint32_t _ResizeBlock(uint32_t BlockID, void * & Buffer, uint32_t Size);

	TCacheEntry * _CacheInsert(uint32_t Idx, TBlockHeadOcc * Cache, bool Virtual);
	TCacheEntry * _CacheFind(uint32_t Idx);
	void _CacheErase(uint32_t Idx);

	void _CachePurge();
	void _PendingAdd(uint32_t BlockID, uint32_t Addr, uint32_t Size, TCacheEntry * Cache);
	void _PendingRemove(TPendingOperation * Pending, bool Free);
	void _PendingFlush(bool FullFlush);


	
	void TransactionBeginRead()
		{
			m_BlockSync.BeginRead();
		};

	void TransactionEndRead()
		{
			m_BlockSync.EndRead();
		};

	void TransactionBeginWrite()
		{
			m_BlockSync.BeginWrite();
			m_FileAccess.UseJournal(m_SaveMode);
		};

	void TransactionEndWrite()
		{
			if (m_BlockSync.WriteRecursionCount() == 1)
			{
				m_FileAccess.CompleteTransaction();
				m_BytesPending += 12;

				if ((m_CacheInfo.LastPurge + cCacheMaximumTimeout < time(NULL))
					|| ((m_CacheInfo.Size + m_CacheInfo.Growth > cCachePurgeSize) 
					&& (m_CacheInfo.Growth > cCacheMinimumGrowthForPurge)
					&& (m_CacheInfo.LastPurge + cCacheMinimumTimeout < time(NULL))))
				{
					_CachePurge();
				} else if ((m_BytesPending >= cJournalFlushBytes) || (time(NULL) > m_LastFlush + cJournalFlushTimeout))
				{
					_PendingFlush(true);
				} else {
					_PendingFlush(false);
				}
			}

			m_BlockSync.EndWrite();
		};
public:
	CBlockManager(CFileAccess & FileAccess, CEncryptionManager & EncryptionManager);
	~CBlockManager();


	class ReadTransaction
	{
		private:
			CBlockManager * m_Owner;
			uint32_t volatile * m_RefCount;
			bool m_Closed;
		public:
			ReadTransaction()
				:	m_Owner(NULL),
					 m_RefCount(NULL),
					 m_Closed(true)
				{

				};
			ReadTransaction(CBlockManager & BlockManager)
				:	m_Owner(&BlockManager),
					m_RefCount(new uint32_t(1)),
					m_Closed(false)
			{
				m_Owner->TransactionBeginRead();
			};
			ReadTransaction(const ReadTransaction & Other)
				: m_Owner(Other.m_Owner),
					m_RefCount(Other.m_RefCount),
					m_Closed(Other.m_Closed)
			{
				if (!m_Closed)
					INC_32(*m_RefCount);
			};
			~ReadTransaction()
			{
				if (!m_Closed && (DEC_32(*m_RefCount) == 0))
				{
					delete m_RefCount;
					m_Owner->TransactionEndRead();
				}
			};

			ReadTransaction & operator =(const ReadTransaction & Other)
			{
				if (!m_Closed && (DEC_32(*m_RefCount) == 0))
				{
					delete m_RefCount;
					m_Owner->TransactionEndRead();
				}

				m_Owner = Other.m_Owner;
				m_RefCount = Other.m_RefCount;
				m_Closed = Other.m_Closed;
				if (!m_Closed)
					INC_32(*m_RefCount);

				return *this;
			}

			void Close()
			{
				if (!m_Closed && (DEC_32(*m_RefCount) == 0))
				{
					delete m_RefCount;
					m_Owner->TransactionEndRead();
				}
				m_Closed = true;
			}

	};
	class WriteTransaction
	{
	private:
		CBlockManager * m_Owner;
		uint32_t volatile * m_RefCount;
		bool m_Closed;
	public:
		WriteTransaction()
			:	m_Owner(NULL),
				m_RefCount(NULL),
				m_Closed(true)
		{

		};
		WriteTransaction(CBlockManager & BlockManager)
			:	m_Owner(&BlockManager),
				m_RefCount(new uint32_t(1)),
				m_Closed(false)
		{
			m_Owner->TransactionBeginWrite();
		};
		WriteTransaction(const WriteTransaction & Other)
			: m_Owner(Other.m_Owner),
				m_RefCount(Other.m_RefCount),
				m_Closed(Other.m_Closed)
		{
			if (!m_Closed)
				INC_32(*m_RefCount);
		};
		~WriteTransaction()
		{
			if (!m_Closed && (DEC_32(*m_RefCount) == 0))
			{
				delete m_RefCount;
				m_Owner->TransactionEndWrite();
			}
		};

		WriteTransaction & operator =(const WriteTransaction & Other)
		{
			if (!m_Closed && (DEC_32(*m_RefCount) == 0))
			{
				delete m_RefCount;
				m_Owner->TransactionEndWrite();
			}

			m_Owner = Other.m_Owner;
			m_RefCount = Other.m_RefCount;
			m_Closed = Other.m_Closed;
			if (!m_Closed)
				INC_32(*m_RefCount);

			return *this;
		}

		void Close()
		{
			if (!m_Closed && (DEC_32(*m_RefCount) == 0))
			{
				delete m_RefCount;
				m_Owner->TransactionEndWrite();
			}
			m_Closed = true;
		}

	};

	uint32_t ScanFile(uint32_t FirstBlockStart, uint32_t HeaderSignature, uint32_t FileSize);

	template <typename BlockType>
	BlockType * ReadBlock(uint32_t BlockID, uint32_t & Size, uint32_t & Signature)
		{
			return reinterpret_cast<BlockType*>(_ReadBlock(BlockID, Size, Signature));
		};

	template <typename BlockType>
	BlockType * CreateBlock(uint32_t & BlockID, const uint32_t Signature, uint32_t Size = sizeof(BlockType))
		{
			return reinterpret_cast<BlockType*>(_CreateBlock(BlockID, Signature, Size));
		};

	template <typename BlockType>
	BlockType * CreateBlockVirtual(uint32_t & BlockID, const uint32_t Signature, uint32_t Size = sizeof(BlockType))
		{
			return reinterpret_cast<BlockType*>(_CreateBlockVirtual(BlockID, Signature, Size));
		};

	template <typename BlockType>
	uint32_t ResizeBlock(uint32_t BlockID, BlockType * & Buffer, uint32_t Size)
		{
			void * tmp = Buffer;
			uint32_t res = _ResizeBlock(BlockID, tmp, Size);
			Buffer = reinterpret_cast<BlockType*>(tmp);
			return res;
		};

	bool UpdateBlock(uint32_t BlockID, uint32_t Signature = 0);
	bool DeleteBlock(uint32_t BlockID);

	bool IsForcedVirtual(uint32_t BlockID);
	bool WriteBlockToDisk(uint32_t BlockID);
	bool MakeBlockVirtual(uint32_t BlockID);
};