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