Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}