Coverage Report

Created: 2026-05-16 08:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Purpose:  Implementation of the VecSegIndex class.
4
 *
5
 * This class is used to manage a vector segment data block index.  There
6
 * will be two instances created, one for the record data (sec_record) and
7
 * one for the vertices (sec_vert).  This class is exclusively a private
8
 * helper class for VecSegHeader.
9
 *
10
 ******************************************************************************
11
 * Copyright (c) 2010
12
 * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
13
 *
14
 * SPDX-License-Identifier: MIT
15
 ****************************************************************************/
16
17
#include "pcidsk.h"
18
#include "core/pcidsk_utils.h"
19
#include "segment/cpcidskvectorsegment.h"
20
#include <cassert>
21
#include <cstring>
22
#include <cstdio>
23
#include <limits>
24
25
using namespace PCIDSK;
26
27
/* -------------------------------------------------------------------- */
28
/*      Size of a block in the record/vertex block tables.  This is    */
29
/*      determined by the PCIDSK format and may not be changed.         */
30
/* -------------------------------------------------------------------- */
31
static const int block_page_size = 8192;
32
33
/************************************************************************/
34
/*                          VecSegDataIndex()                           */
35
/************************************************************************/
36
37
VecSegDataIndex::VecSegDataIndex()
38
39
8.80k
{
40
8.80k
    block_initialized = false;
41
8.80k
    vs = nullptr;
42
8.80k
    dirty = false;
43
8.80k
    section = 0;
44
8.80k
    offset_on_disk_within_section = 0;
45
8.80k
    size_on_disk = 0;
46
8.80k
    block_count = 0;
47
8.80k
    bytes = 0;
48
8.80k
}
49
50
/************************************************************************/
51
/*                          ~VecSegDataIndex()                          */
52
/************************************************************************/
53
54
VecSegDataIndex::~VecSegDataIndex()
55
56
8.80k
{
57
8.80k
}
58
59
/************************************************************************/
60
/*                             Initialize()                             */
61
/************************************************************************/
62
63
void VecSegDataIndex::Initialize( CPCIDSKVectorSegment *vsIn, int sectionIn )
64
65
8.71k
{
66
8.71k
    this->section = sectionIn;
67
8.71k
    this->vs = vsIn;
68
69
8.71k
    if( section == sec_vert )
70
4.35k
        offset_on_disk_within_section = 0;
71
4.35k
    else
72
4.35k
        offset_on_disk_within_section = vs->di[sec_vert].SerializedSize();
73
74
8.71k
    uint32 offset = offset_on_disk_within_section
75
8.71k
        + vs->vh.section_offsets[hsec_shape];
76
77
8.71k
    memcpy( &block_count, vs->GetData(sec_raw,offset,nullptr,4), 4);
78
8.71k
    memcpy( &bytes, vs->GetData(sec_raw,offset+4,nullptr,4), 4);
79
80
8.71k
    bool needs_swap = !BigEndianSystem();
81
82
8.71k
    if( needs_swap )
83
8.71k
    {
84
8.71k
        SwapData( &block_count, 4, 1 );
85
8.71k
        SwapData( &bytes, 4, 1 );
86
8.71k
    }
87
88
8.71k
    if( block_count > (std::numeric_limits<uint32>::max() - 8) /4 )
89
0
    {
90
0
        throw PCIDSKException("Invalid block_count: %u", block_count);
91
0
    }
92
93
8.71k
    size_on_disk = block_count * 4 + 8;
94
8.71k
}
95
96
/************************************************************************/
97
/*                           SerializedSize()                           */
98
/************************************************************************/
99
100
uint32 VecSegDataIndex::SerializedSize()
101
102
15.8k
{
103
15.8k
    return 8 + 4 * block_count;
104
15.8k
}
105
106
/************************************************************************/
107
/*                           GetBlockIndex()                            */
108
/************************************************************************/
109
110
const std::vector<uint32> *VecSegDataIndex::GetIndex()
111
112
29.0k
{
113
/* -------------------------------------------------------------------- */
114
/*      Load block map if needed.                                       */
115
/* -------------------------------------------------------------------- */
116
29.0k
    if( !block_initialized )
117
2.68k
    {
118
2.68k
        bool needs_swap = !BigEndianSystem();
119
120
2.68k
        try
121
2.68k
        {
122
2.68k
            block_index.resize( block_count );
123
2.68k
        }
124
2.68k
        catch( const std::exception& ex )
125
2.68k
        {
126
0
            throw PCIDSKException("Out of memory allocating block_index(%u): %s",
127
0
                                  block_count, ex.what());
128
0
        }
129
2.68k
        if( block_count > 0 )
130
0
        {
131
0
            vs->ReadFromFile( &(block_index[0]),
132
0
                              offset_on_disk_within_section
133
0
                              + vs->vh.section_offsets[hsec_shape] + 8,
134
0
                              4 * block_count );
135
136
0
            if( needs_swap )
137
0
                SwapData( &(block_index[0]), 4, block_count );
138
0
        }
139
140
2.68k
        block_initialized = true;
141
2.68k
    }
142
143
29.0k
    return &block_index;
144
29.0k
}
145
146
/************************************************************************/
147
/*                               Flush()                                */
148
/************************************************************************/
149
150
void VecSegDataIndex::Flush()
151
152
20.2k
{
153
20.2k
    if( !dirty )
154
17.4k
        return;
155
156
2.79k
    GetIndex(); // force loading if not already loaded!
157
158
2.79k
    PCIDSKBuffer wbuf( SerializedSize() );
159
160
2.79k
    memcpy( wbuf.buffer + 0, &block_count, 4 );
161
2.79k
    memcpy( wbuf.buffer + 4, &bytes, 4 );
162
2.79k
    memcpy( wbuf.buffer + 8, &(block_index[0]), 4*block_count );
163
164
2.79k
    bool needs_swap = !BigEndianSystem();
165
166
2.79k
    if( needs_swap )
167
2.79k
        SwapData( wbuf.buffer, 4, block_count+2 );
168
169
    // Make sure this section of the header is large enough.
170
2.79k
    int32 shift = (int32) wbuf.buffer_size - (int32) size_on_disk;
171
172
2.79k
    if( shift != 0 )
173
2.67k
    {
174
2.67k
        uint32 old_section_size = vs->vh.section_sizes[hsec_shape];
175
176
//        fprintf( stderr, "Shifting section %d by %d bytes.\n",
177
//                 section, shift );
178
179
2.67k
        vs->vh.GrowSection( hsec_shape, old_section_size + shift );
180
181
2.67k
        if( section == sec_vert )
182
1.37k
        {
183
            // move record block index and shape index.
184
1.37k
            vs->MoveData( vs->vh.section_offsets[hsec_shape]
185
1.37k
                          + vs->di[sec_vert].size_on_disk,
186
1.37k
                          vs->vh.section_offsets[hsec_shape]
187
1.37k
                          + vs->di[sec_vert].size_on_disk + shift,
188
1.37k
                          old_section_size - size_on_disk );
189
1.37k
        }
190
1.30k
        else
191
1.30k
        {
192
            // only move shape index.
193
1.30k
            vs->MoveData( vs->vh.section_offsets[hsec_shape]
194
1.30k
                          + vs->di[sec_vert].size_on_disk
195
1.30k
                          + vs->di[sec_record].size_on_disk,
196
1.30k
                          vs->vh.section_offsets[hsec_shape]
197
1.30k
                          + vs->di[sec_vert].size_on_disk
198
1.30k
                          + vs->di[sec_record].size_on_disk
199
1.30k
                          + shift,
200
1.30k
                          old_section_size
201
1.30k
                          - vs->di[sec_vert].size_on_disk
202
1.30k
                          - vs->di[sec_record].size_on_disk );
203
1.30k
        }
204
205
2.67k
        if( section == sec_vert )
206
1.37k
            vs->di[sec_record].offset_on_disk_within_section += shift;
207
2.67k
    }
208
209
    // Actually write to disk.
210
2.79k
    vs->WriteToFile( wbuf.buffer,
211
2.79k
                     offset_on_disk_within_section
212
2.79k
                     + vs->vh.section_offsets[hsec_shape],
213
2.79k
                     wbuf.buffer_size );
214
215
2.79k
    size_on_disk = wbuf.buffer_size;
216
2.79k
    dirty = false;
217
2.79k
}
218
219
/************************************************************************/
220
/*                           GetSectionEnd()                            */
221
/************************************************************************/
222
223
uint32 VecSegDataIndex::GetSectionEnd()
224
225
2.61M
{
226
2.61M
    return bytes;
227
2.61M
}
228
229
/************************************************************************/
230
/*                           SetSectionEnd()                            */
231
/************************************************************************/
232
233
void VecSegDataIndex::SetSectionEnd( uint32 new_end )
234
235
1.30M
{
236
    // should we keep track of the need to write this back to disk?
237
1.30M
    bytes = new_end;
238
1.30M
}
239
240
/************************************************************************/
241
/*                          AddBlockToIndex()                           */
242
/************************************************************************/
243
244
void VecSegDataIndex::AddBlockToIndex( uint32 block )
245
246
5.08k
{
247
5.08k
    GetIndex(); // force loading.
248
249
5.08k
    block_index.push_back( block );
250
5.08k
    block_count++;
251
5.08k
    dirty = true;
252
5.08k
}
253
254
/************************************************************************/
255
/*                              SetDirty()                              */
256
/*                                                                      */
257
/*      This method is primarily used to mark the need to write the     */
258
/*      index when the location changes.                                */
259
/************************************************************************/
260
261
void VecSegDataIndex::SetDirty()
262
263
0
{
264
0
    dirty = true;
265
0
}
266
267
/************************************************************************/
268
/*                          VacateBlockRange()                          */
269
/*                                                                      */
270
/*      Move any blocks in the indicated block range to the end of      */
271
/*      the segment to make space for a growing header.                 */
272
/************************************************************************/
273
274
void VecSegDataIndex::VacateBlockRange( uint32 start, uint32 count )
275
276
1.22k
{
277
1.22k
    GetIndex(); // make sure loaded.
278
279
1.22k
    unsigned int i;
280
1.22k
    uint32  next_block = (uint32) (vs->GetContentSize() / block_page_size);
281
282
12.3k
    for( i = 0; i < block_count; i++ )
283
11.1k
    {
284
11.1k
        if( block_index[i] >= start && block_index[i] < start+count )
285
756
        {
286
756
            vs->MoveData( block_index[i] * block_page_size,
287
756
                          next_block * block_page_size,
288
756
                          block_page_size );
289
756
            block_index[i] = next_block;
290
756
            dirty = true;
291
756
            next_block++;
292
756
        }
293
11.1k
    }
294
1.22k
}