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/blocklayer.h" 13 : #include "blockdir/blockfile.h" 14 : #include "pcidsk_exception.h" 15 : 16 : using namespace PCIDSK; 17 : 18 : /************************************************************************/ 19 : /* BlockLayer() */ 20 : /************************************************************************/ 21 : 22 : /** 23 : * Constructor. 24 : * 25 : * @param poBlockDir The associated block directory. 26 : * @param nLayer The index of the block layer. 27 : */ 28 49 : BlockLayer::BlockLayer(BlockDir * poBlockDir, uint32 nLayer) 29 : : mpoBlockDir(poBlockDir), 30 49 : mnLayer(nLayer) 31 : { 32 49 : } 33 : 34 : /************************************************************************/ 35 : /* ~BlockLayer() */ 36 : /************************************************************************/ 37 : 38 : /** 39 : * Destructor. 40 : */ 41 49 : BlockLayer::~BlockLayer(void) 42 : { 43 49 : } 44 : 45 : /************************************************************************/ 46 : /* GetBlockInfo() */ 47 : /************************************************************************/ 48 : 49 : /** 50 : * Gets the layer block at the specified index. 51 : * 52 : * @param iBlock The index of the layer block. 53 : * 54 : * @return The layer block at the specified index. 55 : */ 56 222 : BlockInfo * BlockLayer::GetBlockInfo(uint32 iBlock) 57 : { 58 222 : if (!IsValid()) 59 0 : return nullptr; 60 : 61 222 : uint32 nBlockCount = GetBlockCount(); 62 : 63 222 : if (nBlockCount != moBlockList.size()) 64 : { 65 11 : mpoBlockDir->ReadLayerBlocks(mnLayer); 66 : 67 11 : if (moBlockList.size() != nBlockCount) 68 0 : ThrowPCIDSKExceptionPtr("Corrupted block directory."); 69 : } 70 : 71 222 : if (iBlock >= moBlockList.size()) 72 0 : return nullptr; 73 : 74 222 : return &moBlockList[iBlock]; 75 : } 76 : 77 : /************************************************************************/ 78 : /* AllocateBlocks() */ 79 : /************************************************************************/ 80 : 81 : /** 82 : * Allocates the blocks of the specified data. 83 : * 84 : * @param nOffset The offset of the data. 85 : * @param nSize The size of the data. 86 : */ 87 24 : void BlockLayer::AllocateBlocks(uint64 nOffset, uint64 nSize) 88 : { 89 24 : uint32 nBlockSize = mpoBlockDir->GetBlockSize(); 90 : 91 24 : uint32 iStartBlock = (uint32) (nOffset / nBlockSize); 92 24 : uint32 nStartOffset = (uint32) (nOffset % nBlockSize); 93 : 94 24 : uint32 nNumBlocks = (uint32) 95 24 : ((nSize + nStartOffset + nBlockSize - 1) / nBlockSize); 96 : 97 48 : for (uint32 iBlock = 0; iBlock < nNumBlocks; iBlock++) 98 : { 99 24 : BlockInfo * psBlock = GetBlockInfo(iStartBlock + iBlock); 100 : 101 24 : if (!psBlock) 102 0 : break; 103 : 104 24 : if (psBlock->nSegment == INVALID_SEGMENT || 105 8 : psBlock->nStartBlock == INVALID_BLOCK) 106 : { 107 16 : *psBlock = mpoBlockDir->GetFreeBlock(); 108 : } 109 : } 110 24 : } 111 : 112 : /************************************************************************/ 113 : /* AreBlocksAllocated() */ 114 : /************************************************************************/ 115 : 116 : /** 117 : * Checks if the blocks of the specified data are allocated. 118 : * 119 : * @param nOffset The offset of the data. 120 : * @param nSize The size of the data. 121 : * 122 : * @return If the blocks of the specified data are allocated. 123 : */ 124 46 : bool BlockLayer::AreBlocksAllocated(uint64 nOffset, uint64 nSize) 125 : { 126 46 : uint32 nBlockSize = mpoBlockDir->GetBlockSize(); 127 : 128 46 : uint32 iStartBlock = (uint32) (nOffset / nBlockSize); 129 46 : uint32 nStartOffset = (uint32) (nOffset % nBlockSize); 130 : 131 46 : uint32 nNumBlocks = (uint32) 132 46 : ((nSize + nStartOffset + nBlockSize - 1) / nBlockSize); 133 : 134 92 : for (uint32 iBlock = 0; iBlock < nNumBlocks; iBlock++) 135 : { 136 46 : BlockInfo * psBlock = GetBlockInfo(iStartBlock + iBlock); 137 : 138 46 : if (!psBlock) 139 0 : return false; 140 : 141 46 : if (psBlock->nSegment == INVALID_SEGMENT || 142 46 : psBlock->nStartBlock == INVALID_BLOCK) 143 : { 144 0 : return false; 145 : } 146 : } 147 : 148 46 : return true; 149 : } 150 : 151 : /************************************************************************/ 152 : /* GetContiguousCount() */ 153 : /************************************************************************/ 154 : 155 : /** 156 : * Gets the number of contiguous blocks for the specified data. 157 : * 158 : * @param nOffset The offset of the data. 159 : * @param nSize The size of the data. 160 : * 161 : * @return The number of contiguous blocks for the specified data. 162 : */ 163 70 : uint32 BlockLayer::GetContiguousCount(uint64 nOffset, uint64 nSize) 164 : { 165 70 : uint32 nBlockSize = mpoBlockDir->GetBlockSize(); 166 : 167 70 : uint32 iStartBlock = (uint32) (nOffset / nBlockSize); 168 70 : uint32 nStartOffset = (uint32) (nOffset % nBlockSize); 169 : 170 70 : uint32 nNumBlocks = (uint32) 171 70 : ((nSize + nStartOffset + nBlockSize - 1) / nBlockSize); 172 : 173 70 : BlockInfo * psStartBlock = GetBlockInfo(iStartBlock); 174 : 175 70 : if (!psStartBlock) 176 0 : return 0; 177 : 178 70 : uint32 nContiguousCount = 1; 179 : 180 70 : for (uint32 iBlock = 1; iBlock < nNumBlocks; iBlock++) 181 : { 182 0 : BlockInfo * psNextBlock = GetBlockInfo(iStartBlock + iBlock); 183 : 184 0 : if (!psNextBlock) 185 0 : break; 186 : 187 0 : if (psNextBlock->nSegment != psStartBlock->nSegment) 188 0 : break; 189 : 190 0 : if (psNextBlock->nStartBlock != psStartBlock->nStartBlock + iBlock) 191 0 : break; 192 : 193 0 : nContiguousCount++; 194 : } 195 : 196 70 : return nContiguousCount; 197 : } 198 : 199 : /************************************************************************/ 200 : /* FreeBlocks() */ 201 : /************************************************************************/ 202 : 203 : /** 204 : * Frees the blocks of the specified data. 205 : * 206 : * @param nOffset The offset of the data. 207 : * @param nSize The size of the data. 208 : */ 209 0 : void BlockLayer::FreeBlocks(uint64 nOffset, uint64 nSize) 210 : { 211 0 : uint32 nBlockSize = mpoBlockDir->GetBlockSize(); 212 : 213 0 : uint32 iStartBlock = (uint32) ((nOffset + nBlockSize - 1) / nBlockSize); 214 0 : uint32 iEndBlock = (uint32) ((nOffset + nSize) / nBlockSize); 215 : 216 0 : uint32 nNumBlocks = iStartBlock < iEndBlock ? iEndBlock - iStartBlock : 0; 217 : 218 0 : BlockInfoList oFreeBlocks; 219 : 220 0 : oFreeBlocks.reserve(nNumBlocks); 221 : 222 0 : for (uint32 iBlock = 0; iBlock < nNumBlocks; iBlock++) 223 : { 224 0 : BlockInfo * psBlock = GetBlockInfo(iStartBlock + iBlock); 225 : 226 0 : if (!psBlock) 227 0 : break; 228 : 229 0 : if (psBlock->nSegment != INVALID_SEGMENT && 230 0 : psBlock->nStartBlock != INVALID_BLOCK) 231 : { 232 0 : oFreeBlocks.push_back(*psBlock); 233 : 234 0 : psBlock->nSegment = INVALID_SEGMENT; 235 0 : psBlock->nStartBlock = INVALID_BLOCK; 236 : } 237 : } 238 : 239 0 : mpoBlockDir->AddFreeBlocks(oFreeBlocks); 240 0 : } 241 : 242 : /************************************************************************/ 243 : /* WriteToLayer() */ 244 : /************************************************************************/ 245 : 246 : /** 247 : * Writes the specified data to the layer. 248 : * 249 : * @param pData The data buffer to write. 250 : * @param nOffset The offset of the data. 251 : * @param nSize The size of the data. 252 : */ 253 24 : void BlockLayer::WriteToLayer(const void * pData, uint64 nOffset, uint64 nSize) 254 : { 255 24 : if (nOffset + nSize > GetLayerSize()) 256 16 : Resize(nOffset + nSize); 257 : 258 24 : AllocateBlocks(nOffset, nSize); 259 : 260 24 : uint32 nBlockSize = mpoBlockDir->GetBlockSize(); 261 : 262 24 : uint8 * pabyData = (uint8 *) pData; 263 : 264 48 : for (uint64 iByte = 0; iByte < nSize; ) 265 : { 266 : uint32 nContiguousCount = 267 24 : GetContiguousCount(nOffset + iByte, nSize - iByte); 268 : 269 24 : uint32 iBlock = (uint32) ((nOffset + iByte) / nBlockSize); 270 24 : uint32 iWork = (uint32) ((nOffset + iByte) % nBlockSize); 271 : 272 24 : uint64 nWorkSize = (uint64)nContiguousCount * nBlockSize - iWork; 273 : 274 24 : if (nWorkSize > nSize - iByte) 275 23 : nWorkSize = nSize - iByte; 276 : 277 24 : BlockInfo * psBlock = GetBlockInfo(iBlock); 278 : 279 24 : uint64 nWorkOffset = (uint64) psBlock->nStartBlock * nBlockSize + iWork; 280 : 281 24 : GetFile()->WriteToSegment(psBlock->nSegment, pabyData + iByte, 282 24 : nWorkOffset, nWorkSize); 283 : 284 24 : iByte += nWorkSize; 285 : } 286 24 : } 287 : 288 : /************************************************************************/ 289 : /* ReadFromLayer() */ 290 : /************************************************************************/ 291 : 292 : /** 293 : * Reads the specified data from the layer. 294 : * 295 : * @param pData The data buffer to read. 296 : * @param nOffset The offset of the data. 297 : * @param nSize The size of the data. 298 : */ 299 46 : bool BlockLayer::ReadFromLayer(void * pData, uint64 nOffset, uint64 nSize) 300 : { 301 46 : uint64 nLayerSize = GetLayerSize(); 302 : 303 46 : if (nSize > nLayerSize || 304 46 : nOffset > nLayerSize || 305 46 : nOffset + nSize > nLayerSize) 306 : { 307 0 : return false; 308 : } 309 : 310 46 : if (!AreBlocksAllocated(nOffset, nSize)) 311 0 : return false; 312 : 313 46 : uint32 nBlockSize = mpoBlockDir->GetBlockSize(); 314 : 315 46 : uint8 * pabyData = (uint8 *) pData; 316 : 317 92 : for (uint64 iByte = 0; iByte < nSize; ) 318 : { 319 : uint32 nContiguousCount = 320 46 : GetContiguousCount(nOffset + iByte, nSize - iByte); 321 : 322 46 : uint32 iBlock = (uint32) ((nOffset + iByte) / nBlockSize); 323 46 : uint32 iWork = (uint32) ((nOffset + iByte) % nBlockSize); 324 : 325 46 : uint64 nWorkSize = (uint64)nContiguousCount * nBlockSize - iWork; 326 : 327 46 : if (nWorkSize > nSize - iByte) 328 43 : nWorkSize = nSize - iByte; 329 : 330 46 : BlockInfo * psBlock = GetBlockInfo(iBlock); 331 : 332 46 : uint64 nWorkOffset = (uint64) psBlock->nStartBlock * nBlockSize + iWork; 333 : 334 46 : GetFile()->ReadFromSegment(psBlock->nSegment, pabyData + iByte, 335 46 : nWorkOffset, nWorkSize); 336 : 337 46 : iByte += nWorkSize; 338 : } 339 : 340 46 : return true; 341 : } 342 : 343 : /************************************************************************/ 344 : /* GetFile() */ 345 : /************************************************************************/ 346 : 347 : /** 348 : * Gets the associated file of the block layer. 349 : * 350 : * @return The associated file of the block layer. 351 : */ 352 90 : BlockFile * BlockLayer::GetFile(void) const 353 : { 354 90 : return mpoBlockDir->GetFile(); 355 : } 356 : 357 : /************************************************************************/ 358 : /* NeedsSwap() */ 359 : /************************************************************************/ 360 : 361 : /** 362 : * Checks if the block directory on disk needs swapping. 363 : * 364 : * @return If the block directory on disk needs swapping. 365 : */ 366 0 : bool BlockLayer::NeedsSwap(void) const 367 : { 368 0 : return mpoBlockDir->NeedsSwap(); 369 : } 370 : 371 : /************************************************************************/ 372 : /* IsValid() */ 373 : /************************************************************************/ 374 : 375 : /** 376 : * Checks if the block layer is valid. 377 : * 378 : * @return If the block layer is valid. 379 : */ 380 382 : bool BlockLayer::IsValid(void) const 381 : { 382 382 : return GetLayerType() != BLTDead; 383 : } 384 : 385 : /************************************************************************/ 386 : /* Resize() */ 387 : /************************************************************************/ 388 : 389 : /** 390 : * Resizes the block layer to the specified size in bytes. 391 : * 392 : * @param nLayerSize The new block layer size in bytes. 393 : */ 394 24 : void BlockLayer::Resize(uint64 nLayerSize) 395 : { 396 24 : if (!IsValid()) 397 0 : return; 398 : 399 24 : if (nLayerSize == GetLayerSize()) 400 0 : return; 401 : 402 24 : uint32 nBlockCount = GetBlockCount(); 403 : 404 24 : uint32 nBlockSize = mpoBlockDir->GetBlockSize(); 405 : 406 : // Check how many blocks are needed. 407 24 : uint32 nNeededBlocks = 408 24 : (uint32) ((nLayerSize + nBlockSize - 1) / nBlockSize); 409 : 410 : // Create new blocks. 411 24 : if (nNeededBlocks > nBlockCount) 412 : { 413 16 : uint32 nNewBlocks = nNeededBlocks - nBlockCount; 414 : 415 16 : PushBlocks(mpoBlockDir->CreateNewBlocks(nNewBlocks)); 416 : } 417 : // Free blocks. 418 8 : else if (nNeededBlocks < nBlockCount) 419 : { 420 0 : uint32 nFreeBlocks = nBlockCount - nNeededBlocks; 421 : 422 0 : mpoBlockDir->AddFreeBlocks(PopBlocks(nFreeBlocks)); 423 : } 424 : 425 24 : _SetLayerSize(nLayerSize); 426 : } 427 : 428 : /************************************************************************/ 429 : /* PushBlocks() */ 430 : /************************************************************************/ 431 : 432 : /** 433 : * Pushes the specified block list at the end of the layer's block list. 434 : * 435 : * @param oBlockList The block list to add. 436 : */ 437 24 : void BlockLayer::PushBlocks(const BlockInfoList & oBlockList) 438 : { 439 24 : uint32 nBlockCount = GetBlockCount(); 440 : 441 24 : if (nBlockCount != moBlockList.size()) 442 : { 443 0 : mpoBlockDir->ReadLayerBlocks(mnLayer); 444 : 445 0 : if (moBlockList.size() != nBlockCount) 446 0 : ThrowPCIDSKException("Corrupted block directory."); 447 : } 448 : 449 : try 450 : { 451 24 : moBlockList.resize(nBlockCount + oBlockList.size()); 452 : } 453 0 : catch (const std::exception & ex) 454 : { 455 0 : return ThrowPCIDSKException("Out of memory in BlockLayer::PushBlocks(): %s", ex.what()); 456 : } 457 : 458 168 : for (size_t iBlock = 0; iBlock < oBlockList.size(); iBlock++) 459 144 : moBlockList[nBlockCount + iBlock] = oBlockList[iBlock]; 460 : 461 24 : _SetBlockCount((uint32) moBlockList.size()); 462 : } 463 : 464 : /************************************************************************/ 465 : /* PopBlocks() */ 466 : /************************************************************************/ 467 : 468 : /** 469 : * Pops the specified number of blocks from the end of the layer's block list. 470 : * 471 : * @param nBlockCount The number of blocks to remove. 472 : * 473 : * @return The removed block list. 474 : */ 475 16 : BlockInfoList BlockLayer::PopBlocks(uint32 nBlockCount) 476 : { 477 16 : uint32 nCurrentBlockCount = GetBlockCount(); 478 : 479 16 : if (nCurrentBlockCount != moBlockList.size()) 480 : { 481 0 : mpoBlockDir->ReadLayerBlocks(mnLayer); 482 : 483 0 : if (moBlockList.size() != nCurrentBlockCount) 484 0 : ThrowPCIDSKException("Corrupted block directory."); 485 : } 486 : 487 : uint32 nRemainingBlockCount; 488 : 489 16 : BlockInfoList oRemovedBlocks; 490 : 491 16 : if (nBlockCount < nCurrentBlockCount) 492 : { 493 16 : nRemainingBlockCount = nCurrentBlockCount - nBlockCount; 494 : 495 : oRemovedBlocks = 496 64 : BlockInfoList(moBlockList.begin() + nRemainingBlockCount, 497 48 : moBlockList.begin() + nCurrentBlockCount); 498 : } 499 : else 500 : { 501 0 : nRemainingBlockCount = 0; 502 : 503 0 : oRemovedBlocks = moBlockList; 504 : } 505 : 506 : try 507 : { 508 16 : moBlockList.resize(nRemainingBlockCount); 509 : } 510 0 : catch (const std::exception & ex) 511 : { 512 0 : ThrowPCIDSKException("Out of memory in BlockLayer::PopBlocks(): %s", ex.what()); 513 : } 514 : 515 16 : _SetBlockCount(nRemainingBlockCount); 516 : 517 16 : return oRemovedBlocks; 518 : }