/src/gdal/frmts/pcidsk/sdk/blockdir/blocklayer.cpp
Line | Count | Source |
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 | | BlockLayer::BlockLayer(BlockDir * poBlockDir, uint32 nLayer) |
30 | 336 | : mpoBlockDir(poBlockDir), |
31 | 336 | mnLayer(nLayer) |
32 | 336 | { |
33 | 336 | } |
34 | | |
35 | | /************************************************************************/ |
36 | | /* ~BlockLayer() */ |
37 | | /************************************************************************/ |
38 | | |
39 | | /** |
40 | | * Destructor. |
41 | | */ |
42 | | BlockLayer::~BlockLayer(void) |
43 | 336 | { |
44 | 336 | } |
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 | | BlockInfo * BlockLayer::GetBlockInfo(uint32 iBlock) |
58 | 1.08k | { |
59 | 1.08k | if (!IsValid()) |
60 | 3 | return nullptr; |
61 | | |
62 | 1.08k | uint32 nBlockCount = GetBlockCount(); |
63 | | |
64 | 1.08k | if (nBlockCount != moBlockList.size()) |
65 | 53 | { |
66 | 53 | mpoBlockDir->ReadLayerBlocks(mnLayer); |
67 | | |
68 | 53 | if (moBlockList.size() != nBlockCount) |
69 | 0 | ThrowPCIDSKExceptionPtr("Corrupted block directory."); |
70 | 53 | } |
71 | | |
72 | 1.08k | if (iBlock >= moBlockList.size()) |
73 | 73 | return nullptr; |
74 | | |
75 | 1.00k | return &moBlockList[iBlock]; |
76 | 1.08k | } |
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 | | void BlockLayer::AllocateBlocks(uint64 nOffset, uint64 nSize) |
89 | 0 | { |
90 | 0 | uint32 nBlockSize = mpoBlockDir->GetBlockSize(); |
91 | |
|
92 | 0 | uint32 iStartBlock = (uint32) (nOffset / nBlockSize); |
93 | 0 | uint32 nStartOffset = (uint32) (nOffset % nBlockSize); |
94 | |
|
95 | 0 | uint32 nNumBlocks = (uint32) DIV_ROUND_UP(nSize + nStartOffset, nBlockSize); |
96 | |
|
97 | 0 | for (uint32 iBlock = 0; iBlock < nNumBlocks; iBlock++) |
98 | 0 | { |
99 | 0 | BlockInfo * psBlock = GetBlockInfo(iStartBlock + iBlock); |
100 | |
|
101 | 0 | if (!psBlock) |
102 | 0 | break; |
103 | | |
104 | 0 | if (psBlock->nSegment == INVALID_SEGMENT || |
105 | 0 | psBlock->nStartBlock == INVALID_BLOCK) |
106 | 0 | { |
107 | 0 | *psBlock = mpoBlockDir->GetFreeBlock(); |
108 | 0 | } |
109 | 0 | } |
110 | 0 | } |
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 | | bool BlockLayer::AreBlocksAllocated(uint64 nOffset, uint64 nSize) |
125 | 412 | { |
126 | 412 | uint32 nBlockSize = mpoBlockDir->GetBlockSize(); |
127 | | |
128 | 412 | uint32 iStartBlock = (uint32) (nOffset / nBlockSize); |
129 | 412 | uint32 nStartOffset = (uint32) (nOffset % nBlockSize); |
130 | | |
131 | 412 | uint32 nNumBlocks = (uint32) DIV_ROUND_UP(nSize + nStartOffset, nBlockSize); |
132 | | |
133 | 751 | for (uint32 iBlock = 0; iBlock < nNumBlocks; iBlock++) |
134 | 415 | { |
135 | 415 | BlockInfo * psBlock = GetBlockInfo(iStartBlock + iBlock); |
136 | | |
137 | 415 | if (!psBlock) |
138 | 76 | return false; |
139 | | |
140 | 339 | if (psBlock->nSegment == INVALID_SEGMENT || |
141 | 338 | psBlock->nStartBlock == INVALID_BLOCK) |
142 | 0 | { |
143 | 0 | return false; |
144 | 0 | } |
145 | 339 | } |
146 | | |
147 | 336 | return true; |
148 | 412 | } |
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 | | uint32 BlockLayer::GetContiguousCount(uint64 nOffset, uint64 nSize) |
163 | 335 | { |
164 | 335 | uint32 nBlockSize = mpoBlockDir->GetBlockSize(); |
165 | | |
166 | 335 | uint32 iStartBlock = (uint32) (nOffset / nBlockSize); |
167 | 335 | uint32 nStartOffset = (uint32) (nOffset % nBlockSize); |
168 | | |
169 | 335 | uint32 nNumBlocks = (uint32) DIV_ROUND_UP(nSize + nStartOffset, nBlockSize); |
170 | | |
171 | 335 | BlockInfo * psStartBlock = GetBlockInfo(iStartBlock); |
172 | | |
173 | 335 | if (!psStartBlock) |
174 | 0 | return 0; |
175 | | |
176 | 335 | uint32 nContiguousCount = 1; |
177 | | |
178 | 335 | for (uint32 iBlock = 1; iBlock < nNumBlocks; iBlock++) |
179 | 0 | { |
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 | 0 | } |
193 | | |
194 | 335 | return nContiguousCount; |
195 | 335 | } |
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 | | void BlockLayer::FreeBlocks(uint64 nOffset, uint64 nSize) |
208 | 0 | { |
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 | 0 | { |
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 | 0 | { |
230 | 0 | oFreeBlocks.push_back(*psBlock); |
231 | |
|
232 | 0 | psBlock->nSegment = INVALID_SEGMENT; |
233 | 0 | psBlock->nStartBlock = INVALID_BLOCK; |
234 | 0 | } |
235 | 0 | } |
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 | | void BlockLayer::WriteToLayer(const void * pData, uint64 nOffset, uint64 nSize) |
252 | 0 | { |
253 | 0 | if (nOffset + nSize > GetLayerSize()) |
254 | 0 | Resize(nOffset + nSize); |
255 | |
|
256 | 0 | AllocateBlocks(nOffset, nSize); |
257 | |
|
258 | 0 | uint32 nBlockSize = mpoBlockDir->GetBlockSize(); |
259 | |
|
260 | 0 | uint8 * pabyData = (uint8 *) pData; |
261 | |
|
262 | 0 | for (uint64 iByte = 0; iByte < nSize; ) |
263 | 0 | { |
264 | 0 | uint32 nContiguousCount = |
265 | 0 | GetContiguousCount(nOffset + iByte, nSize - iByte); |
266 | |
|
267 | 0 | uint32 iBlock = (uint32) ((nOffset + iByte) / nBlockSize); |
268 | 0 | uint32 iWork = (uint32) ((nOffset + iByte) % nBlockSize); |
269 | |
|
270 | 0 | uint64 nWorkSize = (uint64)nContiguousCount * nBlockSize - iWork; |
271 | |
|
272 | 0 | if (nWorkSize > nSize - iByte) |
273 | 0 | nWorkSize = nSize - iByte; |
274 | |
|
275 | 0 | BlockInfo * psBlock = GetBlockInfo(iBlock); |
276 | |
|
277 | 0 | uint64 nWorkOffset = (uint64) psBlock->nStartBlock * nBlockSize + iWork; |
278 | |
|
279 | 0 | GetFile()->WriteToSegment(psBlock->nSegment, pabyData + iByte, |
280 | 0 | nWorkOffset, nWorkSize); |
281 | |
|
282 | 0 | iByte += nWorkSize; |
283 | 0 | } |
284 | 0 | } |
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 | | bool BlockLayer::ReadFromLayer(void * pData, uint64 nOffset, uint64 nSize) |
298 | 599 | { |
299 | 599 | uint64 nLayerSize = GetLayerSize(); |
300 | | |
301 | 599 | if (nSize > nLayerSize || |
302 | 503 | nOffset > nLayerSize || |
303 | 412 | nOffset + nSize > nLayerSize) |
304 | 187 | { |
305 | 187 | return false; |
306 | 187 | } |
307 | | |
308 | 412 | if (!AreBlocksAllocated(nOffset, nSize)) |
309 | 76 | return false; |
310 | | |
311 | 336 | uint32 nBlockSize = mpoBlockDir->GetBlockSize(); |
312 | | |
313 | 336 | uint8 * pabyData = (uint8 *) pData; |
314 | | |
315 | 671 | for (uint64 iByte = 0; iByte < nSize; ) |
316 | 335 | { |
317 | 335 | uint32 nContiguousCount = |
318 | 335 | GetContiguousCount(nOffset + iByte, nSize - iByte); |
319 | | |
320 | 335 | uint32 iBlock = (uint32) ((nOffset + iByte) / nBlockSize); |
321 | 335 | uint32 iWork = (uint32) ((nOffset + iByte) % nBlockSize); |
322 | | |
323 | 335 | uint64 nWorkSize = (uint64)nContiguousCount * nBlockSize - iWork; |
324 | | |
325 | 335 | if (nWorkSize > nSize - iByte) |
326 | 310 | nWorkSize = nSize - iByte; |
327 | | |
328 | 335 | BlockInfo * psBlock = GetBlockInfo(iBlock); |
329 | | |
330 | 335 | uint64 nWorkOffset = (uint64) psBlock->nStartBlock * nBlockSize + iWork; |
331 | | |
332 | 335 | GetFile()->ReadFromSegment(psBlock->nSegment, pabyData + iByte, |
333 | 335 | nWorkOffset, nWorkSize); |
334 | | |
335 | 335 | iByte += nWorkSize; |
336 | 335 | } |
337 | | |
338 | 336 | return true; |
339 | 412 | } |
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 | | BlockFile * BlockLayer::GetFile(void) const |
351 | 389 | { |
352 | 389 | return mpoBlockDir->GetFile(); |
353 | 389 | } |
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 | | bool BlockLayer::NeedsSwap(void) const |
365 | 0 | { |
366 | 0 | return mpoBlockDir->NeedsSwap(); |
367 | 0 | } |
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 | | bool BlockLayer::IsValid(void) const |
379 | 2.87k | { |
380 | 2.87k | return GetLayerType() != BLTDead; |
381 | 2.87k | } |
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 | | void BlockLayer::Resize(uint64 nLayerSize) |
393 | 0 | { |
394 | 0 | if (!IsValid()) |
395 | 0 | return; |
396 | | |
397 | 0 | if (nLayerSize == GetLayerSize()) |
398 | 0 | return; |
399 | | |
400 | 0 | uint32 nBlockCount = GetBlockCount(); |
401 | |
|
402 | 0 | uint32 nBlockSize = mpoBlockDir->GetBlockSize(); |
403 | | |
404 | | // Check how many blocks are needed. |
405 | 0 | uint32 nNeededBlocks = (uint32) DIV_ROUND_UP(nLayerSize, nBlockSize); |
406 | | |
407 | | // Create new blocks. |
408 | 0 | if (nNeededBlocks > nBlockCount) |
409 | 0 | { |
410 | 0 | uint32 nNewBlocks = nNeededBlocks - nBlockCount; |
411 | |
|
412 | 0 | PushBlocks(mpoBlockDir->CreateNewBlocks(nNewBlocks)); |
413 | 0 | } |
414 | | // Free blocks. |
415 | 0 | else if (nNeededBlocks < nBlockCount) |
416 | 0 | { |
417 | 0 | uint32 nFreeBlocks = nBlockCount - nNeededBlocks; |
418 | |
|
419 | 0 | mpoBlockDir->AddFreeBlocks(PopBlocks(nFreeBlocks)); |
420 | 0 | } |
421 | |
|
422 | 0 | _SetLayerSize(nLayerSize); |
423 | 0 | } |
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 | | void BlockLayer::PushBlocks(const BlockInfoList & oBlockList) |
435 | 0 | { |
436 | 0 | uint32 nBlockCount = GetBlockCount(); |
437 | |
|
438 | 0 | if (nBlockCount != moBlockList.size()) |
439 | 0 | { |
440 | 0 | mpoBlockDir->ReadLayerBlocks(mnLayer); |
441 | |
|
442 | 0 | if (moBlockList.size() != nBlockCount) |
443 | 0 | ThrowPCIDSKException("Corrupted block directory."); |
444 | 0 | } |
445 | |
|
446 | 0 | try |
447 | 0 | { |
448 | 0 | moBlockList.resize(nBlockCount + oBlockList.size()); |
449 | 0 | } |
450 | 0 | catch (const std::exception & ex) |
451 | 0 | { |
452 | 0 | return ThrowPCIDSKException("Out of memory in BlockLayer::PushBlocks(): %s", ex.what()); |
453 | 0 | } |
454 | | |
455 | 0 | for (size_t iBlock = 0; iBlock < oBlockList.size(); iBlock++) |
456 | 0 | moBlockList[nBlockCount + iBlock] = oBlockList[iBlock]; |
457 | |
|
458 | 0 | _SetBlockCount((uint32) moBlockList.size()); |
459 | 0 | } |
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 | | BlockInfoList BlockLayer::PopBlocks(uint32 nBlockCount) |
473 | 0 | { |
474 | 0 | uint32 nCurrentBlockCount = GetBlockCount(); |
475 | |
|
476 | 0 | if (nCurrentBlockCount != moBlockList.size()) |
477 | 0 | { |
478 | 0 | mpoBlockDir->ReadLayerBlocks(mnLayer); |
479 | |
|
480 | 0 | if (moBlockList.size() != nCurrentBlockCount) |
481 | 0 | ThrowPCIDSKException("Corrupted block directory."); |
482 | 0 | } |
483 | |
|
484 | 0 | uint32 nRemainingBlockCount; |
485 | |
|
486 | 0 | BlockInfoList oRemovedBlocks; |
487 | |
|
488 | 0 | if (nBlockCount < nCurrentBlockCount) |
489 | 0 | { |
490 | 0 | nRemainingBlockCount = nCurrentBlockCount - nBlockCount; |
491 | |
|
492 | 0 | oRemovedBlocks = |
493 | 0 | BlockInfoList(moBlockList.begin() + nRemainingBlockCount, |
494 | 0 | moBlockList.begin() + nCurrentBlockCount); |
495 | 0 | } |
496 | 0 | else |
497 | 0 | { |
498 | 0 | nRemainingBlockCount = 0; |
499 | |
|
500 | 0 | oRemovedBlocks = moBlockList; |
501 | 0 | } |
502 | |
|
503 | 0 | try |
504 | 0 | { |
505 | 0 | moBlockList.resize(nRemainingBlockCount); |
506 | 0 | } |
507 | 0 | catch (const std::exception & ex) |
508 | 0 | { |
509 | 0 | ThrowPCIDSKException("Out of memory in BlockLayer::PopBlocks(): %s", ex.what()); |
510 | 0 | } |
511 | |
|
512 | 0 | _SetBlockCount(nRemainingBlockCount); |
513 | |
|
514 | 0 | return oRemovedBlocks; |
515 | 0 | } |