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(©File,true); 368 writeDirtyIndicator(false,©File); 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