Line data Source code
1 : /****************************************************************************** 2 : * 3 : * Purpose: Block directory API. 4 : * 5 : ****************************************************************************** 6 : * Copyright (c) 2011 7 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada. 8 : * 9 : * SPDX-License-Identifier: MIT 10 : ****************************************************************************/ 11 : 12 : #include "blockdir/blockdir.h" 13 : #include "blockdir/blocklayer.h" 14 : #include "blockdir/blockfile.h" 15 : #include "core/pcidsk_utils.h" 16 : #include "pcidsk_exception.h" 17 : #include <sstream> 18 : #include <cstring> 19 : #include <cassert> 20 : #include <algorithm> 21 : 22 : using namespace PCIDSK; 23 : 24 : /************************************************************************/ 25 : /* BlockDir() */ 26 : /************************************************************************/ 27 : 28 : /** 29 : * Constructor. 30 : * 31 : * @param poFile The associated file object. 32 : * @param nSegment The segment of the block directory. 33 : */ 34 24 : BlockDir::BlockDir(BlockFile * poFile, uint16 nSegment) 35 : : mpoFile(poFile), 36 : mnSegment(nSegment), 37 : mnVersion(0), 38 24 : mchEndianness(BigEndianSystem() ? 'B' : 'L'), 39 : mbNeedsSwap(false), 40 : mnValidInfo(0), 41 : mbModified(false), 42 48 : mbOnDisk(true) 43 : { 44 24 : assert(poFile && nSegment != INVALID_SEGMENT); 45 : 46 24 : mpoFreeBlockLayer = nullptr; 47 24 : } 48 : 49 : /************************************************************************/ 50 : /* BlockDir() */ 51 : /************************************************************************/ 52 : 53 : /** 54 : * Constructor. 55 : * 56 : * @param poFile The associated file object. 57 : * @param nSegment The segment of the block directory. 58 : * @param nVersion The version of the block directory. 59 : */ 60 8 : BlockDir::BlockDir(BlockFile * poFile, uint16 nSegment, uint16 nVersion) 61 : 62 : : mpoFile(poFile), 63 : mnSegment(nSegment), 64 : mnVersion(nVersion), 65 8 : mchEndianness(BigEndianSystem() ? 'B' : 'L'), 66 : mbNeedsSwap(false), 67 : mnValidInfo(0), 68 : mbModified(true), 69 16 : mbOnDisk(false) 70 : { 71 8 : assert(poFile && nSegment != INVALID_SEGMENT); 72 : 73 8 : mpoFreeBlockLayer = nullptr; 74 8 : } 75 : 76 : /************************************************************************/ 77 : /* ~BlockDir() */ 78 : /************************************************************************/ 79 : 80 : /** 81 : * Destructor. 82 : */ 83 32 : BlockDir::~BlockDir(void) 84 : { 85 68 : for (size_t iLayer = 0; iLayer < moLayerList.size(); iLayer++) 86 36 : delete moLayerList[iLayer]; 87 : 88 32 : delete mpoFreeBlockLayer; 89 : 90 32 : delete mpoFile; 91 32 : } 92 : 93 : /************************************************************************/ 94 : /* Sync() */ 95 : /************************************************************************/ 96 : 97 : /** 98 : * Synchronizes the block directory to disk. 99 : */ 100 50 : void BlockDir::Sync(void) 101 : { 102 50 : if (!mbModified) 103 42 : return; 104 : 105 8 : if (!mpoFile->GetUpdatable()) 106 0 : return; 107 : 108 8 : if (!IsValid()) 109 : { 110 0 : ThrowPCIDSKException("Failed to save: %s", 111 0 : mpoFile->GetFilename().c_str()); 112 : } 113 : 114 8 : WriteDir(); 115 : 116 8 : mbModified = false; 117 : } 118 : 119 : /************************************************************************/ 120 : /* GetFile() */ 121 : /************************************************************************/ 122 : 123 : /** 124 : * Gets the associated file of the block directory. 125 : * 126 : * @return The associated file of the block directory. 127 : */ 128 90 : BlockFile * BlockDir::GetFile(void) const 129 : { 130 90 : return mpoFile; 131 : } 132 : 133 : /************************************************************************/ 134 : /* GetSegmentIndex() */ 135 : /************************************************************************/ 136 : 137 : /** 138 : * Gets the index of the block directory segment. 139 : * 140 : * @return The index of the block directory segment. 141 : */ 142 0 : uint16 BlockDir::GetSegmentIndex(void) const 143 : { 144 0 : return mnSegment; 145 : } 146 : 147 : /************************************************************************/ 148 : /* GetVersion() */ 149 : /************************************************************************/ 150 : 151 : /** 152 : * Gets the version of the block directory. 153 : * 154 : * @return The version of the block directory. 155 : */ 156 0 : uint16 BlockDir::GetVersion(void) const 157 : { 158 0 : return mnVersion; 159 : } 160 : 161 : /************************************************************************/ 162 : /* NeedsSwap() */ 163 : /************************************************************************/ 164 : 165 : /** 166 : * Checks if the block directory on disk needs swapping. 167 : * 168 : * @return If the block directory on disk needs swapping. 169 : */ 170 19 : bool BlockDir::NeedsSwap(void) const 171 : { 172 19 : return mbNeedsSwap; 173 : } 174 : 175 : /************************************************************************/ 176 : /* IsValid() */ 177 : /************************************************************************/ 178 : 179 : /** 180 : * Checks if the block directory is valid. 181 : * 182 : * @note The block directory is considered valid if the last 183 : * two bytes of the header are the same as when the header was read. 184 : * 185 : * @return If the block directory is valid. 186 : */ 187 8 : bool BlockDir::IsValid(void) const 188 : { 189 8 : if (!mbOnDisk) 190 8 : return true; 191 : 192 : // Read the block directory header from disk. 193 : uint8 abyHeader[512]; 194 : 195 0 : mpoFile->ReadFromSegment(mnSegment, abyHeader, 0, 512); 196 : 197 : // The last 2 bytes of the header are for the valid info. 198 : uint16 nValidInfo; 199 : 200 0 : memcpy(&nValidInfo, abyHeader + 512 - 2, 2); 201 : 202 0 : SwapValue(&nValidInfo); 203 : 204 : // Check if the valid info has changed since the last read. 205 0 : return nValidInfo == mnValidInfo; 206 : } 207 : 208 : /************************************************************************/ 209 : /* IsModified() */ 210 : /************************************************************************/ 211 : 212 : /** 213 : * Checks if the block directory is modified. 214 : * 215 : * @return If the block directory is modified. 216 : */ 217 0 : bool BlockDir::IsModified(void) const 218 : { 219 0 : return mbModified; 220 : } 221 : 222 : /************************************************************************/ 223 : /* GetLayerCount() */ 224 : /************************************************************************/ 225 : 226 : /** 227 : * Gets the number of block layers. 228 : * 229 : * @return The number of block layers. 230 : */ 231 0 : uint32 BlockDir::GetLayerCount(void) const 232 : { 233 0 : return (uint32) moLayerList.size(); 234 : } 235 : 236 : /************************************************************************/ 237 : /* GetLayerType() */ 238 : /************************************************************************/ 239 : 240 : /** 241 : * Gets the type of the block layer specified index. 242 : * 243 : * @param iLayer The index of the block layer. 244 : * 245 : * @return The type of the specified block layer. 246 : */ 247 0 : uint16 BlockDir::GetLayerType(uint32 iLayer) const 248 : { 249 0 : if (iLayer >= moLayerList.size()) 250 0 : return BLTDead; 251 : 252 0 : return moLayerList[iLayer]->GetLayerType(); 253 : } 254 : 255 : /************************************************************************/ 256 : /* GetLayerSize() */ 257 : /************************************************************************/ 258 : 259 : /** 260 : * Gets the size in bytes of the block layer specified index. 261 : * 262 : * @param iLayer The index of the block layer. 263 : * 264 : * @return The size in bytes of the block layer specified index. 265 : */ 266 0 : uint64 BlockDir::GetLayerSize(uint32 iLayer) const 267 : { 268 0 : if (iLayer >= moLayerList.size()) 269 0 : return 0; 270 : 271 0 : return moLayerList[iLayer]->GetLayerSize(); 272 : } 273 : 274 : /************************************************************************/ 275 : /* IsValid() */ 276 : /************************************************************************/ 277 : 278 : /** 279 : * Checks if the block layer at the specified index is valid. 280 : * 281 : * @param iLayer The index of the block layer. 282 : * 283 : * @return If the the specified block layer is valid. 284 : */ 285 0 : bool BlockDir::IsLayerValid(uint32 iLayer) const 286 : { 287 0 : return GetLayerType(iLayer) != BLTDead; 288 : } 289 : 290 : /************************************************************************/ 291 : /* GetLayer() */ 292 : /************************************************************************/ 293 : 294 : /** 295 : * Gets the block layer at the specified index. 296 : * 297 : * @param iLayer The index of the block layer. 298 : * 299 : * @return The block layer at the specified index. 300 : */ 301 62 : BlockLayer * BlockDir::GetLayer(uint32 iLayer) 302 : { 303 62 : if (iLayer >= moLayerList.size()) 304 0 : return nullptr; 305 : 306 62 : return moLayerList[iLayer]; 307 : } 308 : 309 : /************************************************************************/ 310 : /* CreateLayer() */ 311 : /************************************************************************/ 312 : 313 : /** 314 : * Creates a block layer of the specified type. 315 : * 316 : * @param nLayerType The type of the block layer to create. 317 : * 318 : * @return The index of the new block layer. 319 : */ 320 8 : uint32 BlockDir::CreateLayer(int16 nLayerType) 321 : { 322 : // Try to find an invalid layer. 323 8 : uint32 nNewLayerIndex = INVALID_LAYER; 324 : 325 8 : for (size_t iLayer = 0; iLayer < moLayerList.size(); iLayer++) 326 : { 327 0 : if (!moLayerList[iLayer]->IsValid()) 328 : { 329 0 : nNewLayerIndex = (uint32) iLayer; 330 : 331 0 : break; 332 : } 333 : } 334 : 335 8 : if (nNewLayerIndex == INVALID_LAYER) 336 : { 337 8 : nNewLayerIndex = (uint32) moLayerList.size(); 338 : 339 : try 340 : { 341 8 : moLayerList.resize(moLayerList.size() + 1); 342 : } 343 0 : catch (const std::exception & ex) 344 : { 345 0 : return ThrowPCIDSKException(0, "Out of memory in BlockDir::CreateLayer(): %s", ex.what()); 346 : } 347 : } 348 : else 349 : { 350 0 : delete moLayerList[nNewLayerIndex]; 351 : } 352 : 353 : // Call the virtual method _CreateLayer() to create the layer. 354 8 : moLayerList[nNewLayerIndex] = _CreateLayer(nLayerType, nNewLayerIndex); 355 : 356 8 : mbModified = true; 357 : 358 8 : return nNewLayerIndex; 359 : } 360 : 361 : /************************************************************************/ 362 : /* DeleteLayer() */ 363 : /************************************************************************/ 364 : 365 : /** 366 : * Deletes the block layer with the specified index. 367 : * 368 : * @param iLayer The index of the block layer to delete. 369 : */ 370 0 : void BlockDir::DeleteLayer(uint32 iLayer) 371 : { 372 0 : BlockLayer * poLayer = GetLayer(iLayer); 373 : 374 0 : assert(poLayer && poLayer->IsValid()); 375 0 : if (!poLayer || !poLayer->IsValid()) 376 0 : return; 377 : 378 0 : poLayer->Resize(0); 379 : 380 : // Call the virtual method _DeleteLayer() to delete the layer. 381 0 : _DeleteLayer(iLayer); 382 : 383 0 : mbModified = true; 384 : } 385 : 386 : /************************************************************************/ 387 : /* CreateNewBlocks() */ 388 : /************************************************************************/ 389 : 390 : /** 391 : * Creates the specified number of new blocks. 392 : * 393 : * @param nBlockCount The number of blocks to create. 394 : * 395 : * @return The specified number of new blocks. 396 : */ 397 16 : BlockInfoList BlockDir::CreateNewBlocks(uint32 nBlockCount) 398 : { 399 16 : ValidateNewBlocks(nBlockCount, false); 400 : 401 16 : BlockInfoList oNewBlocks(nBlockCount); 402 : 403 16 : BlockInfoList::iterator oIter = oNewBlocks.begin(); 404 16 : BlockInfoList::iterator oEnd = oNewBlocks.end(); 405 : 406 32 : for (; oIter != oEnd; ++oIter) 407 : { 408 16 : oIter->nSegment = INVALID_SEGMENT; 409 16 : oIter->nStartBlock = INVALID_BLOCK; 410 : } 411 : 412 16 : mbModified = true; 413 : 414 32 : return oNewBlocks; 415 : } 416 : 417 : /************************************************************************/ 418 : /* CreateFreeBlocks() */ 419 : /************************************************************************/ 420 : 421 : /** 422 : * Creates the specified number of free blocks. 423 : * 424 : * @note The new blocks are going to be added to the free block layer. 425 : * 426 : * @param nBlockCount The number of blocks to create. 427 : */ 428 8 : void BlockDir::CreateFreeBlocks(uint32 nBlockCount) 429 : { 430 8 : if (!mpoFreeBlockLayer) 431 0 : ReadFreeBlockLayer(); 432 : 433 8 : ValidateNewBlocks(nBlockCount, true); 434 : 435 8 : uint32 nBlockSize = GetBlockSize(); 436 : 437 : uint16 nDataSegment = 438 8 : mpoFile->ExtendSegment(GetDataSegmentName(), GetDataSegmentDesc(), 439 8 : (uint64) nBlockCount * nBlockSize); 440 : 441 8 : uint64 nBlockOffset = mpoFile->GetSegmentSize(nDataSegment); 442 : 443 8 : assert(nBlockOffset % nBlockSize == 0); 444 : 445 : // Reverse the block list because GetFreeBlock() is LIFO. 446 8 : BlockInfoList oFreeBlockList; 447 : 448 8 : oFreeBlockList.reserve(nBlockCount); 449 : 450 136 : for (uint32 iBlock = 0; iBlock < nBlockCount; iBlock++) 451 : { 452 : BlockInfo sFreeBlock; 453 : 454 128 : nBlockOffset -= nBlockSize; 455 : 456 128 : sFreeBlock.nSegment = nDataSegment; 457 128 : sFreeBlock.nStartBlock = (uint32) (nBlockOffset / nBlockSize); 458 : 459 128 : oFreeBlockList.push_back(sFreeBlock); 460 : } 461 : 462 8 : mpoFreeBlockLayer->PushBlocks(oFreeBlockList); 463 : 464 8 : mbModified = true; 465 8 : } 466 : 467 : /************************************************************************/ 468 : /* AddFreeBlocks() */ 469 : /************************************************************************/ 470 : 471 : /** 472 : * Adds the the specified block list to the free block layer. 473 : * 474 : * @note Only the blocks which are allocated will be added to the 475 : * free block layer. 476 : * 477 : * @param oBlockList The block list to add. 478 : */ 479 0 : void BlockDir::AddFreeBlocks(const BlockInfoList & oBlockList) 480 : { 481 0 : if (!mpoFreeBlockLayer) 482 0 : ReadFreeBlockLayer(); 483 : 484 0 : BlockInfoList oValidBlockList; 485 : 486 0 : oValidBlockList.reserve(oBlockList.size()); 487 : 488 : // Reverse the block list because GetFreeBlock() is LIFO. 489 0 : BlockInfoList::const_reverse_iterator oIter = oBlockList.rbegin(); 490 0 : BlockInfoList::const_reverse_iterator oEnd = oBlockList.rend(); 491 : 492 0 : for (; oIter != oEnd; ++oIter) 493 : { 494 0 : if (oIter->nSegment != INVALID_SEGMENT && 495 0 : oIter->nStartBlock != INVALID_BLOCK) 496 : { 497 0 : oValidBlockList.push_back(*oIter); 498 : } 499 : } 500 : 501 0 : mpoFreeBlockLayer->PushBlocks(oValidBlockList); 502 : 503 0 : mbModified = true; 504 0 : } 505 : 506 : /************************************************************************/ 507 : /* GetFreeBlock() */ 508 : /************************************************************************/ 509 : 510 : /** 511 : * Gets a free block from the free block layer. 512 : * 513 : * @note The block will be removed from the free block layer. 514 : * 515 : * @return A free block from the free block layer. 516 : */ 517 16 : BlockInfo BlockDir::GetFreeBlock(void) 518 : { 519 16 : if (!mpoFreeBlockLayer) 520 0 : ReadFreeBlockLayer(); 521 : 522 : // If we need more free blocks, create a minimum of 16 blocks. 523 16 : if (mpoFreeBlockLayer->GetBlockCount() == 0) 524 8 : CreateFreeBlocks(std::max((uint32) 16, GetNewBlockCount())); 525 : 526 16 : if (mpoFreeBlockLayer->GetBlockCount() <= 0) 527 0 : ThrowPCIDSKException("Cannot create new blocks."); 528 : 529 : BlockInfo sFreeBlock; 530 16 : sFreeBlock.nSegment = INVALID_SEGMENT; 531 16 : sFreeBlock.nStartBlock = INVALID_BLOCK; 532 : 533 32 : const BlockInfoList & oFreeBlockList = mpoFreeBlockLayer->PopBlocks(1); 534 : 535 16 : assert(oFreeBlockList.size() == 1); 536 : 537 16 : if (!oFreeBlockList.empty()) 538 16 : sFreeBlock = oFreeBlockList[0]; 539 : 540 16 : mbModified = true; 541 : 542 32 : return sFreeBlock; 543 : } 544 : 545 : /************************************************************************/ 546 : /* SwapValue() */ 547 : /************************************************************************/ 548 : 549 : /** 550 : * Swaps the specified value. 551 : * 552 : * @param pnValue The value to swap. 553 : */ 554 32 : void BlockDir::SwapValue(uint16 * pnValue) const 555 : { 556 32 : if (!mbNeedsSwap) 557 32 : return; 558 : 559 0 : SwapData(pnValue, 2, 1); 560 : }