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