1 /*/
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application, but
5  * could be used for other applications which could use the PoolFile
6  * class's functionality.
7  *
8  * PoolFile is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published
10  * by the Free Software Foundation; either version 2 of the License,
11  * or (at your option) any later version.
12  *
13  * PoolFile is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
21  */
22 
23 #ifndef __TPoolFile_H__CPP
24 #error this file must be included through TPoolFile.h NOT compiled on its own
25 #endif
26 
27 /* ???
28  * There are places that I have to do: container.erase(container.begin+index);
29  * see if I can eliminate this
30  */
31 
32 /*???
33  * 	In many functions i re-evaluate over and over SAT[poolId][index].. I should probably reduce that to  vector<RLogicalBlock> &poolSAT=SAT[poolId];
34  */
35 
36 /*???
37  *	For now I have chosen simply to write all data as little endian.  This could be improved by writing an endian indicator
38  *	and always write as the native endian.  Then upon reading the data check the endian indicated and convert only if necessary.
39  *	Have a version number increase so that previous versions of rezound loading a new file will say 'this code cannot read that version'
40  *	This way people running rezound on big endian hardware do not get penalized.  Conversion is done only when a file is take from one endian platform to another
41  */
42 
43 /* ??? One possible improvment, would be to fix this situation:
44  * 	blockSize=2048
45  * 	all pools are empty
46  * 	append 1024 bytes into pool0
47  * 	append 1 byte into pool1
48  * 	append 1024 more bytes into pool0
49  *
50  * 	- Right now, the append into pool1 uses the space immediately after the preceeding 1024 bytes of pool0
51  * 	  thus the second append into pool0 has to create a new entry in both the SAT and the physicalBlockList
52  *	- If I had reserved 1024 more bytes in the first append into pool0, then append into pool1 would have
53  *	  gone into physical address 2048, so the second append into pool0 could have simply increased the size
54  *	  of the logical block in the SAT for pool0
55  */
56 
57 /* perhaps some debugging #define could be enabled to avoid all the internal verification ??? */
58 
59 // ??? I didn't think of this until just now, but for fault tolerancy during a space modifications operation, I could simply
60 // restore to the backed up SAT just before the space modification method began if there was an error either in the logic or in
61 // extending the file's length
62 
63 #include <stdio.h> // for the printf errors... should probably use assert (but assumably temporary)  (It's also for printSAT())
64 
65 #if defined(__GNUC__) && (__GNUC__>=3)
66 	#ifdef TESTING_TPOOLFILE
67 		#define dprintf(...) printf(__VA_ARGS__)
68 	#else
69 		#define dprintf(...)
70 	#endif
71 #else
72 	#define dprintf
73 #endif
74 
75 
76 #include <stdexcept>
77 #include <utility>
78 #include <algorithm>
79 #include <iterator>
80 
81 #include <istring>
82 
83 #include <endian_util.h>
84 
85 #include "TPoolFile.h"
86 #include "TStaticPoolAccesser.h"
87 
88 #include <TAutoBuffer.h>
89 
90 
91 // -- Structural integrity is ensured by keeping a separate file to contain the SAT
92 // of the last known stable structure of the file.  These files are created when the file
93 // is opened and is removed just before the file is closed.  All changes to the structure
94 // of the file are made in such a way that the structure information from a SATFile will
95 // apply even if disaster occurs durring the structure change.  There are two sat files
96 // that I alternate between uses.
97 // -- At the moment, I don't sync() after structural changes, although I could... If
98 // data was of the utmost importance I could implement a flag that causes syncs after
99 // most every operation.
100 // --- Now every space modification causes the whole SAT to be written to disk, which makes
101 // TPoolFile not very efficient with small numerous space changes.  It was designed for large
102 // and relatively (compared to the data size) few changes to the space.  However, the bigger
103 // the maxBlockSize, the smaller the SAT will be in length, but the more expensive it is to
104 // hold a block in memory and read/write it to disk.
105 // ??? Optimization: I could avoid writting the WHOLE SAT to disk if I know that only a single
106 // value changed in the SAT and the number of the SAT entries did not change.  This would
107 // greatly improve the performance of small changes in a pools space when the block size is
108 // comparitivly large.
109 
110 // -- When using a TPoolFile among threads the mutual exclusion methods must be used
111 // before calling most any method (exceptions are simple methods like isOpen(), etc...).
112 // Generally, when a routine is to change the size of a pool, it should get an exclusive
113 // lock on the pool file ( exclusiveLock() ).  When a routine is not going to change
114 // the size of a pool, it can get a shared lock ( sharedLock() ) which allows multiple locks
115 // to be obtained while only one exclusive lock can be obtained at a time, only when no shared
116 // locks are existing.  There are exceptions of course.  Anything that calls backupSAT() should
117 // obtain an exclusive lock.
118 
119 // -- I still have yet to resolve all the places where we find an inconsistance and printf-then-exit
120 // Some of these could be converted to exceptions where it's on startup (like constructing the SAT)
121 // But some are sanity checks that verify the logic, and if they happen, then I've probably got a
122 // bug in my logic, or an unhandled situation that I didn't anticipate.  Perhaps I should put an
123 // #ifdef around each of these so I can turn them off, but so are so cheap that I should just leave
124 // them in.
125 
126 
127 #define MAX_POOL_NAME_LENGTH 256
128 #define FORMAT_SIGNATURE formatSignature // now a data member in the class instead of a constant
129 
130 #define FORMAT_VERSION 1
131 /* Format 0 - original
132  * Format 1 - started writing RPoolInfo::size and RLogicalBlock::logicalStart as p_addr_t instead of l_addr_t (since l_addr_t changes in ReZound depending on enable-largefile or not)
133  */
134 
135 #define INITIAL_CACHED_BLOCK_COUNT 4
136 
137 // Signature, EOF, Format Version, Dirty, Which SAT File, Meta Data Offset, Filler To Align To Disk Block Buffer
138 #define LEADING_DATA_SIZE (512)
139 
140 #define SIGNATURE_OFFSET 0
141 #define EOF_OFFSET (SIGNATURE_OFFSET+8)
142 #define FORMAT_VERSION_OFFSET (EOF_OFFSET+1)
143 #define DIRTY_INDICATOR_OFFSET (FORMAT_VERSION_OFFSET+4)
144 #define WHICH_SAT_FILE_OFFSET (DIRTY_INDICATOR_OFFSET+1)
145 #define META_DATA_OFFSET (WHICH_SAT_FILE_OFFSET+1)
146 //would be next #define NEXT_OFFSET (META_DATA_OFFSET+8)
147 
148 
149 template<class l_addr_t,class p_addr_t>
150 	TPoolFile<l_addr_t,p_addr_t>::TPoolFile(const blocksize_t _maxBlockSize,const char *_formatSignature) :
151 
152 	formatSignature(_formatSignature),
153 
154 	maxBlockSize(_maxBlockSize),
155 	maxLogicalAddress(~((l_addr_t)0)),
156 	maxPhysicalAddress(~((p_addr_t)0)),
157 
158 	pasm(blockFile)
159 {
160 	if(maxBlockSize<2)
161 		throw runtime_error(string(__func__)+" -- maxBlockSize is less than 2");
162 	if(maxLogicalAddress>maxPhysicalAddress)
163 		throw runtime_error(string(__func__)+" -- the logical address space is greater than the physical address space");
164 	if(maxBlockSize>maxLogicalAddress)
165 		throw runtime_error(string(__func__)+" -- the maxBlockSize is bigger than the logical address space");
166 
167 	init();
168 }
169 
170 template<class l_addr_t,class p_addr_t>
171 	TPoolFile<l_addr_t,p_addr_t>::TPoolFile(const TPoolFile<l_addr_t,p_addr_t> &src) :
172 
173 		// just initializing these const data-members
174 	formatSignature(""),
175 	maxBlockSize(0),
176 	maxLogicalAddress(0),
177 	maxPhysicalAddress(0),
178 
179 	pasm(blockFile)
180 {
181 	throw runtime_error(string(__func__)+" -- copy constructor invalid");
182 }
183 
184 template<class l_addr_t,class p_addr_t>
185 	TPoolFile<l_addr_t,p_addr_t>::~TPoolFile()
186 {
187 	exclusiveLock();
188 	try
189 	{
190 		if(opened)
191 		{
192 			// or set all accessers's poolFile members to NULL
193 			// need to probably wait for any outstanding accessers to deallocate
194 			while(!accessers.empty());
195 
196 			try
197 			{
198 				invalidateAllCachedBlocks();
199 			} catch(...) {}
200 			closeSATFiles(false);
201 			blockFile.close(false);
202 		}
203 		init(false);
204 		exclusiveUnlock();
205 	}
206 	catch(...)
207 	{
208 		exclusiveUnlock();
209 		// throw exceptions from the destructor ???
210 		throw;
211 	}
212 }
213 
214 
215 
216 template<class l_addr_t,class p_addr_t>
217 	void TPoolFile<l_addr_t,p_addr_t>::openFile(const string _filename,const bool canCreate)
218 {
219  	if(opened)
220  		throw runtime_error(string(__func__)+" -- file already opened");
221 
222 	try
223 	{
224 		init();
225 
226 		blockFile.open(_filename,canCreate);
227 		filename=_filename;
228 
229         	setup();
230 	}
231 	catch(...)
232 	{
233 		blockFile.close(false);
234 		throw;
235 	}
236 
237 
238 }
239 
240 template<class l_addr_t,class p_addr_t>
241 	void TPoolFile<l_addr_t,p_addr_t>::removeFile(const string filename)
242 {
243 		// ??? in the furture needs to as CMultiFile to remove the file since each file may be made up of several files if they go over the 2gig limit
244 	remove(filename.c_str());
245 	remove((filename+".SAT1").c_str());
246 	remove((filename+".SAT2").c_str());
247 }
248 
249 template<class l_addr_t,class p_addr_t>
250 	void TPoolFile<l_addr_t,p_addr_t>::setup()
251 {
252 	try
253 	{
254         	// if the file was not just created then it ought to have a valid pool file in it
255 		if(blockFile.getSize()>0)
256 		{
257 			// check signature string
258 			int8_t buffer[8]={0};
259 			blockFile.read(buffer,8,SIGNATURE_OFFSET);
260 		// ??? if sizeof(char)!=sizeof(int8_t) then strncmp won't work
261 			if(strncmp((const char *)buffer,FORMAT_SIGNATURE,8))
262 				throw runtime_error("invalid format signature");
263 
264 			// check format version
265 			uint32_t formatVersion;
266 			blockFile.read(&formatVersion,sizeof(formatVersion),FORMAT_VERSION_OFFSET);
267 			lethe(&formatVersion);
268 			if(formatVersion!=0 && formatVersion!=FORMAT_VERSION)
269 				throw runtime_error("invalid or unsupported format version: "+istring((int)formatVersion));
270 
271 			int8_t dirty;
272 			blockFile.read(&dirty,sizeof(dirty),DIRTY_INDICATOR_OFFSET);
273 			lethe(&dirty);
274 
275 			if(dirty)
276 			{	// file is marked as dirty
277 				blockFile.read(&whichSATFile,sizeof(whichSATFile),WHICH_SAT_FILE_OFFSET);
278 				lethe(&whichSATFile);
279 
280 				SATFilename=filename+".SAT";
281 
282 				openSATFiles(true);
283 				restoreSAT(formatVersion);
284 				joinAllAdjacentBlocks();
285 
286 				opened=true;
287 
288 				return;
289 			}
290 			else
291 			{
292 				uint64_t metaDataOffset;
293 				blockFile.read(&metaDataOffset,sizeof(metaDataOffset),META_DATA_OFFSET);
294 				lethe(&metaDataOffset);
295 				if(metaDataOffset<LEADING_DATA_SIZE)
296 					throw runtime_error("invalid meta data offset");
297 
298 				buildSATFromFile(&blockFile,metaDataOffset,formatVersion);
299 				joinAllAdjacentBlocks();
300 			}
301 		}
302 		else
303 			clear();
304 
305 		SATFilename=filename+".SAT";
306 
307 		writeMetaData(&blockFile,false);
308 
309 		openSATFiles();
310 		whichSATFile=1;
311 		backupSAT();
312 
313 		writeDirtyIndicator(true,&blockFile);
314 		opened=true;
315 	}
316 	catch(exception &e)
317 	{
318 		opened=false;
319 		closeSATFiles(false);
320 		init();
321 		throw runtime_error(string(__func__)+" -- "+string(e.what()));
322 	}
323 }
324 
325 /*
326  * NOTE: in a multi-threaded app, this method should be called with an
327  * exclusive locksince the file's seek positions will matter while it's
328  * copying.  Though, I do not check for this locked status since this
329  * might not be used in a multi-threaded app.
330  */
331 template<class l_addr_t,class p_addr_t>
332 	void TPoolFile<l_addr_t,p_addr_t>::copyToFile(const string _filename)
333 {
334 	if(!opened)
335 		throw runtime_error(string(__func__)+" -- no file is open");
336 	if(_filename==getFilename())
337 		throw runtime_error(string(__func__)+" -- attempting to copy to the file which is open");
338 
339 	invalidateAllCachedBlocks();
340 
341 	CMultiFile copyFile;
342 	copyFile.open(_filename,true);
343 
344 	CMultiFile::RHandle multiFileHandle1;
345 	CMultiFile::RHandle multiFileHandle2;
346 
347 	try
348 	{
349 		TAutoBuffer<int8_t> temp(maxBlockSize);
350 
351 		const p_addr_t length=blockFile.getSize();
352 
353 		blockFile.seek(0,multiFileHandle1);
354 		copyFile.seek(0,multiFileHandle2);
355 
356 		for(blocksize_t t=0;t<length/maxBlockSize;t++)
357 		{
358 			blockFile.read(temp,maxBlockSize,multiFileHandle1);
359 			copyFile.write(temp,maxBlockSize,multiFileHandle2);
360 		}
361 		if((length%maxBlockSize)!=0)
362 		{
363 			blockFile.read(temp,length%maxBlockSize,multiFileHandle1);
364 			copyFile.write(temp,length%maxBlockSize,multiFileHandle2);
365 		}
366 
367 		writeMetaData(&copyFile,true);
368 		writeDirtyIndicator(false,&copyFile);
369 		copyFile.close(false);
370 	}
371 	catch(exception &e)
372 	{
373 		copyFile.close(true);
374 		throw runtime_error(string(__func__)+" -- error copying to file -- "+e.what());
375 	}
376 }
377 
378 template<class l_addr_t,class p_addr_t>
379 	void TPoolFile<l_addr_t,p_addr_t>::closeFile(const bool _defrag,const bool _removeFile)
380 {
381 	if(!opened)
382 		return;
383 
384 	invalidateAllCachedBlocks();
385 
386 	// need to probably wait for any outstanding accessers to deallocate
387 	if(_removeFile)
388 	{
389 		closeSATFiles();
390 		blockFile.close(true);
391 	}
392 	else
393 	{
394 		if(_defrag)
395 			defrag();
396 
397 		writeMetaData(&blockFile,true);
398 		writeDirtyIndicator(false,&blockFile);
399 
400 		closeSATFiles();
401 
402 		blockFile.sync();
403 		blockFile.close(false);
404 	}
405 	init();
406 }
407 
408 template<class l_addr_t,class p_addr_t>
409 	const bool TPoolFile<l_addr_t,p_addr_t>::isOpen() const
410 {
411 	return opened;
412 }
413 
414 template<class l_addr_t,class p_addr_t>
415 	const p_addr_t TPoolFile<l_addr_t,p_addr_t>::getFileSize() const
416 {
417 	return blockFile.getActualSize();
418 }
419 
420 template<class l_addr_t,class p_addr_t>
421 	void TPoolFile<l_addr_t,p_addr_t>::rename(const string newFilename)
422 {
423 	if(!opened)
424 		throw runtime_error(string(__func__)+" -- no file is open");
425 
426 	blockFile.rename(newFilename);
427 	SATFiles[0].rename(newFilename+".SAT1");
428 	SATFiles[1].rename(newFilename+".SAT2");
429 }
430 
431 
432 template<class l_addr_t,class p_addr_t>
433 	void TPoolFile<l_addr_t,p_addr_t>::flushData()
434 {
435 	invalidateAllCachedBlocks();
436 
437 	backupSAT();
438 
439 	SATFiles[0].sync();
440 	SATFiles[1].sync();
441 	blockFile.sync();
442 }
443 
444 template<class l_addr_t,class p_addr_t>
445 	const string TPoolFile<l_addr_t,p_addr_t>::getFilename() const
446 {
447 	return filename;
448 }
449 
450 // Non-Const Pool Accesser Methods
451 template<class l_addr_t,class p_addr_t>
452 	template<class pool_element_t> TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > TPoolFile<l_addr_t,p_addr_t>::getPoolAccesser(const poolId_t poolId)
453 {
454 	if(!opened)
455 		throw runtime_error(string(__func__)+" -- no file is open");
456 	if(!isValidPoolId(poolId))
457 		throw runtime_error(string(__func__)+" -- invalid poolId parameter: "+istring(poolId));
458 	if(sizeof(pool_element_t)!=pools[poolId].alignment)
459 		throw runtime_error(string(__func__)+" -- method instantiated with a type whose size does not match the pool's alignment: sizeof(pool_element_t): "+istring(sizeof(pool_element_t))+" -- "+getPoolDescription(poolId));
460 
461 	TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > accesser(this,poolId);
462 	return accesser;
463 }
464 
465 template<class l_addr_t,class p_addr_t>
466 	template<class pool_element_t> TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > TPoolFile<l_addr_t,p_addr_t>::getPoolAccesser(const string poolName)
467 {
468 	return getPoolAccesser<pool_element_t>(getPoolIdByName(poolName));
469 }
470 
471 
472 // Const Pool Accesser Methods
473 template<class l_addr_t,class p_addr_t>
474 	template<class pool_element_t> const TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > TPoolFile<l_addr_t,p_addr_t>::getPoolAccesser(const poolId_t poolId) const
475 {
476 	if(!opened)
477 		throw runtime_error(string(__func__)+" -- no file is open");
478 	if(!isValidPoolId(poolId))
479 		throw runtime_error(string(__func__)+" -- invalid poolId parameter: "+istring(poolId));
480 	if(sizeof(pool_element_t)!=pools[poolId].alignment)
481 		throw runtime_error(string(__func__)+" -- method instantiated with a type whose size does not match the pool's alignment: sizeof(pool_element_t): "+istring(sizeof(pool_element_t))+" -- "+getPoolDescription(poolId));
482 
483 	const TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > accesser(const_cast<TPoolFile<l_addr_t,p_addr_t> *>(this),poolId);
484 	return accesser;
485 }
486 
487 template<class l_addr_t,class p_addr_t>
488 	template<class pool_element_t> const TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > TPoolFile<l_addr_t,p_addr_t>::getPoolAccesser(const string poolName) const
489 {
490 	return getPoolAccesser<pool_element_t>(getPoolIdByName(poolName));
491 }
492 
493 
494 
495 template<class l_addr_t,class p_addr_t>
496 	l_addr_t TPoolFile<l_addr_t,p_addr_t>::readPoolRaw(const poolId_t poolId,void *buffer,l_addr_t readSize,l_addr_t pos)
497 {
498 	if(!opened)
499 		throw runtime_error(string(__func__)+" -- no file is open");
500 	if(!isValidPoolId(poolId))
501 		throw runtime_error(string(__func__)+" -- invalid poolId parameter: "+istring(poolId));
502 
503 	TStaticPoolAccesser<uint8_t,TPoolFile<l_addr_t,p_addr_t> > accesser(const_cast<TPoolFile<l_addr_t,p_addr_t> *>(this),poolId);
504 	accesser.seek(pos);
505 	readSize=min(readSize,accesser.getSize()-pos);
506 	accesser.read((uint8_t *)buffer,readSize);
507 	return readSize;
508 }
509 
510 template<class l_addr_t,class p_addr_t>
511 	l_addr_t TPoolFile<l_addr_t,p_addr_t>::readPoolRaw(const string poolName,void *buffer,l_addr_t readSize,l_addr_t pos)
512 {
513 	return readPoolRaw(getPoolIdByName(poolName),buffer,readSize,pos);
514 }
515 
516 template<class l_addr_t,class p_addr_t>
517 	l_addr_t TPoolFile<l_addr_t,p_addr_t>::writePoolRaw(const poolId_t poolId,void *buffer,l_addr_t writeSize,l_addr_t pos)
518 {
519 	if(!opened)
520 		throw runtime_error(string(__func__)+" -- no file is open");
521 	if(!isValidPoolId(poolId))
522 		throw runtime_error(string(__func__)+" -- invalid poolId parameter: "+istring(poolId));
523 
524 	TStaticPoolAccesser<uint8_t,TPoolFile<l_addr_t,p_addr_t> > accesser(const_cast<TPoolFile<l_addr_t,p_addr_t> *>(this),poolId);
525 	accesser.seek(pos);
526 	writeSize=min(writeSize,accesser.getSize()-pos);
527 	accesser.write((uint8_t *)buffer,writeSize);
528 	return writeSize;
529 }
530 
531 template<class l_addr_t,class p_addr_t>
532 	l_addr_t TPoolFile<l_addr_t,p_addr_t>::writePoolRaw(const string poolName,void *buffer,l_addr_t writeSize,l_addr_t pos)
533 {
534 	return writePoolRaw(getPoolIdByName(poolName),buffer,writeSize,pos);
535 }
536 
537 template<class l_addr_t,class p_addr_t>
538 	template<class pool_element_t> TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > TPoolFile<l_addr_t,p_addr_t>::createPool(const string poolName,const bool throwOnExistance)
539 {
540 	if(!opened)
541 		throw runtime_error(string(__func__)+" -- no file is open");
542 	prvCreatePool(poolName,sizeof(pool_element_t),throwOnExistance);
543 
544 	return getPoolAccesser<pool_element_t>(poolName);
545 }
546 
547 
548 
549 template<class l_addr_t,class p_addr_t>
550 	void TPoolFile<l_addr_t,p_addr_t>::clear()
551 {
552 	if(!accessers.empty())
553 		throw runtime_error(string(__func__)+" -- there are outstanding accessors");
554 
555 	invalidateAllCachedBlocks();
556 	poolNames.clear();
557 	SAT.clear();
558 	pools.clear();
559 	pasm.free_all();
560 	pasm.make_file_smallest();
561 	if(isOpen())
562 		backupSAT();
563 }
564 
565 template<class l_addr_t,class p_addr_t>
566 	void TPoolFile<l_addr_t,p_addr_t>::removePool(const poolId_t poolId)
567 {
568 	if(!opened)
569 		throw runtime_error(string(__func__)+" -- no file is open");
570 	if(!isValidPoolId(poolId))
571 		throw runtime_error(string(__func__)+" -- invalid poolId: "+istring(poolId));
572 
573 	invalidateAllCachedBlocks(false,poolId);
574 
575 	// remove poolName with poolId of the parameter
576 	for(map<const string,poolId_t>::const_iterator t=poolNames.begin();t!=poolNames.end();t++)
577 	{
578 		if(t->second==poolId)
579 		{
580 			poolNames.erase(t->first);
581 			break;
582 		}
583 	}
584 
585 	// mark pool as not valid
586 	pools[poolId].size=0;
587 	pools[poolId].alignment=0;
588 	pools[poolId].isValid=false;
589 
590 	for(size_t t=0;t<SAT[poolId].size();t++)
591 		pasm.free(SAT[poolId][t].physicalStart); // ??? there might be a more efficient way than calling free_physical for each
592 	SAT[poolId].clear();
593 
594 	pasm.make_file_smallest();
595 
596 	backupSAT();
597 }
598 
599 
600 template<class l_addr_t,class p_addr_t>
601 	void TPoolFile<l_addr_t,p_addr_t>::removePool(const string poolName,const bool throwIfNotExists)
602 {
603 	poolId_t id;
604 	try
605 	{
606 		id=getPoolIdByName(poolName);
607 	}
608 	catch(...)
609 	{
610 		if(throwIfNotExists)
611 			throw;
612 		else
613 			return;
614 	}
615 
616 	removePool(id);
617 }
618 
619 template<class l_addr_t,class p_addr_t>
620 	const typename TPoolFile<l_addr_t,p_addr_t>::alignment_t TPoolFile<l_addr_t,p_addr_t>::getPoolAlignment(const poolId_t poolId) const
621 {
622 	if(!opened)
623 		throw runtime_error(string(__func__)+" -- no file is open");
624 	if(!isValidPoolId(poolId))
625 		throw runtime_error(string(__func__)+" -- invalid poolId parameter: "+istring(poolId));
626 
627 	const alignment_t alignment=pools[poolId].alignment;
628 
629 	return alignment;
630 }
631 
632 template<class l_addr_t,class p_addr_t>
633 	const typename TPoolFile<l_addr_t,p_addr_t>::alignment_t TPoolFile<l_addr_t,p_addr_t>::getPoolAlignment(const string poolName) const
634 {
635 	return getPoolAlignment(getPoolIdByName(poolName));
636 }
637 
638 template<class l_addr_t,class p_addr_t>
639 	void TPoolFile<l_addr_t,p_addr_t>::setPoolAlignment(const poolId_t poolId,size_t alignment)
640 {
641 	if(!opened)
642 		throw runtime_error(string(__func__)+" -- no file is open");
643 	if(!isValidPoolId(poolId))
644 		throw runtime_error(string(__func__)+" -- invalid poolId parameter: "+istring(poolId));
645 	if(pools[poolId].size>0)
646 		throw runtime_error(string(__func__)+" -- pool must be empty to change alignment");
647 	if(alignment==0 || alignment>maxBlockSize)
648 		throw runtime_error(string(__func__)+" -- invalid alignment: "+istring(alignment)+" alignment must be 0 < alignment <= maxBlockSize (which is: "+istring(maxBlockSize)+")");
649 
650 	invalidateAllCachedBlocks(false,poolId);
651 
652 	pools[poolId].alignment=alignment;
653 
654 	backupSAT();
655 }
656 
657 template<class l_addr_t,class p_addr_t>
658 	void TPoolFile<l_addr_t,p_addr_t>::setPoolAlignment(const string poolName,size_t alignment)
659 {
660 	setPoolAlignment(getPoolIdByName(poolName),alignment);
661 }
662 
663 template<class l_addr_t,class p_addr_t>
664 	void TPoolFile<l_addr_t,p_addr_t>::swapPools(const poolId_t poolId1,const poolId_t poolId2)
665 {
666 	if(!opened)
667 		throw runtime_error(string(__func__)+" -- no file is open");
668 
669 	if(!isValidPoolId(poolId1))
670 		throw runtime_error(string(__func__)+" -- invalid poolId1 parameter: "+istring(poolId1));
671 	if(!isValidPoolId(poolId2))
672 		throw runtime_error(string(__func__)+" -- invalid poolId2 parameter: "+istring(poolId2));
673 	if(poolId1==poolId2)
674 		return;
675 
676 	invalidateAllCachedBlocks(false,poolId1);
677 	invalidateAllCachedBlocks(false,poolId2);
678 
679 	// swap SATs for two pools
680 	vector<RLogicalBlock> tempSAT=SAT[poolId1];
681 	SAT[poolId1]=SAT[poolId2];
682 	SAT[poolId2]=tempSAT;
683 
684 	// swap pool size info
685 	const RPoolInfo tempPool=pools[poolId1];
686 	pools[poolId1]=pools[poolId2];
687 	pools[poolId2]=tempPool;
688 }
689 
690 template<class l_addr_t,class p_addr_t>
691 	void TPoolFile<l_addr_t,p_addr_t>::swapPools(const string poolName1,const string poolName2)
692 {
693 	swapPools(getPoolIdByName(poolName1),getPoolIdByName(poolName2));
694 }
695 
696 
697 
698 template<class l_addr_t,class p_addr_t>
699 	const bool TPoolFile<l_addr_t,p_addr_t>::prvGetPoolIdByName(const string poolName,poolId_t &poolId) const
700 {
701 	map<const string,poolId_t>::const_iterator i=poolNames.find(poolName);
702 	if(i==poolNames.end())
703 		return false;
704 
705 	poolId=i->second;
706 	return true;
707 }
708 
709 template<class l_addr_t,class p_addr_t>
710 	const typename TPoolFile<l_addr_t,p_addr_t>::poolId_t TPoolFile<l_addr_t,p_addr_t>::getPoolIdByName(const string poolName) const
711 {
712 	if(!opened)
713 		throw runtime_error(string(__func__)+" -- no file is open");
714 
715 	poolId_t poolId=0;
716 	if(prvGetPoolIdByName(poolName,poolId))
717 		return poolId;
718 	else
719 		throw runtime_error(string(__func__)+" -- name not found: "+poolName);
720 }
721 
722 template<class l_addr_t,class p_addr_t>
723 	const size_t TPoolFile<l_addr_t,p_addr_t>::getPoolIndexCount() const
724 {
725 	if(!opened)
726 		throw runtime_error(string(__func__)+" -- no file is open");
727 	return poolNames.size();
728 }
729 
730 template<class l_addr_t,class p_addr_t>
731 	const typename TPoolFile<l_addr_t,p_addr_t>::poolId_t TPoolFile<l_addr_t,p_addr_t>::getPoolIdByIndex(const size_t index) const // where index is 0 to getPoolIndexCount()-1
732 {
733 	if(index>=poolNames.size())
734 		throw runtime_error(string(__func__)+" -- index out of bounds: "+istring(index));
735 
736 	map<const string,poolId_t>::const_iterator i=poolNames.begin();
737 	advance(i,index);
738 	return i->second;
739 }
740 
741 template<class l_addr_t,class p_addr_t>
742 	const string TPoolFile<l_addr_t,p_addr_t>::getPoolNameById(const poolId_t poolId) const
743 {
744 	if(!isValidPoolId(poolId))
745 		throw runtime_error(string(__func__)+" -- poolId parameter out of bounds: "+istring(poolId));
746 
747 	for(map<const string,poolId_t>::const_iterator i=poolNames.begin();i!=poolNames.end();i++)
748 	{
749 		if(i->second==poolId)
750 			return i->first;
751 	}
752 
753 	printf("uh oh.. pool name not found for pool id: %u\n",(unsigned)poolId);
754 	exit(0);
755 	return "";
756 }
757 template<class l_addr_t,class p_addr_t>
758 	const bool  TPoolFile<l_addr_t,p_addr_t>::isValidPoolId(const poolId_t poolId) const
759 {
760 	return poolId<pools.size() && pools[poolId].isValid;
761 }
762 
763 
764 template<class l_addr_t,class p_addr_t>
765 	const bool TPoolFile<l_addr_t,p_addr_t>::containsPool(const string poolName) const
766 {
767 	return poolNames.find(poolName)!=poolNames.end();
768 }
769 
770 
771 
772 
773 // Mutual Exclusion Methods
774 template<class l_addr_t,class p_addr_t>
775 	void TPoolFile<l_addr_t,p_addr_t>::sharedLock() const
776 {
777 	structureMutex.readLock();
778 }
779 
780 template<class l_addr_t,class p_addr_t>
781 	const bool TPoolFile<l_addr_t,p_addr_t>::sharedTrylock() const
782 {
783 	return structureMutex.tryReadLock();
784 }
785 
786 template<class l_addr_t,class p_addr_t>
787 	void TPoolFile<l_addr_t,p_addr_t>::sharedUnlock() const
788 {
789 	structureMutex.unlock();
790 }
791 
792 template<class l_addr_t,class p_addr_t>
793 	const size_t TPoolFile<l_addr_t,p_addr_t>::getSharedLockCount() const
794 {
795 	return structureMutex.getReadLockCount();
796 }
797 
798 template<class l_addr_t,class p_addr_t>
799 	void TPoolFile<l_addr_t,p_addr_t>::exclusiveLock() const
800 {
801 	structureMutex.writeLock();
802 }
803 
804 template<class l_addr_t,class p_addr_t>
805 	const bool TPoolFile<l_addr_t,p_addr_t>::exclusiveTrylock() const
806 {
807 	return structureMutex.tryWriteLock();
808 }
809 
810 template<class l_addr_t,class p_addr_t>
811 	void TPoolFile<l_addr_t,p_addr_t>::exclusiveUnlock() const
812 {
813 	structureMutex.unlock();
814 }
815 
816 template<class l_addr_t,class p_addr_t>
817 	const bool TPoolFile<l_addr_t,p_addr_t>::isExclusiveLocked() const
818 {
819 	return structureMutex.isLockedForWrite();
820 }
821 
822 
823 
824 
825 
826 // --- Private Methods --------------------------------------------------------
827 
828 // Misc
829 template<class l_addr_t,class p_addr_t>
830 	void TPoolFile<l_addr_t,p_addr_t>::init(const bool createInitialCachedBlocks)
831 {
832 	filename="";
833 
834 	SAT.clear();
835 	SAT.reserve(64);
836 
837 	pasm.free_all();
838 
839 	poolNames.clear();
840 
841 	pools.clear();
842 	pools.reserve(64);
843 
844 	accessers.clear();
845 
846 	if(createInitialCachedBlocks)
847 	{
848 		while(unusedCachedBlocks.size()>INITIAL_CACHED_BLOCK_COUNT)
849 		{
850 			delete unusedCachedBlocks.front();
851 			unusedCachedBlocks.pop_front();
852 		}
853 
854 		while(unusedCachedBlocks.size()<INITIAL_CACHED_BLOCK_COUNT)
855 			unusedCachedBlocks.push_back(new RCachedBlock(maxBlockSize));
856 	}
857 	else
858 	{
859 		while(!unusedCachedBlocks.empty())
860 		{
861 			delete unusedCachedBlocks.front();
862 			unusedCachedBlocks.pop_front();
863 		}
864 	}
865 
866 	while(!unreferencedCachedBlocks.empty())
867 	{
868 		typename set<RCachedBlock *>::iterator i=unreferencedCachedBlocks.begin();
869 		delete *i;
870 		unreferencedCachedBlocks.erase(i);
871 	}
872 	while(!activeCachedBlocks.empty())
873 	{
874 		typename set<RCachedBlock *>::iterator i=activeCachedBlocks.begin();
875 		delete *i;
876 		activeCachedBlocks.erase(i);
877 	}
878 
879 	opened=false;
880 }
881 
882 template<class l_addr_t,class p_addr_t>
883 	void TPoolFile<l_addr_t,p_addr_t>::verifyAllBlockInfo(bool expectContinuousPhysicalAllocs)
884 {
885 	// for each pool, make sure the logical space is continuous with no gaps
886 	// and for each block in the logical space, make sure there is a physical block with the right size
887 	for(size_t poolId=0;poolId<pools.size();poolId++)
888 	{
889 		if(!pools[poolId].isValid)
890 			continue;
891 
892 		l_addr_t expectedStart=0;
893 
894 		const blocksize_t maxBlockSize=getMaxBlockSizeFromAlignment(pools[poolId].alignment);
895 
896 		for(size_t t=0;t<SAT[poolId].size();t++)
897 		{
898 			const RLogicalBlock &logicalBlock=SAT[poolId][t];
899 
900 			if(logicalBlock.logicalStart!=expectedStart)
901 			{
902 				printSAT();
903 				printf("pool: %u -- logical start wasn't what was expected in logical block: %lld\n",(unsigned)poolId,(long long)expectedStart);
904 				logicalBlock.print();
905 				exit(1);
906 			}
907 
908 			if(!pasm.isAlloced(logicalBlock.physicalStart))
909 			{
910 				printSAT();
911 				printf("pool: %u -- physicalStart isn't allocated\n",(unsigned)poolId);
912 				logicalBlock.print();
913 				exit(1);
914 			}
915 
916 			if(pasm.getAllocedSize(logicalBlock.physicalStart)!=logicalBlock.size)
917 			{
918 				printSAT();
919 				printf("pool: %u physical block's size isn't the same as the logical block's:\n",(unsigned)poolId);
920 				logicalBlock.print();
921 				printf("physical block's size: %lld\n",(long long)pasm.getAllocedSize(logicalBlock.physicalStart));
922 				exit(1);
923 			}
924 
925 			if(t!=SAT[poolId].size()-1)
926 			{ // check if next block could be joined with this block, just notify if this is true, it's not a serious problem
927 				const RLogicalBlock &nextLogicalBlock=SAT[poolId][t+1];
928 
929 				if((logicalBlock.physicalStart+logicalBlock.size)==nextLogicalBlock.physicalStart &&
930 				   (logicalBlock.size+nextLogicalBlock.size)<=maxBlockSize)
931 				{
932 					printf("NOTE: two blocks could be joined: %u and %u\n",(unsigned)t,(unsigned)t+1);
933 					logicalBlock.print();
934 					nextLogicalBlock.print();
935 				}
936 			}
937 
938 			expectedStart+=logicalBlock.size;
939 
940 			// make sure no other logical block exists in any pool with an overlapping physical start
941 			for(size_t x=poolId;x<pools.size();x++)
942 			{
943 				for(size_t y= (x==poolId) ? t+1 : 0;y<SAT[x].size();y++)
944 				{
945 					if(CPhysicalAddressSpaceManager::overlap(logicalBlock.physicalStart,logicalBlock.size,SAT[x][y].physicalStart,SAT[x][y].size))
946 					{
947 						printSAT();
948 						printf("pool: %u -- two blocks are occupying the same physical space\n",(unsigned)poolId);
949 						logicalBlock.print();
950 						printf("and pool: %u\n",(unsigned)x);
951 						SAT[x][y].print();
952 						exit(1);
953 					}
954 				}
955 			}
956 		}
957 
958 		if(SAT[poolId].size()>0)
959 		{
960 			const RLogicalBlock &b=SAT[poolId][SAT[poolId].size()-1];
961 			if((b.logicalStart+b.size)!=getPoolSize(poolId))
962 			{
963 				printSAT();
964 				printf("pool: %u -- last address doesn't equal up to poolSize\n",(unsigned)poolId);
965 				exit(1);
966 			}
967 
968 		}
969 	}
970 
971 	pasm.verify(expectContinuousPhysicalAllocs);
972 }
973 
974 template<class l_addr_t,class p_addr_t>
975 	const p_addr_t TPoolFile<l_addr_t,p_addr_t>::getProceedingPoolSizes(const poolId_t poolId) const
976 {
977 	p_addr_t proceedingPoolSizes=0;
978 	for(size_t t=0;t<poolId;t++)
979 	{
980 		if(pools[t].isValid) // wouldn't really matter cause size of invalids is supposed to be zero
981 			proceedingPoolSizes+=pools[t].size;
982 	}
983 	return proceedingPoolSizes;
984 }
985 
986 template<class l_addr_t,class p_addr_t>
987 	const typename TPoolFile<l_addr_t,p_addr_t>::blocksize_t TPoolFile<l_addr_t,p_addr_t>::getMaxBlockSizeFromAlignment(const alignment_t alignment) const
988 {
989 	if(maxBlockSize<=(maxBlockSize%alignment))
990 		throw runtime_error(string(__func__)+" -- alignment size is too big");
991 
992 	return maxBlockSize-(maxBlockSize%alignment);
993 }
994 
995 template<class l_addr_t,class p_addr_t>
996 	const l_addr_t TPoolFile<l_addr_t,p_addr_t>::getPoolSize(const poolId_t poolId) const
997 {
998 	if(!isValidPoolId(poolId))
999 		throw runtime_error(string(__func__)+" -- invalid poolId parameter: "+istring(poolId));
1000 
1001 	return pools[poolId].size;
1002 }
1003 
1004 template<class l_addr_t,class p_addr_t>
1005 	const l_addr_t TPoolFile<l_addr_t,p_addr_t>::getPoolSize(const string poolName) const
1006 {
1007 	return getPoolSize(getPoolIdByName(poolName));
1008 }
1009 
1010 template<class l_addr_t,class p_addr_t>
1011 	void TPoolFile<l_addr_t,p_addr_t>::writeMetaData(CMultiFile *f,bool writeSAT)
1012 {
1013 	if(f==&blockFile) // only do if we're working on our blockFile
1014 		pasm.make_file_smallest();
1015 
1016 	if(blockFile.getSize()<LEADING_DATA_SIZE)
1017 		blockFile.setSize(LEADING_DATA_SIZE);
1018 
1019 	// write meta and user info
1020 	uint64_t metaDataOffset=f->getSize();
1021 
1022 	if(writeSAT)
1023 		writeSATToFile(f,metaDataOffset);
1024 
1025 	// Signature
1026 	f->write(FORMAT_SIGNATURE,8,SIGNATURE_OFFSET);
1027 
1028 	// EOF
1029 	int8_t eofChar=26;
1030 	f->write(&eofChar,sizeof(eofChar),EOF_OFFSET);
1031 
1032 	// Format Version
1033 	{
1034 	uint32_t formatVersion=FORMAT_VERSION;
1035 	hetle(&formatVersion);
1036 	f->write(&formatVersion,sizeof(formatVersion),FORMAT_VERSION_OFFSET);
1037 	}
1038 
1039 	// Dirty
1040 	int8_t dirty=1;
1041 	f->write(&dirty,sizeof(dirty),DIRTY_INDICATOR_OFFSET);
1042 
1043 	// Which SAT File
1044 	{
1045 	uint8_t _whichSATFile=0;
1046 	f->write(&_whichSATFile,sizeof(_whichSATFile),WHICH_SAT_FILE_OFFSET);
1047 	}
1048 
1049 	// Meta Data Offset
1050 	hetle(&metaDataOffset);
1051 	f->write(&metaDataOffset,sizeof(metaDataOffset),META_DATA_OFFSET);
1052 }
1053 
1054 template<class l_addr_t,class p_addr_t>
1055 	void TPoolFile<l_addr_t,p_addr_t>::prvCreatePool(const string poolName,const alignment_t alignment,const bool throwOnExistance,const bool reuseOldPoolIds)
1056 {
1057 	/* the stuff about a the poolName being "__internal_invalid_pool" deals with a restoring of the SAT from disk and needing to create same poolIds (isValid and !isValid) as before */
1058 	poolId_t poolId;
1059 	if(poolName=="__internal_invalid_pool" || !prvGetPoolIdByName(poolName,poolId))
1060 	{	// poolName not found
1061 		if(poolName.length()>MAX_POOL_NAME_LENGTH)
1062 			throw runtime_error(string(__func__)+" -- pool name too long: "+poolName);
1063 
1064 		// this is where the poolId get's created.. either pools.size() or a recycled element in the pools vector
1065 
1066 		// check for any unused positions in the pools vector
1067 		poolId_t newPoolId=pools.size(); // <-- the would-be new one if we don't find one to use
1068 		if(reuseOldPoolIds) // sometimes (buildSATFromFile()) we don't want to use the old poolIds because we want to preserve original poolId numbers
1069 		{
1070 			for(size_t t=0;t<pools.size();t++)
1071 			{
1072 				if(!pools[t].isValid)
1073 				{
1074 					newPoolId=t;
1075 					break;
1076 				}
1077 			}
1078 		}
1079 
1080 
1081 		// either record the name of the new pool or set as not valid
1082 		if(poolName!="__internal_invalid_pool")
1083 			poolNames.insert(make_pair(poolName,newPoolId));
1084 		addPool(newPoolId,alignment,poolName!="__internal_invalid_pool");
1085 	}
1086 	else
1087 	{
1088 		// pool name already exists
1089 		if(throwOnExistance)
1090 			throw runtime_error(string(__func__)+" -- pool name already exists: "+poolName);
1091 	}
1092 }
1093 
1094 template<class l_addr_t,class p_addr_t>
1095 	void TPoolFile<l_addr_t,p_addr_t>::addPool(const poolId_t poolId,const alignment_t alignment,bool isValid)
1096 {
1097 	invalidateAllCachedBlocks(false,poolId);
1098 
1099 	if((isValid && alignment==0) || alignment>maxBlockSize)
1100 		throw runtime_error(string(__func__)+" -- invalid alignment: "+istring(alignment)+" alignment must be 0 < alignment <= maxBlockSize (which is: "+istring(maxBlockSize)+")");
1101 
1102 	if(poolId<pools.size() && !pools[poolId].isValid)
1103 	{ // reusing existing element the SAT vector
1104 		pools[poolId].size=0;
1105 		pools[poolId].alignment=alignment;
1106 		pools[poolId].isValid=isValid;
1107 	}
1108 	else if(poolId==pools.size())
1109 	{ // create new entry in the SAT vector
1110 		appendNewSAT();
1111 		pools.push_back(RPoolInfo(0,alignment,isValid));
1112 	}
1113 	else
1114 		throw runtime_error(string(__func__)+" -- invalid new pool id or pool already exists: "+istring(poolId));
1115 }
1116 
1117 
1118 template<class l_addr_t,class p_addr_t>
1119 	const string TPoolFile<l_addr_t,p_addr_t>::getPoolDescription(const poolId_t poolId) const
1120 {
1121 	return "poolId: "+istring(poolId)+" name: '"+getPoolNameById(poolId)+"' byte size: "+istring(getPoolSize(poolId))+" byte alignment: "+istring(getPoolAlignment(poolId));
1122 }
1123 
1124 template<class l_addr_t,class p_addr_t>
1125 	void TPoolFile<l_addr_t,p_addr_t>::writeDirtyIndicator(const bool dirty,CMultiFile *f)
1126 {
1127 	int8_t temp;
1128 	temp=dirty ? 1 : 0;
1129 	f->write(&temp,sizeof(temp),DIRTY_INDICATOR_OFFSET);
1130 }
1131 
1132 template<class l_addr_t,class p_addr_t>
1133 	void TPoolFile<l_addr_t,p_addr_t>::appendNewSAT()
1134 {
1135 	SAT.push_back(vector<RLogicalBlock>());
1136 	SAT[SAT.size()-1].reserve(1024);
1137 }
1138 
1139 template<class l_addr_t,class p_addr_t>
1140 	void TPoolFile<l_addr_t,p_addr_t>::offsetLogicalAddressSpace(typename vector<RLogicalBlock>::iterator first,typename vector<RLogicalBlock>::iterator end,const l_addr_t offset,int add_or_sub)
1141 {
1142 	if(add_or_sub>0)
1143 	{
1144 		for(typename vector<RLogicalBlock>::iterator i=first;i!=end;i++)
1145 			i->logicalStart+=offset;
1146 	}
1147 	else
1148 	{
1149 		for(typename vector<RLogicalBlock>::iterator i=first;i!=end;i++)
1150 			i->logicalStart-=offset;
1151 	}
1152 }
1153 
1154 
1155 
1156 
1157 // Structural Integrity Methods
1158 template<class l_addr_t,class p_addr_t>
1159 	void TPoolFile<l_addr_t,p_addr_t>::openSATFiles(const bool mustExist)
1160 {
1161 	SATFiles[0].open(SATFilename+"1",!mustExist);
1162 	SATFiles[1].open(SATFilename+"2",!mustExist);
1163 }
1164 
1165 template<class l_addr_t,class p_addr_t>
1166 	void TPoolFile<l_addr_t,p_addr_t>::closeSATFiles(const bool removeFiles)
1167 {
1168 	SATFiles[0].sync();
1169 	SATFiles[1].sync();
1170 
1171 	SATFiles[0].close(removeFiles);
1172 	SATFiles[1].close(removeFiles);
1173 }
1174 
1175 template<class l_addr_t,class p_addr_t>
1176 	void TPoolFile<l_addr_t,p_addr_t>::writeWhichSATFile()
1177 {
1178 	uint8_t temp;
1179 	temp=whichSATFile;
1180 	blockFile.write(&temp,sizeof(temp),WHICH_SAT_FILE_OFFSET);
1181 }
1182 
1183 template<class l_addr_t,class p_addr_t>
1184 	void TPoolFile<l_addr_t,p_addr_t>::backupSAT()
1185 {
1186 	whichSATFile= ((whichSATFile==0) ? 1 : 0);
1187 
1188 	writeSATToFile(&SATFiles[whichSATFile],0);
1189 	writeWhichSATFile();
1190 }
1191 
1192 template<class l_addr_t,class p_addr_t>
1193 	void TPoolFile<l_addr_t,p_addr_t>::writeSATToFile(CMultiFile *f,const p_addr_t writeWhere)
1194 {
1195 	// ??? probably want to put a SAT file signature here to make sure it's a SAT file
1196 
1197 	CMultiFile::RHandle multiFileHandle;
1198 	f->seek(writeWhere,multiFileHandle);
1199 
1200 	// write number of pools
1201 	{
1202 	uint32_t poolCount=SAT.size();
1203 	hetle(&poolCount);
1204 	f->write(&poolCount,sizeof(poolCount),multiFileHandle);
1205 	}
1206 
1207 	for(size_t poolId=0;poolId<SAT.size();poolId++)
1208 	{
1209 		// write name of pool
1210 		if(pools[poolId].isValid)
1211 			writeString(getPoolNameById(poolId),f,multiFileHandle);
1212 		else
1213 			writeString(string("__internal_invalid_pool"),f,multiFileHandle);
1214 
1215 		// write pool info structure
1216 		pools[poolId].writeToFile(f,multiFileHandle);
1217 
1218 		// write number of SAT entries for this pool
1219 		{
1220 		uint32_t SATSize=SAT[poolId].size();
1221 		hetle(&SATSize);
1222 		f->write(&SATSize,sizeof(SATSize),multiFileHandle);
1223 		}
1224 
1225 		TAutoBuffer<uint8_t> mem(SAT[poolId].size()*RLogicalBlock().getMemSize(FORMAT_VERSION));
1226 
1227 		// write each SAT entry
1228 		size_t offset=0;
1229 		for(size_t t=0;t<SAT[poolId].size();t++)
1230 			SAT[poolId][t].writeToMem(mem,offset);
1231 
1232 		f->write(mem,mem.getSize(),multiFileHandle);
1233 	}
1234 }
1235 
1236 template<class l_addr_t,class p_addr_t>
1237 	void TPoolFile<l_addr_t,p_addr_t>::restoreSAT(int formatVersion)
1238 {
1239 	invalidateAllCachedBlocks();
1240 
1241 
1242 	uint8_t buffer;
1243 	blockFile.read(&buffer,sizeof(buffer),WHICH_SAT_FILE_OFFSET);
1244 	whichSATFile=buffer;
1245 	if(whichSATFile!=0 && whichSATFile!=1)
1246 	{
1247 		printf("invalid which SAT file value\n");
1248 		exit(0);
1249 	}
1250 
1251 	buildSATFromFile(&SATFiles[whichSATFile],0,formatVersion);
1252 }
1253 
1254 template<class l_addr_t,class p_addr_t>
1255 	void TPoolFile<l_addr_t,p_addr_t>::buildSATFromFile(CMultiFile *f,const p_addr_t readWhere,int formatVersion)
1256 {
1257 	CMultiFile::RHandle multiFileHandle;
1258 	f->seek(readWhere,multiFileHandle);
1259 
1260 	// ??? probably want to put a SAT file signature here to make sure it's a SAT file
1261 
1262 	// destory current SAT and pool information
1263 	pools.clear();
1264 	poolNames.clear();
1265 	SAT.clear();
1266 	pasm.free_all();
1267 
1268 	// read number of pools
1269 	uint32_t poolCount;
1270 	f->read(&poolCount,sizeof(poolCount),multiFileHandle);
1271 	lethe(&poolCount);
1272 	for(size_t poolId=0;poolId<poolCount;poolId++)
1273 	{
1274 		// read pool name
1275 		string poolName;
1276 		readString(poolName,f,multiFileHandle);
1277 
1278 		// read pool info structure
1279 		RPoolInfo poolInfo;
1280 		poolInfo.readFromFile(f,multiFileHandle,formatVersion);
1281 		poolInfo.isValid= (poolName!="__internal_invalid_pool");
1282 
1283 		if(poolInfo.isValid)
1284 		{
1285 			if(poolInfo.alignment==0 || poolInfo.alignment>this->maxBlockSize)
1286 				throw runtime_error(string(__func__)+" -- invalid pool alignment read from file: "+istring(poolInfo.alignment)+" alignment must be 0 < alignment <= maxBlockSize (which is: "+istring(this->maxBlockSize)+")");
1287 		}
1288 
1289 		try
1290 		{
1291 			prvCreatePool(poolName,poolInfo.alignment,true,false);
1292 		}
1293 		catch(...)
1294 		{
1295 			printf("error creating pool\n");
1296 			exit(0);
1297 		}
1298 
1299 		// read number of SAT entries
1300 		uint32_t SATSize;
1301 		f->read(&SATSize,sizeof(SATSize),multiFileHandle);
1302 		lethe(&SATSize);
1303 
1304 		if(!poolInfo.isValid && SATSize>0)
1305 			throw runtime_error(string(__func__)+" -- SATSize is >0 on an invalid pool: '"+poolName+"'");
1306 
1307 		// read SAT into mem buffer
1308 		TAutoBuffer<uint8_t> mem(SATSize*RLogicalBlock().getMemSize(formatVersion));
1309 		f->read(mem,mem.getSize(),multiFileHandle);
1310 
1311 		const blocksize_t maxBlockSize=poolInfo.isValid ? getMaxBlockSizeFromAlignment(poolInfo.alignment) : 0;
1312 
1313 		// read each SAT entry from that mem buffer and put into the actual SAT data-member
1314 		size_t offset=0;
1315 		for(size_t t=0;t<SATSize;t++)
1316 		{
1317 			RLogicalBlock logicalBlock;
1318 			logicalBlock.readFromMem(mem,offset,formatVersion);
1319 
1320 			// divide the size of the block just read into pieces that will fit into maxBlockSize sizes blocks
1321 			// just in case the maxBlockSize is smaller than it used to be
1322 			const blocksize_t blockSize=logicalBlock.size;
1323 			for(blocksize_t j=0;j<blockSize/maxBlockSize;j++)
1324 			{
1325 				logicalBlock.size=maxBlockSize;
1326 
1327 				sortedInsert(SAT[poolId],logicalBlock);
1328 
1329 				logicalBlock.logicalStart+=maxBlockSize;
1330 				logicalBlock.physicalStart+=maxBlockSize;
1331 			}
1332 			logicalBlock.size=blockSize%maxBlockSize;
1333 			if(logicalBlock.size>0)
1334 				sortedInsert(SAT[poolId],logicalBlock);
1335 
1336 			pools[poolId].size+=blockSize;
1337 		}
1338 
1339 		if(pools[poolId].size!=poolInfo.size)
1340 		{
1341 			printf("buildSATFromFile -- pool size from SAT read does not match pool size from pool info read\n");
1342 			exit(0);
1343 		}
1344 	}
1345 	pasm.buildFromSAT(SAT);
1346 	pasm.make_file_smallest();
1347 }
1348 
1349 
1350 
1351 
1352 // SAT operations
1353 template<class l_addr_t,class p_addr_t>
1354 	const size_t TPoolFile<l_addr_t,p_addr_t>::findSATBlockContaining(const poolId_t poolId,const l_addr_t where,bool &atStartOfBlock) const
1355 {
1356 	if(SAT[poolId].empty())
1357 		throw runtime_error(string(__func__)+" -- SAT is empty");
1358 
1359 	const size_t i=(upper_bound(SAT[poolId].begin(),SAT[poolId].end(),RLogicalBlock(where))-1)-SAT[poolId].begin();
1360 	atStartOfBlock=(SAT[poolId][i].logicalStart==where);
1361 	return i;
1362 }
1363 
1364 
1365 template<class l_addr_t,class p_addr_t>
1366 	const bool TPoolFile<l_addr_t,p_addr_t>::isInWindow(const p_addr_t start,const p_addr_t end,const p_addr_t windowStart,const p_addr_t windowEnd)
1367 {
1368 	if(start<=windowStart && end>=windowStart)
1369 		return true;
1370 	if(end>=windowEnd && start<=windowEnd)
1371 		return true;
1372 	if(start>=windowStart && end<=windowEnd)
1373 		return true;
1374 	return false;
1375 }
1376 
1377 template<class l_addr_t,class p_addr_t>
1378 	void TPoolFile<l_addr_t,p_addr_t>::joinAllAdjacentBlocks()
1379 {
1380 	for(size_t poolId=0;poolId<pools.size();poolId++)
1381 	{
1382 		if(pools[poolId].isValid)
1383 			joinAdjacentBlocks(poolId);
1384 	}
1385 }
1386 
1387 template<class l_addr_t,class p_addr_t>
1388 	void TPoolFile<l_addr_t,p_addr_t>::joinAdjacentBlocks(const poolId_t poolId)
1389 {
1390 	joinAdjacentBlocks(poolId,0,SAT[poolId].size());
1391 }
1392 
1393 template<class l_addr_t,class p_addr_t>
1394 	void TPoolFile<l_addr_t,p_addr_t>::joinAdjacentBlocks(const poolId_t poolId,const size_t firstBlockIndex,const size_t blockCount)
1395 {
1396 	const size_t totalBlocks=SAT[poolId].size();
1397 	const blocksize_t maxBlockSize=getMaxBlockSizeFromAlignment(pools[poolId].alignment);
1398 
1399 	for(size_t t=firstBlockIndex;t<=firstBlockIndex+blockCount;t++)
1400 	{
1401 		if(t==firstBlockIndex)
1402 			continue; // skip first iteration because we're looking at t and the one before t (also avoids t-1 being underflowing)
1403 		if(t>=totalBlocks)
1404 			break; // just in case firstBlockIndex + blockCount specifies too many blocks
1405 
1406 		RLogicalBlock &b1=SAT[poolId][t-1];
1407 		const RLogicalBlock &b2=SAT[poolId][t];
1408 
1409 		if((b1.physicalStart+b1.size)==b2.physicalStart)
1410 		{ // blocks are physically next to each other -- candidate for joining
1411 			const l_addr_t newSize=b1.size+b2.size;
1412 
1413 			// size of blocks if joined doesn't make a block too big
1414 			if(newSize<=maxBlockSize)
1415 			{ // now join blocks blockIndex and blockIndex+1
1416 
1417 				pasm.join_blocks(b1.physicalStart,b2.physicalStart);
1418 
1419 				b1.size=newSize;
1420 				SAT[poolId].erase(SAT[poolId].begin()+t);
1421 
1422 				// check this block again the next time around
1423 				t--;
1424 			}
1425 		}
1426 	}
1427 }
1428 
1429 template<class l_addr_t,class p_addr_t>
1430 	void TPoolFile<l_addr_t,p_addr_t>::printSAT() const
1431 {
1432 	printf("\nSAT(s): (maxBlockSize: %u)\n",maxBlockSize);
1433 	for(size_t poolId=0;poolId<pools.size();poolId++)
1434 	{
1435 		if(!pools[poolId].isValid)
1436 			continue;
1437 
1438 		printf("\t%-4u Pool: '%s' size: %lld alignment: %lld\n",(unsigned)poolId,getPoolNameById(poolId).c_str(),(long long)getPoolSize(poolId),(long long)getPoolAlignment(poolId));
1439 		for(size_t t=0;t<SAT[poolId].size();t++)
1440 		{
1441 			printf("\t\t%-4u ",(unsigned)t);
1442 			SAT[poolId][t].print();
1443 		}
1444 
1445 	}
1446 	pasm.print();
1447 
1448 	printf("\n");
1449 }
1450 
1451 template<class l_addr_t,class p_addr_t>
1452 	bool TPoolFile<l_addr_t,p_addr_t>::defrag()
1453 {
1454 	if(!isOpen())
1455 		throw runtime_error(string(__func__)+" -- file not open");
1456 
1457 	invalidateAllCachedBlocks();
1458 
1459 	/* ???
1460 	 * Right now, defragging can happen and be much more inefficiant than it needs to be.
1461 	 * This is because it always makes the first poolId come first in the file.  Fragementation
1462 	 * should not care which order than the pools exist, only that they are contiguous on
1463 	 * disk.  I would do better to move as few blocks as possible, this would mean knowing
1464 	 * which order is best for the pools on disk.  Perhaps I could go thru each permutation of
1465 	 * poolId, but I'm not quite sure how do know which is the best ordering.
1466 	 */
1467 
1468 
1469 	/*
1470 	 * The defragging algorithm works as follows:
1471 	 * From the SAT, create a list of physical blocks that are used.
1472 	 * This is used to determine where we can put things later.
1473 	 * For each pool and each block in the pool, call a function
1474 	 * that moves that block physically where it it tells it too.  This
1475 	 * function is told the correct position from this defrag method, but
1476 	 * it might move things out of the way to fulfil its duty.
1477 	 *
1478 	 * Finally, when everything is consecutive in a pool, a new SAT is built
1479 	 * and the physical address space manager is told to build it's info
1480 	 * from this new SAT
1481 	 *
1482 	 * This algorithm is no less than O(n^2) where n is the number of physical
1483 	 * blocks before defragging
1484 	 */
1485 
1486 	bool didSomething=false;
1487 	TAutoBuffer<int8_t> temp(maxBlockSize);
1488 
1489 	//    addr     size
1490 	map<p_addr_t,p_addr_t> physicalBlockList;
1491 
1492 	// build physical block list
1493 	for(poolId_t poolId=0;poolId<pools.size();poolId++)
1494 	{
1495 		if(!pools[poolId].isValid)
1496 			continue;
1497 
1498 		for(size_t t=0;t<SAT[poolId].size();t++)
1499 		{
1500 			const RLogicalBlock &b=SAT[poolId][t];
1501 			physicalBlockList[b.physicalStart]=b.size;
1502 		}
1503 	}
1504 
1505 	// call method to correct each block's position
1506 	p_addr_t physicallyWhere=0;
1507 	for(poolId_t poolId=0;poolId<pools.size();poolId++)
1508 	{
1509 		if(!pools[poolId].isValid)
1510 			continue;
1511 
1512 		for(size_t t=0;t<SAT[poolId].size();t++)
1513 		{
1514 			RLogicalBlock &b=SAT[poolId][t];
1515 			didSomething|=physicallyMoveBlock(b,physicallyWhere,physicalBlockList,temp);
1516 			physicallyWhere+=b.size;
1517 		}
1518 	}
1519 
1520 	if(didSomething)
1521 	{
1522 		pasm.make_file_smallest();
1523 
1524 		// create new SAT
1525 		physicallyWhere=0;
1526 		for(poolId_t poolId=0;poolId<pools.size();poolId++)
1527 		{
1528 			SAT[poolId].clear();
1529 			if(!pools[poolId].isValid)
1530 				continue;
1531 
1532 			const blocksize_t maxBlockSize=getMaxBlockSizeFromAlignment(pools[poolId].alignment);
1533 
1534 			const l_addr_t blockCount=pools[poolId].size/maxBlockSize;
1535 			for(l_addr_t t=0;t<=blockCount;t++)
1536 			{
1537 				const l_addr_t blockSize= (t!=blockCount) ? maxBlockSize : pools[poolId].size%maxBlockSize;
1538 				if(blockSize>0)
1539 				{
1540 					RLogicalBlock b;
1541 					b.logicalStart=t*maxBlockSize;
1542 					b.physicalStart=physicallyWhere;
1543 					b.size=blockSize;
1544 
1545 					SAT[poolId].push_back(b);
1546 
1547 					physicallyWhere+=blockSize;
1548 				}
1549 			}
1550 		}
1551 
1552 		pasm.buildFromSAT(SAT);
1553 		backupSAT();
1554 	}
1555 
1556 	return didSomething;
1557 }
1558 
1559 
1560 template<class l_addr_t,class p_addr_t>
1561 	bool TPoolFile<l_addr_t,p_addr_t>::physicallyMoveBlock(RLogicalBlock &block,p_addr_t physicallyWhere,map<p_addr_t,p_addr_t> &physicalBlockList,int8_t *temp)
1562 {
1563 	if(block.physicalStart!=physicallyWhere)
1564 	{
1565 		// find what may exist in the location we want to move block to
1566 		for(poolId_t x=0;x<SAT.size();x++)
1567 		{
1568 			if(!pools[x].isValid)
1569 				continue;
1570 
1571 			for(size_t y=0;y<SAT[x].size();y++)
1572 			{
1573 				RLogicalBlock &b=SAT[x][y];
1574 
1575 				// see if b is in the way of where we want to put block
1576 				if(&b!=&block && CPhysicalAddressSpaceManager::overlap(physicallyWhere,block.size,b.physicalStart,b.size))
1577 				{ // b is in the way
1578 					p_addr_t moveTo=0;
1579 
1580 					// find a new place to put b (look backwards, because we're building up the beginning)
1581 					for(typename map<p_addr_t,p_addr_t>::reverse_iterator i=physicalBlockList.rbegin();i!=physicalBlockList.rend();i++)
1582 					{
1583 						typename map<p_addr_t,p_addr_t>::reverse_iterator prev_i=i; prev_i++;
1584 						if(prev_i!=physicalBlockList.rend())
1585 						{
1586 							const p_addr_t holeStart=prev_i->first+prev_i->second;
1587 							const p_addr_t holeSize=i->first - holeStart;
1588 							if(holeSize>=b.size)
1589 							{ // hole is large enough
1590 								// now make sure that this hole isn't where we want to put block
1591 								if(!CPhysicalAddressSpaceManager::overlap(physicallyWhere,block.size,holeStart,holeSize))
1592 								{ // move it here
1593 									moveTo=holeStart;
1594 									break;
1595 								}
1596 							}
1597 						}
1598 					}
1599 
1600 					if(moveTo==0)
1601 					{ // no place to move b to, so create new space
1602 						moveTo=pasm.get_file_size();
1603 						pasm.set_file_size(pasm.get_file_size()+b.size);
1604 						dprintf("just grew file from %lld to %lld\n",(long long)moveTo,(long long)pasm.get_file_size());
1605 					}
1606 
1607 					// now actually move b
1608 					{
1609 						dprintf("moving block from %lld to %lld\n",(long long)b.physicalStart,(long long)moveTo);
1610 
1611 						// read b off disk
1612 						blockFile.read(temp,b.size,b.physicalStart+LEADING_DATA_SIZE);
1613 
1614 						// update physicalBlockList and SAT
1615 						physicalBlockList.erase(physicalBlockList.find(b.physicalStart));
1616 						b.physicalStart=moveTo;
1617 						physicalBlockList[moveTo]=b.size;
1618 
1619 						// write b back out in the new location
1620 						blockFile.write(temp,b.size,moveTo+LEADING_DATA_SIZE);
1621 					}
1622 				}
1623 			}
1624 		}
1625 
1626 		// now nothing is in the way to move block, so move it
1627 		{
1628 			dprintf("moving block from %lld to %lld\n",(long long)block.physicalStart,(long long)physicallyWhere);
1629 
1630 			// read block off disk
1631 			blockFile.read(temp,block.size,block.physicalStart+LEADING_DATA_SIZE);
1632 
1633 			// update physicalBlockList and SAT
1634 			physicalBlockList.erase(physicalBlockList.find(block.physicalStart));
1635 			block.physicalStart=physicallyWhere;
1636 			physicalBlockList[physicallyWhere]=block.size;
1637 
1638 			// write block back out in the proper location
1639 			blockFile.write(temp,block.size,physicallyWhere+LEADING_DATA_SIZE);
1640 		}
1641 
1642 		return true;
1643 	}
1644 	return false;
1645 }
1646 
1647 
1648 
1649 
1650 // Pool Modification (pe -- pool elements, b -- bytes)
1651 template<class l_addr_t,class p_addr_t>
1652 	void TPoolFile<l_addr_t,p_addr_t>::insertSpace(const poolId_t poolId,const l_addr_t peWhere,const l_addr_t peCount)
1653 {
1654 	if(!opened)
1655 		throw runtime_error(string(__func__)+" -- no file is open");
1656 	if(peCount==0)
1657 		return;
1658 
1659 	if(!isValidPoolId(poolId))
1660 		throw runtime_error(string(__func__)+" -- invalid poolId: "+istring(poolId));
1661 
1662 	const alignment_t bAlignment=pools[poolId].alignment;
1663 	const l_addr_t bPoolSize=pools[poolId].size;
1664 	const l_addr_t pePoolSize=bPoolSize/bAlignment;
1665 	const l_addr_t pePoolMaxSize=maxLogicalAddress/bAlignment;
1666 
1667 	if(pePoolMaxSize-pePoolSize<peCount)
1668 		throw runtime_error(string(__func__)+" -- insufficient logical address space to insert "+istring(peCount)+" elements into pool ("+getPoolDescription(poolId)+")");
1669 	if(peWhere>pePoolSize)
1670 		throw runtime_error(string(__func__)+" -- out of range peWhere "+istring(peWhere)+" for pool ("+getPoolDescription(poolId)+")");
1671 
1672 	invalidateAllCachedBlocks(false,poolId);
1673 
1674 	const l_addr_t bWhere=peWhere*bAlignment;
1675 	const l_addr_t bCount=peCount*bAlignment;
1676 
1677 	const blocksize_t maxBlockSize=getMaxBlockSizeFromAlignment(bAlignment);
1678 	const size_t newLogicalBlockCount=(bCount/maxBlockSize);
1679 	bool didSplitOne=false;
1680 
1681 	size_t logicalBlockIndex=SAT[poolId].size();
1682 	if(bWhere<bPoolSize)
1683 	{ // in the middle (not appending)
1684 		bool atStartOfBlock;
1685 		logicalBlockIndex=findSATBlockContaining(poolId,bWhere,atStartOfBlock);
1686 
1687 		if(!atStartOfBlock)
1688 		{ // split the block at logicalBlockIndex since where isn't exacly at the beginning of the block
1689 dprintf("insertSpace - case 1/6\n");
1690 			didSplitOne=true;
1691 
1692 			RLogicalBlock &logicalBlock=SAT[poolId][logicalBlockIndex];
1693 
1694 			// sanity check
1695 			if(bWhere<=logicalBlock.logicalStart)
1696 			{ // logical impossibility since atStartOfBlock wasn't true (unless it was wrong)
1697 				printf("oops...\n");
1698 				exit(1);
1699 			}
1700 
1701 			// modify block at logicalBlockIndex and create a new RLogicalBlock for the second part of the split block
1702 			const l_addr_t firstPartSize=bWhere-logicalBlock.logicalStart;
1703 			const l_addr_t secondPartSize=logicalBlock.size-firstPartSize;
1704 
1705 			// inform the physical address space manager that we want to split the physical block into two parts
1706 			const p_addr_t newPhysicalStart=pasm.split_block(logicalBlock.physicalStart,firstPartSize);
1707 
1708 			// shrink the logical block's size
1709 			logicalBlock.size=firstPartSize;
1710 
1711 			// create new logical block which is the second part of the old block
1712 			RLogicalBlock newLogicalBlock;
1713 			newLogicalBlock.logicalStart=logicalBlock.logicalStart+firstPartSize;
1714 			newLogicalBlock.physicalStart=newPhysicalStart;
1715 			newLogicalBlock.size=secondPartSize;
1716 
1717 			// add the new logical block
1718 			sortedInsert(SAT[poolId],newLogicalBlock);
1719 
1720 			// alter the logical mapping by increasing all blocks' logicalStarts after the insertion point
1721 			offsetLogicalAddressSpace(SAT[poolId].begin()+logicalBlockIndex+1,SAT[poolId].end(),bCount,1);
1722 		}
1723 		else
1724 		{ // increase all blocks at and after insertion point
1725 dprintf("insertSpace - case 2/6\n");
1726 			offsetLogicalAddressSpace(SAT[poolId].begin()+logicalBlockIndex,SAT[poolId].end(),bCount,1);
1727 
1728 			if(logicalBlockIndex>0)
1729 				logicalBlockIndex--;
1730 			else
1731 				didSplitOne=true;
1732 		}
1733 	}
1734 	else if(bWhere==bPoolSize)
1735 	{ // at the end (or just starting)
1736 		if(bPoolSize>0)
1737 		{
1738 dprintf("insertSpace - case 3/6\n");
1739 			logicalBlockIndex=SAT[poolId].size()-1;
1740 			if((SAT[poolId][logicalBlockIndex].logicalStart+SAT[poolId][logicalBlockIndex].size)!=bWhere)
1741 			{
1742 				printf("huh??\n");
1743 				exit(0);
1744 			}
1745 		}
1746 	}
1747 
1748 
1749 	// create all the new logical and physical blocks needed to fill the gap from the split of the old block
1750 
1751 	RLogicalBlock newLogicalBlock;
1752 	for(size_t t=0;t<=newLogicalBlockCount;t++)
1753 	{
1754 		if(t==0)
1755 		{ // first block
1756 			dprintf("insertSpace - case 4/6\n");
1757 			newLogicalBlock.size=bCount%maxBlockSize;
1758 			if(newLogicalBlock.size==0)
1759 				continue; // it wasn't necessary to do this first iteration that handles the remainder
1760 			newLogicalBlock.logicalStart=bWhere;
1761 		}
1762 		else
1763 		{
1764 			dprintf("insertSpace - case 5/6\n");
1765 			newLogicalBlock.size=maxBlockSize;
1766 			newLogicalBlock.logicalStart=bWhere+((t-1)*maxBlockSize)+(bCount%maxBlockSize);
1767 		}
1768 
1769 		newLogicalBlock.physicalStart=pasm.alloc(newLogicalBlock.size);
1770 
1771 		/* ??? I could perhaps move this logic in the t==0 case above and join as much
1772 		 * of the new space with SAT[poolId][logicalBlockIndex] as possible (if the allocator
1773 		 * gives us space adjacent to it's physical space.  This MIGHT (i need to work it out
1774 		 * more) result in fewer blocks on average */
1775 		if(!didSplitOne && t==0) // the below would only be true iff this is
1776 		{
1777 			if(logicalBlockIndex<SAT[poolId].size()) // false when pool is empty
1778 			{
1779 				// if we're about to create a new logical block that could just as well be joined with the one before it
1780 				// then don't create a new one (appending optimization)
1781 				RLogicalBlock &logicalBlock=SAT[poolId][logicalBlockIndex];
1782 				if(
1783 				   ((logicalBlock.physicalStart+logicalBlock.size)==newLogicalBlock.physicalStart) &&
1784 			   	   ((logicalBlock.size+newLogicalBlock.size)<=maxBlockSize)
1785 				)
1786 				{
1787 					dprintf("insertSpace case - 6/6\n");
1788 					logicalBlock.size+=newLogicalBlock.size;
1789 					pasm.join_blocks(logicalBlock.physicalStart,newLogicalBlock.physicalStart);
1790 					pools[poolId].size+=newLogicalBlock.size;
1791 					continue; // skip insertion of new block
1792 				}
1793 			}
1794 		}
1795 
1796 		sortedInsert(SAT[poolId],newLogicalBlock);
1797 		pools[poolId].size+=newLogicalBlock.size;
1798 	}
1799 
1800 	// ZZZ ??? Takes too long in a real-time situation (i.e. Recording in ReZound)
1801 	//backupSAT();
1802 }
1803 
1804 template<class l_addr_t,class p_addr_t>
1805 	void TPoolFile<l_addr_t,p_addr_t>::removeSpace(const poolId_t poolId,const l_addr_t peWhere,const l_addr_t peCount)
1806 {
1807 	if(!opened)
1808 		throw runtime_error(string(__func__)+" -- no file is open");
1809 	if(peCount==0)
1810 		return;
1811 
1812 	// validate the parameters
1813 	if(!isValidPoolId(poolId))
1814 		throw runtime_error(string(__func__)+" -- invalid poolId: "+istring(poolId));
1815 
1816 	const alignment_t bAlignment=pools[poolId].alignment;
1817 	const l_addr_t bPoolSize=pools[poolId].size;
1818 	const l_addr_t pePoolSize=bPoolSize/bAlignment;
1819 
1820 	if(peWhere>=pePoolSize)
1821 		throw runtime_error(string(__func__)+" -- out of range peWhere "+istring(peWhere)+" for pool ("+getPoolDescription(poolId)+")");
1822 	if(pePoolSize-peWhere<peCount)
1823 		throw runtime_error(string(__func__)+" -- out of range peWhere "+istring(peWhere)+" and peCount "+istring(peCount)+" for pool ("+getPoolDescription(poolId)+")");
1824 
1825 	invalidateAllCachedBlocks(false,poolId);
1826 
1827 	const l_addr_t bWhere=peWhere*bAlignment;
1828 	const l_addr_t bCount=peCount*bAlignment;
1829 
1830 	bool atStartOfBlock;
1831 	const size_t logicalBlockIndex=findSATBlockContaining(poolId,bWhere,atStartOfBlock);
1832 
1833 	l_addr_t removeSize=bCount;
1834 	size_t t=logicalBlockIndex;
1835 	while(removeSize>0 && t<SAT[poolId].size())
1836 	{
1837 		RLogicalBlock &block=SAT[poolId][t];
1838 		const l_addr_t block_start=block.logicalStart;
1839 		const l_addr_t block_end=block_start+(block.size-1);
1840 		const l_addr_t remove_start= (bWhere<block_start) ? block_start : bWhere;
1841 		const l_addr_t remove_end= ((bWhere+(bCount-1))>block_end) ? block_end : (bWhere+(bCount-1));
1842 		const l_addr_t remove_in_block_size=remove_end-remove_start+1;
1843 
1844 		if(remove_start==block_start && remove_end==block_end)
1845 		{ // case 1 -- remove whole block -- on first and only block, middle or last block
1846 			// |[.......]|	([..] -- block ; |..| -- section to remove)
1847 dprintf("removeSpace - case 1/4\n");
1848 
1849 			pasm.free(block.physicalStart);
1850 			SAT[poolId].erase(SAT[poolId].begin()+t);
1851 		}
1852 		else if(remove_start==block_start && remove_end<block_end)
1853 		{ // case 2 -- remove a head of block -- on first and only block, last block
1854 			// |[.....|..]
1855 dprintf("removeSpace - case 2/4\n");
1856 
1857 			const blocksize_t new_blockSize=block.size-remove_in_block_size;
1858 			const p_addr_t newPhysicalStart=pasm.partial_free(block.physicalStart,block.physicalStart+remove_in_block_size,new_blockSize);
1859 
1860 			block.logicalStart-=bCount-remove_in_block_size; // ...-remove_in_block_size because, this block is not going to be affected by the loop which does this later since we increment t
1861 			block.physicalStart=newPhysicalStart;
1862 			block.size=new_blockSize;
1863 			t++;
1864 		}
1865 		else if(remove_start>block_start && remove_end==block_end)
1866 		{ // case 3 -- remove a tail of block -- first and only block, first block
1867 			// [..|.....]|
1868 dprintf("removeSpace - case 3/4\n");
1869 			const blocksize_t newBlockSize=block.size-remove_in_block_size;
1870 
1871 			pasm.partial_free(block.physicalStart,block.physicalStart,newBlockSize);
1872 
1873 			block.size=newBlockSize;
1874 			t++;
1875 		}
1876 		else if(remove_start>block_start && remove_end<block_end)
1877 		{ // case 4 -- split block -- on first and only block
1878 			// [..|...|..]
1879 			//    p1  p2
1880 dprintf("removeSpace - case 4/4\n");
1881 
1882 			const blocksize_t p1=remove_start-block_start;
1883 			const blocksize_t p2=remove_end-block_start;
1884 
1885 			// the phyical operation consists of a split, then a partial free of the right of the split block
1886 			p_addr_t newPhysicalStart=pasm.split_block(block.physicalStart,p1);
1887 				// now a new physical block starts at block.physicalStart+p1;
1888 			newPhysicalStart=pasm.partial_free(newPhysicalStart,block.physicalStart+p2+1,block.size-p2-1);
1889 				// now the new physical block starts at block.physicalStart+p2;
1890 
1891 			block.size=p1;
1892 
1893 			RLogicalBlock newLogicalBlock;
1894 			newLogicalBlock.logicalStart=remove_start;
1895 			newLogicalBlock.physicalStart=newPhysicalStart;
1896 			newLogicalBlock.size=block_end-remove_end;
1897 
1898 			sortedInsert(SAT[poolId],newLogicalBlock);
1899 			t+=2;
1900 		}
1901 		else
1902 		{
1903 			printf("error in cases 1-4\n");
1904 			exit(1);
1905 		}
1906 
1907 		// ??? sanity check
1908 		if(remove_in_block_size>removeSize)
1909 		{
1910 			printf("remove_in_block_size is greater than removeSize\n");
1911 			exit(1);
1912 		}
1913 
1914 		removeSize-=remove_in_block_size;
1915 	}
1916 
1917 	pools[poolId].size-=bCount;
1918 
1919 	// move all the subsequent logicalStarts downward
1920 	offsetLogicalAddressSpace(SAT[poolId].begin()+t,SAT[poolId].end(),bCount,-1);
1921 
1922 	// join the blocks that just became adjacent if possible
1923 	joinAdjacentBlocks(poolId,logicalBlockIndex>0 ? logicalBlockIndex-1 : 0,1);
1924 
1925 	pasm.make_file_smallest();
1926 
1927 	backupSAT();
1928 }
1929 
1930 template<class l_addr_t,class p_addr_t>
1931 	void TPoolFile<l_addr_t,p_addr_t>::moveData(const poolId_t destPoolId,const l_addr_t peDestWhere,const poolId_t srcPoolId,const l_addr_t peSrcWhere,const l_addr_t peCount)
1932 {
1933 	/*
1934 	 * Basically, this logic works the same as removeSpace except every time it removes some
1935 	 * space from the src pool, it adds it to the dest pool
1936 	 */
1937 
1938 	if(!opened)
1939 		throw runtime_error(string(__func__)+" -- no file is open");
1940 	if(peCount==0)
1941 		return;
1942 
1943 	// validate the parameters
1944 	if(!isValidPoolId(srcPoolId))
1945 		throw runtime_error(string(__func__)+" -- invalid srcPoolId: "+istring(srcPoolId));
1946 	if(!isValidPoolId(destPoolId))
1947 		throw runtime_error(string(__func__)+" -- invalid destPoolId: "+istring(destPoolId));
1948 
1949 	const alignment_t bAlignment=pools[srcPoolId].alignment;
1950 
1951 	// instead of implementing this separately... I move from src to temp then temp to dest
1952 	if(srcPoolId==destPoolId)
1953 	{
1954 		const string tempPoolName="__internal_moveData_pool__";
1955 		removePool(tempPoolName,false);
1956 		prvCreatePool(tempPoolName,bAlignment,false);
1957 		const poolId_t tempPoolId=getPoolIdByName(tempPoolName);
1958 
1959 		try
1960 		{
1961 dprintf("moveData -- case 1/6\n");
1962 			moveData(tempPoolId,0,srcPoolId,peSrcWhere,peCount);
1963 			dprintf("------------------------------------\n");
1964 			moveData(destPoolId,peDestWhere,tempPoolId,0,peCount);
1965 			removePool(tempPoolName,false);
1966 		}
1967 		catch(...)
1968 		{
1969 			removePool(tempPoolName,false);
1970 			throw;
1971 		}
1972 
1973 		backupSAT();
1974 		return;
1975 	}
1976 
1977 	if(pools[destPoolId].alignment!=bAlignment)
1978 		throw runtime_error(string(__func__)+" -- alignments do not match for srcPool ("+getPoolDescription(srcPoolId)+") and destPool ("+getPoolDescription(destPoolId)+")");
1979 
1980 	const l_addr_t bSrcPoolSize=pools[srcPoolId].size;
1981 	const l_addr_t peSrcPoolSize=bSrcPoolSize/bAlignment;
1982 
1983 	if(peSrcWhere>=peSrcPoolSize)
1984 		throw runtime_error(string(__func__)+" -- out of range peSrcWhere "+istring(peSrcWhere)+" for srcPool ("+getPoolDescription(srcPoolId)+")");
1985 	if(peSrcPoolSize-peSrcWhere<peCount)
1986 		throw runtime_error(string(__func__)+" -- out of range peSrcWhere "+istring(peSrcWhere)+" and peCount "+istring(peCount)+" for pool ("+getPoolDescription(srcPoolId)+")");
1987 
1988 
1989 	const l_addr_t bDestPoolSize=pools[destPoolId].size;
1990 	const l_addr_t peDestPoolSize=bDestPoolSize/bAlignment;
1991 
1992 	const l_addr_t pePoolMaxSize=maxLogicalAddress/bAlignment;
1993 
1994 
1995 	if(pePoolMaxSize-peDestPoolSize<peCount)
1996 		throw runtime_error(string(__func__)+" -- insufficient logical address space to insert "+istring(peCount)+" elements into destPool ("+getPoolDescription(destPoolId)+")");
1997 	if(peDestWhere>peDestPoolSize)
1998 		throw runtime_error(string(__func__)+" -- out of range peDestWhere "+istring(peDestWhere)+" for pool ("+getPoolDescription(destPoolId)+")");
1999 
2000 	invalidateAllCachedBlocks(false,srcPoolId);
2001 	invalidateAllCachedBlocks(false,destPoolId);
2002 
2003 	const l_addr_t bSrcWhere=peSrcWhere*bAlignment;
2004 	l_addr_t bDestWhere=peDestWhere*bAlignment;
2005 	const l_addr_t bCount=peCount*bAlignment;
2006 
2007 	if(bDestWhere==0 && bDestPoolSize==0 && bSrcWhere==0 && bCount==bSrcPoolSize)
2008 	{ // the whole src pool is moving to an empty dest pool, so just assign the whole SAT
2009 		// move src to dest
2010 		SAT[destPoolId]=SAT[srcPoolId];
2011 		pools[destPoolId].size=bSrcPoolSize;
2012 
2013 		// clear src
2014 		SAT[srcPoolId].clear();
2015 		pools[srcPoolId].size=0;
2016 	}
2017 	else
2018 	{ // only part of the src pool is moving or all of it is moving to a non-empty pool
2019 		bool atStartOfSrcBlock;
2020 		const size_t srcBlockIndex=findSATBlockContaining(srcPoolId,bSrcWhere,atStartOfSrcBlock);
2021 
2022 		size_t destBlockIndex=0;
2023 		if(bDestWhere<bDestPoolSize)
2024 		{
2025 dprintf("moveData -- case 2/6\n");
2026 			bool atStartOfDestBlock;
2027 			destBlockIndex=findSATBlockContaining(destPoolId,bDestWhere,atStartOfDestBlock);
2028 			if(!atStartOfDestBlock)
2029 			{ // go ahead and split the destination block so that we can simply insert new ones along the way
2030 dprintf("moveData -- case 2.5/6\n");
2031 
2032 				RLogicalBlock &destLogicalBlock=SAT[destPoolId][destBlockIndex];
2033 
2034 				if(bDestWhere<=destLogicalBlock.logicalStart)
2035 				{ // logcal impossibility since atStartOfBlock wasn't true (unless it was wrong)
2036 					printf("oops...\n");
2037 					exit(1);
2038 				}
2039 
2040 				const l_addr_t firstPartSize=bDestWhere-destLogicalBlock.logicalStart;
2041 				const l_addr_t secondPartSize=destLogicalBlock.size-firstPartSize;
2042 
2043 
2044 				// inform the physical address space manager that we want to split the dest physical block into two parts
2045 				pasm.split_block(destLogicalBlock.physicalStart,firstPartSize);
2046 
2047 				// shrink the dest logical block's size
2048 				destLogicalBlock.size=firstPartSize;
2049 
2050 				// create the new logical block which are the second part of the old block
2051 				RLogicalBlock newLogicalBlock;
2052 				newLogicalBlock.logicalStart=destLogicalBlock.logicalStart+firstPartSize;
2053 				newLogicalBlock.physicalStart=destLogicalBlock.physicalStart+firstPartSize;
2054 				newLogicalBlock.size=secondPartSize;
2055 
2056 				// add the new logical block
2057 				sortedInsert(SAT[destPoolId],newLogicalBlock);
2058 
2059 				destBlockIndex++;
2060 			}
2061 
2062 			// move upward all the logicalStarts in the dest pool past the destination where point
2063 			offsetLogicalAddressSpace(SAT[destPoolId].begin()+destBlockIndex,SAT[destPoolId].end(),bCount,1);
2064 		}
2065 
2066 
2067 		size_t loopCount=0;
2068 		l_addr_t moveSize=bCount;
2069 		size_t src_t=srcBlockIndex;
2070 		while(moveSize>0 && src_t<SAT[srcPoolId].size())
2071 		{
2072 			RLogicalBlock &srcBlock=SAT[srcPoolId][src_t];
2073 			const l_addr_t src_block_start=srcBlock.logicalStart;
2074 			const l_addr_t src_block_end=src_block_start+(srcBlock.size-1);
2075 			const l_addr_t src_remove_start= (bSrcWhere<src_block_start) ? src_block_start : bSrcWhere;
2076 			const l_addr_t src_remove_end= ((bSrcWhere+(bCount-1))>src_block_end) ? src_block_end : (bSrcWhere+(bCount-1));
2077 			const l_addr_t remove_in_src_block_size=src_remove_end-src_remove_start+1;
2078 
2079 			if(src_remove_start==src_block_start && src_remove_end==src_block_end)
2080 			{ // case 1 -- remove whole block from src pool -- on first and only block, middle or last block
2081 dprintf("moveData -- case 3/6 -- %d\n",src_t);
2082 				// |[.......]|	([..] -- block ; |..| -- section to remove)
2083 
2084 				RLogicalBlock newDestLogicalBlock(srcBlock);
2085 				newDestLogicalBlock.logicalStart=bDestWhere;
2086 
2087 				SAT[srcPoolId].erase(SAT[srcPoolId].begin()+src_t);
2088 
2089 				sortedInsert(SAT[destPoolId],newDestLogicalBlock);
2090 			}
2091 			else if(src_remove_start==src_block_start && src_remove_end<src_block_end)
2092 			{ // case 2 -- remove a head of block -- on first and only block, last block
2093 dprintf("moveData -- case 4/6 -- %d\n",src_t);
2094 				// |[.....|..]
2095 
2096 				const blocksize_t new_srcBlockSize=srcBlock.size-remove_in_src_block_size;
2097 
2098 				// create physical block for src block now to point to
2099 				const p_addr_t new_srcPhysicalStart=pasm.split_block(srcBlock.physicalStart,remove_in_src_block_size);
2100 
2101 				// create the new logical block in dest pool
2102 				RLogicalBlock newDestLogicalBlock;
2103 				newDestLogicalBlock.logicalStart=bDestWhere;
2104 				newDestLogicalBlock.size=remove_in_src_block_size;
2105 				newDestLogicalBlock.physicalStart=srcBlock.physicalStart;
2106 				sortedInsert(SAT[destPoolId],newDestLogicalBlock);
2107 
2108 				// modify existing src block
2109 				srcBlock.logicalStart-=bCount-remove_in_src_block_size; // do this because, this srcBlock is not gonna be affected by the offsetLogicalAddressSpace() below
2110 				srcBlock.physicalStart=new_srcPhysicalStart;
2111 				srcBlock.size=new_srcBlockSize;
2112 
2113 				src_t++;
2114 			}
2115 			else if(src_remove_start>src_block_start && src_remove_end==src_block_end)
2116 			{ // case 3 -- remove a tail of block -- first and only block, first block
2117 dprintf("moveData -- case 5/6\n");
2118 				// [..|.....]|
2119 
2120 				const blocksize_t new_srcBlockSize=srcBlock.size-remove_in_src_block_size;
2121 
2122 				// create physical block for new dest block
2123 				const p_addr_t new_destPhysicalStart=pasm.split_block(srcBlock.physicalStart,new_srcBlockSize);
2124 
2125 				// create the new logical block in dest pool
2126 				RLogicalBlock newDestLogicalBlock;
2127 				newDestLogicalBlock.logicalStart=bDestWhere;
2128 				newDestLogicalBlock.size=remove_in_src_block_size;
2129 				newDestLogicalBlock.physicalStart=new_destPhysicalStart;
2130 				sortedInsert(SAT[destPoolId],newDestLogicalBlock);
2131 
2132 				// modify the existing src block
2133 				srcBlock.size=new_srcBlockSize;
2134 
2135 				src_t++;
2136 			}
2137 			else if(src_remove_start>src_block_start && src_remove_end<src_block_end)
2138 			{ // case 4 -- split block -- on first and only block
2139 dprintf("moveData -- case 6/6\n");
2140 				// [..|...|..]
2141 				//    p1  p2
2142 
2143 				const blocksize_t p1=src_remove_start-src_block_start;
2144 				const blocksize_t p2=src_remove_end-src_block_start;
2145 
2146 				// create new physical block by splitting existing src physical block
2147 				const p_addr_t new_destPhysicalStart=pasm.split_block(srcBlock.physicalStart,p1);
2148 
2149 				// modify existing src block
2150 				srcBlock.size=p1;
2151 
2152 				// split the new physical block; left side -> new dest block, right side -> new src block
2153 				const p_addr_t new_srcPhysicalStart=pasm.split_block(new_destPhysicalStart,remove_in_src_block_size);
2154 
2155 				// create new src block
2156 				RLogicalBlock newSrcLogicalBlock;
2157 				newSrcLogicalBlock.logicalStart=srcBlock.logicalStart+srcBlock.size;
2158 				newSrcLogicalBlock.physicalStart=new_srcPhysicalStart;
2159 				newSrcLogicalBlock.size=src_block_end-src_remove_end;
2160 				sortedInsert(SAT[srcPoolId],newSrcLogicalBlock);
2161 
2162 				// create new dest block
2163 				RLogicalBlock newDestLogicalBlock;
2164 				newDestLogicalBlock.logicalStart=bDestWhere;
2165 				newDestLogicalBlock.physicalStart=new_destPhysicalStart;
2166 				newDestLogicalBlock.size=remove_in_src_block_size;
2167 				sortedInsert(SAT[destPoolId],newDestLogicalBlock);
2168 
2169 				src_t+=2;
2170 			}
2171 			else
2172 			{
2173 				printf("error in cases 1-4\n");
2174 				exit(1);
2175 			}
2176 
2177 			loopCount++;
2178 			bDestWhere+=remove_in_src_block_size;
2179 
2180 			// ??? sanity check
2181 			if(remove_in_src_block_size>moveSize)
2182 			{
2183 				printf("remove_in_src_block_size is greater than moveSize\n");
2184 				exit(1);
2185 			}
2186 
2187 			moveSize-=remove_in_src_block_size;
2188 		}
2189 
2190 		pools[srcPoolId].size-=bCount;
2191 		pools[destPoolId].size+=bCount;
2192 
2193 		// move all the subsequent logicalStarts of the src pool downward
2194 		offsetLogicalAddressSpace(SAT[srcPoolId].begin()+src_t,SAT[srcPoolId].end(),bCount,-1);
2195 
2196 
2197 		// join blocks at first or least dest blocks delt with if possible
2198 		joinAdjacentBlocks(destPoolId,destBlockIndex>0 ? destBlockIndex-1 : 0,1);
2199 		if(loopCount>0 && SAT[destPoolId].size()>0)
2200 			joinAdjacentBlocks(destPoolId,(destBlockIndex+loopCount-1)<SAT[destPoolId].size() ? destBlockIndex+loopCount-2 : SAT[destPoolId].size()-1,2);
2201 
2202 		// join the blocks that just became adjacent in the src pool if possible
2203 		joinAdjacentBlocks(srcPoolId,srcBlockIndex>0 ? srcBlockIndex-1 : 0,1);
2204 	}
2205 
2206 	backupSAT();
2207 }
2208 
2209 template<class l_addr_t,class p_addr_t>
2210 	void TPoolFile<l_addr_t,p_addr_t>::clearPool(const poolId_t poolId)
2211 {
2212 	if(!opened)
2213 		throw runtime_error(string(__func__)+" -- no file is open");
2214 	if(!isValidPoolId(poolId))
2215 		throw runtime_error(string(__func__)+" -- invalid poolId: "+istring(poolId));
2216 
2217 	invalidateAllCachedBlocks(false,poolId);
2218 
2219 	// free all physical blocks associated with this pool
2220 	for(size_t t=0;t<SAT[poolId].size();t++)
2221 		pasm.free(SAT[poolId][t].physicalStart);
2222 
2223 	// remove all localBlocks in the SAT associated with this pool
2224 	SAT[poolId].clear();
2225 
2226 	pools[poolId].size=0;
2227 
2228 	pasm.make_file_smallest();
2229 
2230 	backupSAT();
2231 }
2232 
2233 
2234 // Pool Data Access
2235 template<class l_addr_t,class p_addr_t>
2236 	template<class pool_element_t> void TPoolFile<l_addr_t,p_addr_t>::cacheBlock(const l_addr_t peWhere,const TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > *accesser)
2237 {
2238 	CMutexLocker lock(accesserInfoMutex);
2239 
2240 		// assume is valid
2241 	const poolId_t poolId=accesser->poolId;
2242 
2243 	if(peWhere>=maxLogicalAddress/sizeof(pool_element_t))
2244 		throw runtime_error(string(__func__)+" -- invalid peWhere "+istring(peWhere)+" for pool ("+getPoolDescription(poolId)+")");
2245 
2246 	const l_addr_t byteWhere=peWhere*sizeof(pool_element_t);
2247 
2248 	unreferenceCachedBlock(accesser);
2249 
2250 	RCachedBlock *found=NULL;
2251 
2252 	// look to see if this block is already cached in the active cached blocks
2253 	for(typename set<RCachedBlock *>::iterator i=activeCachedBlocks.begin();i!=activeCachedBlocks.end();i++)
2254 	{
2255 		RCachedBlock *cachedBlock=(*i);
2256 		if(cachedBlock->poolId==poolId && cachedBlock->containsAddress(byteWhere))
2257 		{
2258 			found=cachedBlock;
2259 			break;
2260 		}
2261 	}
2262 
2263 	// if not found then look to see if this block is already cached in the unreferenced cached blocks
2264 	if(found==NULL)
2265 	{
2266 		for(typename set<RCachedBlock *>::iterator i=unreferencedCachedBlocks.begin();i!=unreferencedCachedBlocks.end();i++)
2267 		{
2268 			RCachedBlock *cachedBlock=(*i);
2269 			if(cachedBlock->poolId==poolId && cachedBlock->containsAddress(byteWhere))
2270 			{
2271 				found=cachedBlock;
2272 				unreferencedCachedBlocks.erase(i);
2273 				break;
2274 			}
2275 		}
2276 
2277 		if(found==NULL)
2278 		{	// use an unused block if available or take the LRUed unreferenced cached block if available or finally create a new one
2279 
2280 			// validate address
2281 			if(byteWhere>=getPoolSize(poolId))
2282 			{
2283 				accesser->init();
2284 				throw runtime_error(string(__func__)+" -- invalid peWhere "+istring(peWhere)+" for pool ("+getPoolDescription(poolId)+")");
2285 			}
2286 
2287 			bool dummy;
2288 			const size_t SATIndex=findSATBlockContaining(poolId,byteWhere,dummy);
2289 
2290 			// use an unused one if available
2291 			if(!unusedCachedBlocks.empty())
2292 			{
2293 				found=unusedCachedBlocks.front();
2294 				unusedCachedBlocks.pop_front();
2295 			}
2296 
2297 			if(found==NULL)
2298 			{	// invalidate an unreferenced one or create a new one if none are available
2299 				if(!unreferencedCachedBlocks.empty())
2300 				{	// create an unused one from the unreferencedCachedBlocks
2301 					invalidateCachedBlock(*unreferencedCachedBlocks.begin());
2302 					// ??? sanity check
2303 					if(unusedCachedBlocks.empty())
2304 					{
2305 						printf("what? one's supposed to be there now...\n");
2306 						exit(0);
2307 					}
2308 					found=unusedCachedBlocks.front();
2309 					unusedCachedBlocks.pop_front();
2310 				}
2311 				else
2312 					found=new RCachedBlock(maxBlockSize);
2313 			}
2314 
2315 			// could check of found==NULL here... error if so
2316 
2317 			blockFile.read(found->buffer,SAT[poolId][SATIndex].size,SAT[poolId][SATIndex].physicalStart+LEADING_DATA_SIZE);
2318 
2319 			// initialize the cachedBlock
2320 			found->init(poolId,SAT[poolId][SATIndex].logicalStart,SAT[poolId][SATIndex].size);
2321 		}
2322 		activeCachedBlocks.insert(found);
2323 	}
2324 
2325 	found->referenceCount++;
2326 	accesser->startAddress=(found->logicalStart)/sizeof(pool_element_t);
2327 	accesser->endAddress=((found->logicalStart+found->size)/sizeof(pool_element_t))-1;
2328 	accesser->cacheBuffer=(pool_element_t *)(found->buffer);
2329 	accesser->dirty=false;
2330 	accesser->cachedBlock=found;
2331 }
2332 
2333 template<class l_addr_t,class p_addr_t>
2334 	void TPoolFile<l_addr_t,p_addr_t>::clearPool(const string poolName)
2335 {
2336 	clearPool(getPoolIdByName(poolName));
2337 }
2338 
2339 template<class l_addr_t,class p_addr_t>
2340 	template<class pool_element_t> void TPoolFile<l_addr_t,p_addr_t>::invalidateAccesser(const TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > *accesser)
2341 {
2342 	RCachedBlock *cachedBlock=accesser->cachedBlock;
2343 	if(cachedBlock!=NULL)
2344 		unreferenceCachedBlock(accesser);
2345 }
2346 
2347 template<class l_addr_t,class p_addr_t>
2348 	void TPoolFile<l_addr_t,p_addr_t>::invalidateCachedBlock(RCachedBlock *cachedBlock)
2349 {
2350 	for(size_t t=0;cachedBlock->referenceCount>0 && t<accessers.size();t++)
2351 	{
2352 		if(accessers[t]->cachedBlock==cachedBlock)
2353 			unreferenceCachedBlock(accessers[t]);
2354 	}
2355 
2356 	if(cachedBlock->dirty)
2357 	{
2358 		bool atStartOfBlock;
2359 		size_t SATIndex=findSATBlockContaining(cachedBlock->poolId,cachedBlock->logicalStart,atStartOfBlock);
2360 		// if atStartOfBlock is not true.. problem!!!
2361 		blockFile.write(cachedBlock->buffer,SAT[cachedBlock->poolId][SATIndex].size,SAT[cachedBlock->poolId][SATIndex].physicalStart+LEADING_DATA_SIZE);
2362 	}
2363 
2364 	// the cached block structure is now unreferenced and unused
2365 	typename set<RCachedBlock *>::iterator i=unreferencedCachedBlocks.find(cachedBlock);
2366 	if(i!=unreferencedCachedBlocks.end())
2367 	{
2368 		unreferencedCachedBlocks.erase(i);
2369 		unusedCachedBlocks.push_back(cachedBlock);
2370 	}
2371 	else
2372 	{
2373 		typename set<RCachedBlock *>::iterator i=activeCachedBlocks.find(cachedBlock);
2374 		if(i!=activeCachedBlocks.end())
2375 		{
2376 			activeCachedBlocks.erase(i);
2377 			printf("I think a mem-leak just occurred!\n");
2378 			if(find(unusedCachedBlocks.begin(),unusedCachedBlocks.end(),cachedBlock)==unusedCachedBlocks.end()
2379 			&& find(unreferencedCachedBlocks.begin(),unreferencedCachedBlocks.end(),cachedBlock)==unreferencedCachedBlocks.end())
2380 				printf("  yep..\n");
2381 			else
2382 				printf("  nope..\n");
2383 		}
2384 	}
2385 }
2386 
2387 template<class l_addr_t,class p_addr_t>
2388 	template<class pool_element_t> void TPoolFile<l_addr_t,p_addr_t>::unreferenceCachedBlock(const TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > *accesser)
2389 {
2390 	RCachedBlock *cachedBlock=accesser->cachedBlock;
2391 	if(cachedBlock!=NULL)
2392 	{
2393 		cachedBlock->dirty |= accesser->dirty;
2394 		cachedBlock->referenceCount--;
2395 		if(cachedBlock->referenceCount==0)
2396 		{	// move cachedBlock from activeQueue to unreferencedQueue
2397 			typename set<RCachedBlock *>::iterator i=activeCachedBlocks.find(cachedBlock);
2398 			if(i!=activeCachedBlocks.end())
2399 				activeCachedBlocks.erase(i);
2400 			unreferencedCachedBlocks.insert(cachedBlock);
2401 		}
2402 		accesser->init();
2403 	}
2404 }
2405 
2406 template<class l_addr_t,class p_addr_t>
2407 	void TPoolFile<l_addr_t,p_addr_t>::invalidateAllCachedBlocks(bool allPools,poolId_t poolId)
2408 {
2409 	CMutexLocker lock(accesserInfoMutex);
2410 
2411 	for(typename set<RCachedBlock *>::iterator i=activeCachedBlocks.begin();i!=activeCachedBlocks.end();)
2412 	{
2413 		typename set<RCachedBlock *>::iterator ii=i;
2414 		i++;
2415 		if(allPools || (*ii)->poolId==poolId)
2416 			invalidateCachedBlock(*ii);
2417 	}
2418 	for(typename set<RCachedBlock *>::iterator i=unreferencedCachedBlocks.begin();i!=unreferencedCachedBlocks.end();)
2419 	{
2420 		typename set<RCachedBlock *>::iterator ii=i;
2421 		i++;
2422 		if(allPools || (*ii)->poolId==poolId)
2423 			invalidateCachedBlock(*ii);
2424 	}
2425 }
2426 
2427 template<class l_addr_t,class p_addr_t>
2428 	template<class pool_element_t> void TPoolFile<l_addr_t,p_addr_t>::addAccesser(const TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > *accesser)
2429 {
2430 	CMutexLocker lock(accesserInfoMutex);
2431 	accessers.push_back((const CGenericPoolAccesser *)accesser);
2432 		// ??? I used to make sure that it wasn't already there
2433 }
2434 
2435 template<class l_addr_t,class p_addr_t>
2436 	template<class pool_element_t> void TPoolFile<l_addr_t,p_addr_t>::removeAccesser(const TStaticPoolAccesser<pool_element_t,TPoolFile<l_addr_t,p_addr_t> > *accesser)
2437 {
2438 	CMutexLocker lock(accesserInfoMutex);
2439 	invalidateAccesser(accesser);
2440 
2441 	//						??? see about changing this to lower_bound... figure out just if lower_bound-1 or upper_bound-1 should be used
2442 	typename vector<const CGenericPoolAccesser *>::iterator i=find(accessers.begin(),accessers.end(),(const CGenericPoolAccesser *)accesser);
2443 	if(i!=accessers.end())
2444 		accessers.erase(i);
2445 }
2446 
2447 
2448 
2449 
2450 
2451 // ---- RPoolInfo ---------------------------------------------------------
2452 template<class l_addr_t,class p_addr_t>
2453 	TPoolFile<l_addr_t,p_addr_t>::RPoolInfo::RPoolInfo()
2454 {
2455 	size=0;
2456 	alignment=0;
2457 	isValid=false;
2458 }
2459 
2460 template<class l_addr_t,class p_addr_t>
2461 	TPoolFile<l_addr_t,p_addr_t>::RPoolInfo::RPoolInfo(const l_addr_t _size,const alignment_t _alignment,const bool _isValid)
2462 {
2463 	size=_size;
2464 	alignment=_alignment;
2465 	isValid=_isValid;
2466 }
2467 
2468 template<class l_addr_t,class p_addr_t>
2469 	TPoolFile<l_addr_t,p_addr_t>::RPoolInfo::RPoolInfo(const RPoolInfo &src)
2470 {
2471 	operator=(src);
2472 }
2473 
2474 template<class l_addr_t,class p_addr_t>
2475 	typename TPoolFile<l_addr_t,p_addr_t>::RPoolInfo &TPoolFile<l_addr_t,p_addr_t>::RPoolInfo::operator=(const RPoolInfo &src)
2476 {
2477 	size=src.size;
2478 	alignment=src.alignment;
2479 	isValid=src.alignment;
2480 	return *this;
2481 }
2482 
2483 template<class l_addr_t,class p_addr_t>
2484 	void TPoolFile<l_addr_t,p_addr_t>::RPoolInfo::writeToFile(CMultiFile *f,CMultiFile::RHandle &multiFileHandle) const
2485 {
2486 	p_addr_t tSize=size; // always write size as a p_addr_t because l_addr_t can change in ReZound
2487 	alignment_t tAlignment=alignment;
2488 
2489 	hetle(&tSize);
2490 	f->write(&tSize,sizeof(tSize),multiFileHandle);
2491 	hetle(&tAlignment);
2492 	f->write(&tAlignment,sizeof(tAlignment),multiFileHandle);
2493 }
2494 
2495 template<class l_addr_t,class p_addr_t>
2496 	void TPoolFile<l_addr_t,p_addr_t>::RPoolInfo::readFromFile(CMultiFile *f,CMultiFile::RHandle &multiFileHandle,int formatVersion)
2497 {
2498 	if(formatVersion==0)
2499 	{
2500 		uint32_t _size;
2501 		f->read(&_size,sizeof(_size),multiFileHandle);
2502 		lethe(&_size);
2503 		size=_size;
2504 	}
2505 	else // if(formatVersion>=1)
2506 	{
2507 		p_addr_t _size;
2508 		f->read(&_size,sizeof(_size),multiFileHandle);
2509 		lethe(&_size);
2510 		size=_size;
2511 	}
2512 	f->read(&alignment,sizeof(alignment),multiFileHandle);
2513 	lethe(&alignment);
2514 }
2515 
2516 
2517 
2518 
2519 // ---- RCachedBlock ---------------------------------------------------
2520 template<class l_addr_t,class p_addr_t>
2521 	TPoolFile<l_addr_t,p_addr_t>::RCachedBlock::RCachedBlock(const blocksize_t maxBlockSize)
2522 {
2523 	if((buffer=malloc(maxBlockSize))==NULL)
2524 		throw runtime_error(string(__func__)+" -- unable to allocate buffer space");
2525 }
2526 
2527 template<class l_addr_t,class p_addr_t>
2528 	TPoolFile<l_addr_t,p_addr_t>::RCachedBlock::~RCachedBlock()
2529 {
2530 	free(buffer);
2531 }
2532 
2533 template<class l_addr_t,class p_addr_t>
2534 	bool TPoolFile<l_addr_t,p_addr_t>::RCachedBlock::containsAddress(l_addr_t where) const
2535 {
2536 	return where>=logicalStart && where<(logicalStart+size);
2537 }
2538 
2539 template<class l_addr_t,class p_addr_t>
2540 	void TPoolFile<l_addr_t,p_addr_t>::RCachedBlock::init(const poolId_t _poolId,const l_addr_t _logicalStart,const blocksize_t _size)
2541 {
2542 	poolId=_poolId;
2543 	logicalStart=_logicalStart;
2544 	size=_size;
2545 
2546 	dirty=false;
2547 	referenceCount=0;
2548 }
2549 
2550 
2551 
2552 
2553 // ---- RLogicalBlock ---------------------------------------------------------
2554 
2555 template<class l_addr_t,class p_addr_t>
2556 	TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::RLogicalBlock()
2557 {
2558 	logicalStart=size=physicalStart=0;
2559 }
2560 
2561 template<class l_addr_t,class p_addr_t>
2562 	TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::RLogicalBlock(const l_addr_t _logicalStart)
2563 {
2564 	size=physicalStart=0;
2565 	logicalStart=_logicalStart;
2566 }
2567 
2568 template<class l_addr_t,class p_addr_t>
2569 	TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::RLogicalBlock(const RLogicalBlock &src)
2570 {
2571 	operator=(src);
2572 }
2573 
2574 template<class l_addr_t,class p_addr_t>
2575 	typename TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock &TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::operator=(const RLogicalBlock &src)
2576 {
2577 	logicalStart=src.logicalStart;
2578 	size=src.size;
2579 	physicalStart=src.physicalStart;
2580 	return *this;
2581 }
2582 
2583 template<class l_addr_t,class p_addr_t>
2584 	const bool TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::operator==(const RLogicalBlock &src) const
2585 {
2586 	if(logicalStart==src.logicalStart)
2587 		return true;
2588 	return false;
2589 }
2590 
2591 template<class l_addr_t,class p_addr_t>
2592 	const bool TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::operator<(const RLogicalBlock &src) const
2593 {
2594 	if(src.logicalStart>logicalStart)
2595 		return true;
2596 	return false;
2597 }
2598 
2599 template<class l_addr_t,class p_addr_t>
2600 	const size_t TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::getMemSize(int formatVersion)
2601 {
2602 	return (formatVersion==0 ? sizeof(uint32_t) : sizeof(p_addr_t))+sizeof(size)+sizeof(physicalStart);
2603 }
2604 
2605 template<class l_addr_t,class p_addr_t>
2606 	void TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::writeToMem(uint8_t *mem,size_t &offset) const
2607 {
2608 	p_addr_t _logicalStart=logicalStart;
2609 	memcpy(mem+offset,&_logicalStart,sizeof(_logicalStart));
2610 	hetle((p_addr_t *)(mem+offset));
2611 	offset+=sizeof(_logicalStart);
2612 
2613 	memcpy(mem+offset,&size,sizeof(size));
2614 	hetle((size_t *)(mem+offset));
2615 	offset+=sizeof(size);
2616 
2617 	memcpy(mem+offset,&physicalStart,sizeof(physicalStart));
2618 	hetle((p_addr_t *)(mem+offset));
2619 	offset+=sizeof(physicalStart);
2620 }
2621 
2622 template<class l_addr_t,class p_addr_t>
2623 	void TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::readFromMem(const uint8_t *mem,size_t &offset,int formatVersion)
2624 {
2625 	if(formatVersion==0)
2626 	{
2627 		uint32_t _logicalStart;
2628 		memcpy(&_logicalStart,mem+offset,sizeof(_logicalStart));
2629 		lethe(&_logicalStart);
2630 		offset+=sizeof(_logicalStart);
2631 		logicalStart=_logicalStart;
2632 	}
2633 	else //if(formatVersion>=1)
2634 	{
2635 		p_addr_t _logicalStart;
2636 		memcpy(&_logicalStart,mem+offset,sizeof(_logicalStart));
2637 		lethe(&_logicalStart);
2638 		offset+=sizeof(_logicalStart);
2639 		logicalStart=_logicalStart;
2640 	}
2641 
2642 	memcpy(&size,mem+offset,sizeof(size));
2643 	lethe(&size);
2644 	offset+=sizeof(size);
2645 
2646 	memcpy(&physicalStart,mem+offset,sizeof(physicalStart));
2647 	lethe(&physicalStart);
2648 	offset+=sizeof(physicalStart);
2649 }
2650 
2651 template<class l_addr_t,class p_addr_t>
2652 	void TPoolFile<l_addr_t,p_addr_t>::RLogicalBlock::print() const
2653 {
2654 	printf("logicalStart: %-10lld size: %-5lld physicalStart: %-10lld\n",(long long)logicalStart,(long long)size,(long long)physicalStart);
2655 }
2656 
2657 
2658 
2659 // --- physical address space management ------------------------
2660 
2661 
2662 template<class l_addr_t,class p_addr_t>
2663 	TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::CPhysicalAddressSpaceManager(CMultiFile &_file) :
2664 	file(_file)
2665 {
2666 }
2667 
2668 template<class l_addr_t,class p_addr_t>
2669 	p_addr_t TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::appendAlloc(const p_addr_t size)
2670 {
2671 	const p_addr_t addr=get_file_size();
2672 	set_file_size(addr+size);
2673 	alloced.insert(alloced.end(),make_pair(addr,size));
2674 
2675 	lastAllocAppended=true;
2676 	lastAllocSize=size;
2677 
2678 	return addr;
2679 }
2680 
2681 template<class l_addr_t,class p_addr_t>
2682 	p_addr_t TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::alloc(blocksize_t size)
2683 {
2684 	if(size<=0)
2685 		return 0;
2686 
2687 	if(holes.empty())
2688 	{ // need to make more space
2689 dprintf("alloc: case 1\n");
2690 		return appendAlloc(size);
2691 	}
2692 	else
2693 	{
2694 		if(lastAllocAppended && size>=lastAllocSize) /* optimization of repeated consecutive appends */
2695 		{ // need to make more space
2696 dprintf("alloc: case 1.5\n");
2697 			return appendAlloc(size);
2698 		}
2699 		else
2700 		{
2701 			lastAllocAppended=false;
2702 
2703 			// check the largest hole
2704 			typename holeSizeIndex_t::iterator i=holeSizeIndex.end();
2705 			i--;
2706 
2707 			if(i->first>size)
2708 			{ // hole more than big enough
2709 dprintf("alloc: case 2\n");
2710 				const p_addr_t addr=i->second->first;
2711 				alloced[addr]=size;
2712 
2713 				const p_addr_t newHoleSize=i->first-size;
2714 				const p_addr_t newHoleStart=i->second->first+size;
2715 
2716 				// update holes and holeSizeIndex
2717 				holes.erase(i->second);
2718 				holeSizeIndex.erase(i);
2719 
2720 				typename holes_t::iterator hole_i=holes.insert(make_pair(newHoleStart,newHoleSize)).first;
2721 				holeSizeIndex.insert(make_pair(newHoleSize,hole_i));
2722 
2723 				return addr;
2724 			}
2725 			else if(i->first==size)
2726 			{ // hole is the exact size we need
2727 dprintf("alloc: case 3\n");
2728 				const p_addr_t addr=i->second->first;
2729 				alloced[i->second->first]=size;
2730 
2731 				holes.erase(i->second);
2732 				holeSizeIndex.erase(i);
2733 
2734 				return addr;
2735 			}
2736 			else
2737 			{ // need to make more space
2738 dprintf("alloc: case 4\n");
2739 				return appendAlloc(size);
2740 			}
2741 		}
2742 	}
2743 }
2744 
2745 template<class l_addr_t,class p_addr_t>
2746 	p_addr_t TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::split_block(p_addr_t addr,blocksize_t newBlockStartsAt)
2747 {
2748 dprintf("split_block: case 1\n");
2749 	typename alloced_t::iterator i=alloced.find(addr);
2750 	if(i==alloced.end())
2751 		throw runtime_error(string(__func__)+" -- addr is not an alloced block: "+istring(addr));
2752 
2753 	if(newBlockStartsAt<=0)
2754 		throw runtime_error(string(__func__)+" -- parameters don't cause a split, newBlockStartsAt is <= zero");
2755 
2756 	if(newBlockStartsAt>=i->second)
2757 		throw runtime_error(string(__func__)+" -- newBlockStartsAt is out of range: "+istring(i->second)+" < "+istring(newBlockStartsAt));
2758 
2759 	// create new block
2760 	const p_addr_t newAddr=addr+newBlockStartsAt;
2761 	alloced.insert(i,make_pair(newAddr,i->second-newBlockStartsAt));
2762 
2763 	// update existing block to be smaller
2764 	i->second=newBlockStartsAt;
2765 
2766 	return newAddr;
2767 }
2768 
2769 template<class l_addr_t,class p_addr_t>
2770 	p_addr_t TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::partial_free(p_addr_t addr,p_addr_t newAddr,blocksize_t newSize)
2771 {
2772 	typename alloced_t::iterator i=alloced.find(addr);
2773 	if(i==alloced.end())
2774 		throw runtime_error(string(__func__)+" -- addr is not an alloced block: "+istring(addr));
2775 
2776 	if(newSize<=0)
2777 		throw runtime_error(string(__func__)+" -- newSize is <= zero");
2778 
2779 		// ??? maybe cases like this don't matter .. maybe only do if testing
2780 	if(newAddr==addr && newSize==i->second)
2781 		throw runtime_error(string(__func__)+" -- partial free does nothing");
2782 
2783 	if(newAddr+newSize>addr+i->second)
2784 		throw runtime_error(string(__func__)+" -- newAddr+newSize would extend beyond the originally alloced block: "+istring(newAddr)+"+"+istring(newSize)+" > "+istring(addr)+"+"+istring(i->second));
2785 
2786 
2787 	lastAllocAppended=false; // a new hole may be about to open up so don't do this optimization next time
2788 
2789 	if(newAddr>addr)
2790 	{ // freeing what's left of newAddr
2791 
2792 		// either create a new hole left of addr or join with an existing one
2793 		const p_addr_t newHoleSize=newAddr-addr;
2794 		if(i!=alloced.begin())
2795 		{
2796 			// find the hole left of i
2797 			typename alloced_t::iterator prev_i=i; prev_i--;
2798 			typename holes_t::iterator holes_i=holes.find(prev_i->first+prev_i->second);
2799 			if(holes_i==holes.end())
2800 			{ // (case 1) no hole to join with, so create one
2801 dprintf("partial_free: case 1\n");
2802 				holes_i=holes.insert(make_pair(addr,newHoleSize)).first;
2803 				holeSizeIndex.insert(make_pair(newHoleSize,holes_i));
2804 			}
2805 			else
2806 			{ // (case 2) hole found to join with
2807 dprintf("partial_free: case 2\n");
2808 				holeSizeIndex.erase(findHoleSizeIndexEntry(holes_i->second,holes_i->first));
2809 				holes_i->second+=newHoleSize;
2810 				holeSizeIndex.insert(make_pair(holes_i->second,holes_i));
2811 			}
2812 
2813 		}
2814 		else
2815 		{ // i is the first alloced block, so check the left most hole or create a new left-most hole
2816 
2817 			typename holes_t::iterator holes_i=holes.begin();
2818 			if(!holes.empty() && holes_i->first<addr)
2819 			{ // (case 3) join with this hole
2820 dprintf("partial_free: case 3\n");
2821 				// remove the holeSizeIndex entry for the hole at addr 0
2822 				typename holeSizeIndex_t::iterator holeSizeIndex_i=findHoleSizeIndexEntry(holes_i->second,0);
2823 				holeSizeIndex.erase(holeSizeIndex_i);
2824 
2825 				// update the hole at addr 0
2826 				holes_i->second+=newHoleSize;
2827 
2828 				// create new holeSizeIndex entry for hole at addr 0
2829 				holeSizeIndex.insert(make_pair(holes_i->second,holes_i));
2830 
2831 			}
2832 			else
2833 			{ // (case 4) create a new left-most hole
2834 dprintf("partial_free: case 4\n");
2835 				typename holes_t::iterator holes_i=holes.insert(holes.begin(),make_pair(addr,newHoleSize));
2836 				holeSizeIndex.insert(make_pair(newHoleSize,holes_i));
2837 			}
2838 		}
2839 	}
2840 
2841 	if(newAddr+newSize<addr+i->second)
2842 	{ // freeing something to the right of newAddr+newSize
2843 		// either create a new hole to the right of newAddr+newSize or join with an existing after addr+i->second
2844 		const p_addr_t newHoleAddr=newAddr+newSize;
2845 		const p_addr_t newHoleSize=(addr+i->second)-newHoleAddr;
2846 
2847 		typename holes_t::iterator holes_i=holes.find(addr+i->second);
2848 		if(holes_i==holes.end())
2849 		{ // (case 5) no hole found right of original allocated block, so create one
2850 dprintf("partial_free: case 5\n");
2851 			holes_i=holes.insert(make_pair(newHoleAddr,newHoleSize)).first;
2852 			holeSizeIndex.insert(make_pair(newHoleSize,holes_i));
2853 		}
2854 		else
2855 		{ // (case 6) hole found right of original allocated block, so join with it
2856 dprintf("partial_free: case 6\n");
2857 			const p_addr_t joinHoleAddr=holes_i->first;
2858 			const p_addr_t joinHoleSize=holes_i->second;
2859 
2860 			typename holes_t::iterator holes_i2=holes.insert(holes_i,make_pair(newHoleAddr,joinHoleSize+newHoleSize));
2861 			holeSizeIndex.insert(make_pair(joinHoleSize+newHoleSize,holes_i2));
2862 
2863 			holes.erase(holes_i);
2864 			holeSizeIndex.erase(findHoleSizeIndexEntry(joinHoleSize,joinHoleAddr));
2865 		}
2866 	}
2867 
2868 	// update alloced
2869 	alloced.erase(i);
2870 	alloced.insert(make_pair(newAddr,newSize));
2871 
2872 	return newAddr;
2873 }
2874 
2875 template<class l_addr_t,class p_addr_t>
2876 	typename TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::holeSizeIndex_t::iterator TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::findHoleSizeIndexEntry(const p_addr_t size,const p_addr_t addr)
2877 {
2878 	typename holeSizeIndex_t::iterator holeSizeIndex_i=holeSizeIndex.find(size);
2879 	while(holeSizeIndex_i!=holeSizeIndex.end())
2880 	{
2881 		if(holeSizeIndex_i->first!=size)
2882 		{
2883 			printf("WHAT! there is no holeSizeIndex entry for the hole we're looking for\n");
2884 			break;
2885 		}
2886 
2887 		if(holeSizeIndex_i->second->first==addr)
2888 		{ // found it
2889 			return holeSizeIndex_i;
2890 		}
2891 
2892 		holeSizeIndex_i++;
2893 	}
2894 
2895 	throw runtime_error(string(__func__)+" -- no holeSizeIndex entry found");
2896 }
2897 
2898 template<class l_addr_t,class p_addr_t>
2899 	void TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::free(p_addr_t addr)
2900 {
2901 	typename alloced_t::iterator alloced_i=alloced.find(addr);
2902 	if(alloced_i==alloced.end())
2903 		throw runtime_error(string(__func__)+" -- attempting to free something that was allocated");
2904 
2905 	lastAllocAppended=false; // a new hole may be about to open up so don't do this optimization next time
2906 
2907 	// attempt to find holes on either side of block being freed and join block's space with either one or both holes
2908 
2909 	// find hole on left (set to holes.end() if there is no hole on the left)
2910 	typename holes_t::iterator leftwardHole;
2911 	if(alloced_i->first==alloced.begin()->first)
2912 	{ // addr is the first alloced block, so check if there is a hole left of the alloced block
2913 		leftwardHole=holes.begin();
2914 		if(leftwardHole->first!=0)
2915 			// first hole is after the first alloced block
2916 			leftwardHole=holes.end();
2917 	}
2918 	else
2919 	{
2920 		typename alloced_t::iterator prev_i=alloced_i; prev_i--;
2921 		leftwardHole=holes.find(prev_i->first+prev_i->second);
2922 	}
2923 
2924 	// find hole on right (set to holes.end() if there is no hole on the right)
2925 	typename holes_t::iterator rightwardHole=holes.find(alloced_i->first+alloced_i->second);
2926 
2927 	if(leftwardHole==holes.end())
2928 	{ // no hole on the left
2929 		if(rightwardHole==holes.end())
2930 		{ // (case 1) no hole on the right either, so just make alloced the first hole
2931 dprintf("free: case 1\n");
2932 			typename holes_t::iterator hole_i=holes.insert(make_pair(alloced_i->first,alloced_i->second)).first;
2933 			holeSizeIndex.insert(make_pair(alloced_i->second,hole_i));
2934 		}
2935 		else
2936 		{ // (case 2) there is a hole only on the right, so join the free block with it
2937 dprintf("free: case 2\n");
2938 			// remove holeSizeIndex entry for rightward hole we just removed
2939 			typename holeSizeIndex_t::iterator holeSizeIndex_i=findHoleSizeIndexEntry(rightwardHole->second,rightwardHole->first);
2940 			holeSizeIndex.erase(holeSizeIndex_i);
2941 
2942 			const p_addr_t newHoleSize=alloced_i->second+rightwardHole->second;
2943 
2944 			typename holes_t::iterator hole_i=holes.insert(make_pair(alloced_i->first,newHoleSize)).first;
2945 			holeSizeIndex.insert(make_pair(newHoleSize,hole_i));
2946 
2947 			// remove rightward hole because we've created a new hole that accounts for its space
2948 			holes.erase(rightwardHole);
2949 		}
2950 	}
2951 	else
2952 	{ // there is a hole on the left
2953 		if(rightwardHole==holes.end())
2954 		{ // (case 3) no hole on the right, so just join the freed block with hole on the left
2955 dprintf("free: case 3\n");
2956 			const p_addr_t oldHoleSize=leftwardHole->second;
2957 
2958 			// update the size of the left hole
2959 			leftwardHole->second+=alloced_i->second;
2960 
2961 			// remove holeSizeIndex entry for leftward hole we just updated and re-add it with new size
2962 			typename holeSizeIndex_t::iterator holeSizeIndex_i=findHoleSizeIndexEntry(oldHoleSize,leftwardHole->first);
2963 			holeSizeIndex.erase(holeSizeIndex_i);
2964 			holeSizeIndex.insert(make_pair(leftwardHole->second,leftwardHole));
2965 		}
2966 		else
2967 		{ // (case 4) there is a hole on both sides of the alloced block, so join the freed block and the hole on the right with the hole on the left
2968 dprintf("free: case 4\n");
2969 			const p_addr_t oldHoleSize=leftwardHole->second;
2970 
2971 			// update the size of the left hole
2972 			leftwardHole->second+=alloced_i->second+rightwardHole->second;
2973 
2974 			// remove holeSizeIndex entry for leftward hole we just updated and re-add it with the new size
2975 			typename holeSizeIndex_t::iterator holeSizeIndex_i=findHoleSizeIndexEntry(oldHoleSize,leftwardHole->first);
2976 			holeSizeIndex.erase(holeSizeIndex_i);
2977 			holeSizeIndex.insert(make_pair(leftwardHole->second,leftwardHole));
2978 
2979 			// remove the holeSizeIndex entry for rightward hole we just updated
2980 			holeSizeIndex_i=findHoleSizeIndexEntry(rightwardHole->second,rightwardHole->first);
2981 			holeSizeIndex.erase(holeSizeIndex_i);
2982 
2983 			// remove the rightward hole
2984 			holes.erase(rightwardHole);
2985 		}
2986 	}
2987 
2988 	// remove block from the alloced list
2989 	alloced.erase(alloced_i);
2990 }
2991 
2992 template<class l_addr_t,class p_addr_t>
2993 	void TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::free_all()
2994 {
2995 	lastAllocAppended=false;
2996 	alloced.clear();
2997 	holes.clear();
2998 	holeSizeIndex.clear();
2999 
3000 	if(file.isOpen())
3001 		holeSizeIndex.insert(make_pair(get_file_size(),holes.insert(make_pair(0,get_file_size())).first));
3002 }
3003 
3004 template<class l_addr_t,class p_addr_t>
3005 	void TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::join_blocks(p_addr_t addr1,p_addr_t addr2)
3006 {
3007 	typename alloced_t::iterator i1=alloced.find(addr1);
3008 	typename alloced_t::iterator i2=alloced.find(addr2);
3009 
3010 	if(i1==alloced.end())
3011 		throw runtime_error(string(__func__)+" -- addr1 is not allocated");
3012 	if(i2==alloced.end())
3013 		throw runtime_error(string(__func__)+" -- addr2 is not allocated");
3014 
3015 	if((i1->first+i1->second)==i2->first)
3016 	{ // join blocks
3017 		i1->second+=i2->second;
3018 		alloced.erase(i2);
3019 	}
3020 	else
3021 		throw runtime_error(string(__func__)+" -- addr1 and addr2 are not adjacent");
3022 }
3023 
3024 template<class l_addr_t,class p_addr_t>
3025 	void TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::buildFromSAT(const vector<vector<RLogicalBlock> > &SAT)
3026 {
3027 	lastAllocAppended=false;
3028 	alloced.clear();
3029 	holes.clear();
3030 	holeSizeIndex.clear();
3031 
3032 	// create list of just alloced stuff
3033 	for(size_t x=0;x<SAT.size();x++)
3034 	{
3035 		for(size_t y=0;y<SAT[x].size();y++)
3036 		{
3037 			alloced.insert(make_pair(SAT[x][y].physicalStart,SAT[x][y].size));
3038 		}
3039 	}
3040 
3041 	if(!alloced.empty())
3042 	{
3043 
3044 		// if first alloced block doesn't start at 0, then create a hole that accounts for this
3045 		if(alloced.begin()->first!=0)
3046 		{
3047 			holeSizeIndex.insert(make_pair(alloced.begin()->first,holes.insert(make_pair(0,alloced.begin()->first)).first));
3048 		}
3049 
3050 		// create holes that are between any alloced blocks
3051 		for(typename alloced_t::const_iterator i=alloced.begin();i!=alloced.end();i++)
3052 		{
3053 			typename alloced_t::const_iterator next_i=i; next_i++;
3054 			if(next_i!=alloced.end())
3055 			{
3056 				const p_addr_t right_addr=i->first+i->second;
3057 				if(right_addr!=next_i->first)
3058 				{
3059 					const p_addr_t holeSize=next_i->first-right_addr;
3060 					holeSizeIndex.insert(make_pair(holeSize,holes.insert(make_pair(right_addr,holeSize)).first));
3061 				}
3062 			}
3063 		}
3064 
3065 		// create hole after last allocated block if necessary
3066 		{
3067 			const p_addr_t last_addr=alloced.rbegin()->first+alloced.rbegin()->second;
3068 			const p_addr_t last_hole_size=get_file_size()-last_addr;
3069 			if(last_addr<get_file_size())
3070 				holeSizeIndex.insert(make_pair(last_hole_size,holes.insert(make_pair(last_addr,last_hole_size)).first));
3071 		}
3072 	}
3073 	else
3074 	{
3075 		// create hole to account for all space if necessary
3076 		if(get_file_size()>0)
3077 			holeSizeIndex.insert(make_pair(get_file_size(),holes.insert(make_pair(0,get_file_size())).first));
3078 	}
3079 
3080 
3081 }
3082 
3083 template<class l_addr_t,class p_addr_t>
3084 	void TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::make_file_smallest()
3085 {
3086 	lastAllocAppended=false;
3087 	if(alloced.empty())
3088 	{
3089 		set_file_size(0);
3090 		holes.clear();
3091 		holeSizeIndex.clear();
3092 	}
3093 	else
3094 	{
3095 		const p_addr_t lastAddr=alloced.rbegin()->first+alloced.rbegin()->second;
3096 		set_file_size(lastAddr);
3097 
3098 		// update holes if necessary (by removing the last hole that was at lastAddr)
3099 		typename holes_t::iterator i=holes.find(lastAddr);
3100 		if(i!=holes.end())
3101 		{
3102 			holeSizeIndex.erase(findHoleSizeIndexEntry(i->second,i->first));
3103 			holes.erase(i);
3104 		}
3105 	}
3106 }
3107 
3108 template<class l_addr_t,class p_addr_t>
3109 	void TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::set_file_size(const p_addr_t newSize)
3110 {
3111 	file.setSize(newSize+LEADING_DATA_SIZE);
3112 }
3113 
3114 template<class l_addr_t,class p_addr_t>
3115 	const p_addr_t TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::get_file_size() const
3116 {
3117 	return max(file.getSize(),(CMultiFile::l_addr_t)LEADING_DATA_SIZE)-LEADING_DATA_SIZE;
3118 }
3119 
3120 
3121 template<class l_addr_t,class p_addr_t>
3122 	bool TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::overlap(p_addr_t addr1,p_addr_t size1,p_addr_t addr2,p_addr_t size2)
3123 {
3124 	if(addr1<addr2 && (addr1+size1)<=addr2)
3125 		return false;
3126 	if(addr2<addr1 && (addr2+size2)<=addr1)
3127 		return false;
3128 	return true;
3129 }
3130 
3131 template<class l_addr_t,class p_addr_t>
3132 	void TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::verify(bool expectContinuousPhysicalAllocs)
3133 {
3134 	// make sure no alloced blocks overlap
3135 	for(typename alloced_t::const_iterator alloced_i=alloced.begin();alloced_i!=alloced.end();alloced_i++)
3136 	{
3137 		typename alloced_t::const_iterator next_alloced_i=alloced_i; next_alloced_i++;
3138 		if(next_alloced_i!=alloced.end())
3139 		{
3140 			if(alloced_i->first+alloced_i->second>next_alloced_i->first)
3141 				printf("*** FAILURE alloced blocks overlap: %lld+%lld > %lld\n",(long long)alloced_i->first,(long long)alloced_i->second,(long long)next_alloced_i->first);
3142 		}
3143 	}
3144 
3145 	// make sure no holes overlap or are consecutive(needing joining)
3146 	for(typename holes_t::const_iterator holes_i=holes.begin();holes_i!=holes.end();holes_i++)
3147 	{
3148 		typename holes_t::const_iterator next_holes_i=holes_i; next_holes_i++;
3149 		if(next_holes_i!=holes.end())
3150 		{
3151 			if(holes_i->first+holes_i->second>=next_holes_i->first)
3152 				printf("*** FAILURE holes overlap or are consecutive: %lld+%lld >= %lld\n",(long long)holes_i->first,(long long)holes_i->second,(long long)next_holes_i->first);
3153 		}
3154 	}
3155 
3156 
3157 	// make sure no alloced block overlaps with a holes
3158 	for(typename alloced_t::const_iterator alloced_i=alloced.begin();alloced_i!=alloced.end();alloced_i++)
3159 	{
3160 		for(typename holes_t::const_iterator holes_i=holes.begin();holes_i!=holes.end();holes_i++)
3161 		{
3162 			if(overlap(holes_i->first,holes_i->second,alloced_i->first,alloced_i->second))
3163 				printf("*** FAILURE overlapping hole and alloced:\n\talloced: %lld %lld\t\n\thole:  %lld %lld\n",(long long)alloced_i->first,(long long)alloced_i->second,(long long)holes_i->first,(long long)holes_i->second);
3164 		}
3165 	}
3166 
3167 	// make sure that holes + alloced == file size
3168 	p_addr_t total=0;
3169 	for(typename alloced_t::const_iterator alloced_i=alloced.begin();alloced_i!=alloced.end();alloced_i++)
3170 		total+=alloced_i->second;
3171 	for(typename holes_t::const_iterator holes_i=holes.begin();holes_i!=holes.end();holes_i++)
3172 		total+=holes_i->second;
3173 	if(total>get_file_size())
3174 		printf("*** FAILURE alloced + holes > file size :   %lld>%lld\n",(long long)total,(long long)get_file_size());
3175 
3176 	// validate that holeSizeIndex is correct by building one from scratch and comparing it to the one we have
3177 	holeSizeIndex_t temp;
3178 	for(typename holes_t::iterator holes_i=holes.begin();holes_i!=holes.end();holes_i++)
3179 		temp.insert(make_pair(holes_i->second,holes_i));
3180 
3181 	if(temp.size()!=holeSizeIndex.size())
3182 		printf("*** FAILURE holeSizeIndex is out of sync (size differs)\n");
3183 
3184 		// for each element in temp, look for it in holeSizeIndex
3185 	for(typename holeSizeIndex_t::iterator i=temp.begin();i!=temp.end();i++)
3186 	{
3187 		try
3188 		{
3189 			findHoleSizeIndexEntry(i->first,i->second->first);
3190 		}
3191 		catch(...)
3192 		{
3193 			printf("*** FAILURE holeSizeIndex is out of sync\n");
3194 			break;
3195 		}
3196 	}
3197 
3198 	// verify that all the physical blocks are contiguous (not an invalid pool file if it happens, but the user may expect it)
3199 	if(expectContinuousPhysicalAllocs)
3200 	{
3201 		p_addr_t expectedStart=0;
3202 		for(typename alloced_t::iterator i=alloced.begin();i!=alloced.end();i++)
3203 		{
3204 			if(i->first!=expectedStart)
3205 			{
3206 				printf("*** FAILURE physical allocated space is not continuous\n");
3207 				break;
3208 			}
3209 			expectedStart+=i->second;
3210 		}
3211 	}
3212 
3213 	printf("PASSED\n");
3214 }
3215 
3216 template<class l_addr_t,class p_addr_t>
3217 	void TPoolFile<l_addr_t,p_addr_t>::CPhysicalAddressSpaceManager::print() const
3218 {
3219 	printf("\nAllocated Physical Blocks:\n");
3220 	for(typename alloced_t::const_iterator t=alloced.begin();t!=alloced.end();t++)
3221 		printf("physicalStart: %-10lld size: %-5lld\n",(long long)t->first,(long long)t->second);
3222 
3223 	printf("\nUnallocated Physical Blocks:\n");
3224 	for(typename holes_t::const_iterator t=holes.begin();t!=holes.end();t++)
3225 		printf("physicalStart: %-10lld size: %-5lld\n",(long long)t->first,(long long)t->second);
3226 
3227 	printf("\nIndex into Unallocated Physical Blocks:\n");
3228 	for(typename holeSizeIndex_t::const_iterator t=holeSizeIndex.begin();t!=holeSizeIndex.end();t++)
3229 		printf("size: %-10lld addr: %-5lld\n",(long long)t->first,(long long)t->second->first);
3230 
3231 	printf("\n\n");
3232 }
3233 
3234 
3235 
3236 
3237 // --- util methods ----------------
3238 
3239 template<class l_addr_t,class p_addr_t>
3240 	template<class C,class I> void TPoolFile<l_addr_t,p_addr_t>::sortedInsert(C &c,const I &i)
3241 {
3242 		// ??? need an overloaded version which doesn't always start from the beginning
3243 	c.insert(lower_bound(c.begin(),c.end(),i),i);
3244 }
3245 
3246 template<class l_addr_t,class p_addr_t>
3247 	void TPoolFile<l_addr_t,p_addr_t>::readString(string &s,CMultiFile *f,CMultiFile::RHandle &multiFileHandle)
3248 {
3249 	uint32_t len;
3250 	f->read(&len,sizeof(len),multiFileHandle);
3251 	lethe(&len);
3252 /* ??? if sizeof(char) != sizeof(int8_t) then we're in trouble */
3253 	char buffer[1024];
3254 	for(size_t t=0;t<len/1024;t++)
3255 	{
3256 		f->read(buffer,1024,multiFileHandle);
3257 		s.append(buffer,1024);
3258 	}
3259 	if(len%1024)
3260 	{
3261 		f->read(buffer,len%1024,multiFileHandle);
3262 		s.append(buffer,len%1024);
3263 	}
3264 }
3265 
3266 template<class l_addr_t,class p_addr_t>
3267 	void TPoolFile<l_addr_t,p_addr_t>::writeString(const string &s,CMultiFile *f,CMultiFile::RHandle &multiFileHandle)
3268 {
3269 	const uint32_t len=s.length();
3270 	uint32_t tLen=len;
3271 	hetle(&tLen);
3272 	f->write(&tLen,sizeof(len),multiFileHandle);
3273 /* ??? if sizeof(char) != sizeof(int8_t) then we're in trouble */
3274 	f->write(s.c_str(),len,multiFileHandle);
3275 }
3276 
3277 
3278