Coverage Report

Created: 2025-08-11 09:23

/src/gdal/frmts/pcidsk/sdk/segment/vecsegdataindex.cpp
Line
Count
Source (jump to first uncovered line)
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
3.75k
{
40
3.75k
    block_initialized = false;
41
3.75k
    vs = nullptr;
42
3.75k
    dirty = false;
43
3.75k
    section = 0;
44
3.75k
    offset_on_disk_within_section = 0;
45
3.75k
    size_on_disk = 0;
46
3.75k
    block_count = 0;
47
3.75k
    bytes = 0;
48
3.75k
}
49
50
/************************************************************************/
51
/*                          ~VecSegDataIndex()                          */
52
/************************************************************************/
53
54
VecSegDataIndex::~VecSegDataIndex()
55
56
3.75k
{
57
3.75k
}
58
59
/************************************************************************/
60
/*                             Initialize()                             */
61
/************************************************************************/
62
63
void VecSegDataIndex::Initialize( CPCIDSKVectorSegment *vsIn, int sectionIn )
64
65
3.60k
{
66
3.60k
    this->section = sectionIn;
67
3.60k
    this->vs = vsIn;
68
69
3.60k
    if( section == sec_vert )
70
1.80k
        offset_on_disk_within_section = 0;
71
1.80k
    else
72
1.80k
        offset_on_disk_within_section = vs->di[sec_vert].SerializedSize();
73
74
3.60k
    uint32 offset = offset_on_disk_within_section
75
3.60k
        + vs->vh.section_offsets[hsec_shape];
76
77
3.60k
    memcpy( &block_count, vs->GetData(sec_raw,offset,nullptr,4), 4);
78
3.60k
    memcpy( &bytes, vs->GetData(sec_raw,offset+4,nullptr,4), 4);
79
80
3.60k
    bool needs_swap = !BigEndianSystem();
81
82
3.60k
    if( needs_swap )
83
3.60k
    {
84
3.60k
        SwapData( &block_count, 4, 1 );
85
3.60k
        SwapData( &bytes, 4, 1 );
86
3.60k
    }
87
88
3.60k
    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
3.60k
    size_on_disk = block_count * 4 + 8;
94
3.60k
}
95
96
/************************************************************************/
97
/*                           SerializedSize()                           */
98
/************************************************************************/
99
100
uint32 VecSegDataIndex::SerializedSize()
101
102
6.88k
{
103
6.88k
    return 8 + 4 * block_count;
104
6.88k
}
105
106
/************************************************************************/
107
/*                           GetBlockIndex()                            */
108
/************************************************************************/
109
110
const std::vector<uint32> *VecSegDataIndex::GetIndex()
111
112
26.3k
{
113
/* -------------------------------------------------------------------- */
114
/*      Load block map if needed.                                       */
115
/* -------------------------------------------------------------------- */
116
26.3k
    if( !block_initialized )
117
1.35k
    {
118
1.35k
        bool needs_swap = !BigEndianSystem();
119
120
1.35k
        try
121
1.35k
        {
122
1.35k
            block_index.resize( block_count );
123
1.35k
        }
124
1.35k
        catch( const std::exception& ex )
125
1.35k
        {
126
0
            throw PCIDSKException("Out of memory allocating block_index(%u): %s",
127
0
                                  block_count, ex.what());
128
0
        }
129
1.35k
        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
1.35k
        block_initialized = true;
141
1.35k
    }
142
143
26.3k
    return &block_index;
144
26.3k
}
145
146
/************************************************************************/
147
/*                               Flush()                                */
148
/************************************************************************/
149
150
void VecSegDataIndex::Flush()
151
152
8.45k
{
153
8.45k
    if( !dirty )
154
6.97k
        return;
155
156
1.47k
    GetIndex(); // force loading if not already loaded!
157
158
1.47k
    PCIDSKBuffer wbuf( SerializedSize() );
159
160
1.47k
    memcpy( wbuf.buffer + 0, &block_count, 4 );
161
1.47k
    memcpy( wbuf.buffer + 4, &bytes, 4 );
162
1.47k
    memcpy( wbuf.buffer + 8, &(block_index[0]), 4*block_count );
163
164
1.47k
    bool needs_swap = !BigEndianSystem();
165
166
1.47k
    if( needs_swap )
167
1.47k
        SwapData( wbuf.buffer, 4, block_count+2 );
168
169
    // Make sure this section of the header is large enough.
170
1.47k
    int32 shift = (int32) wbuf.buffer_size - (int32) size_on_disk;
171
172
1.47k
    if( shift != 0 )
173
1.35k
    {
174
1.35k
        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
1.35k
        vs->vh.GrowSection( hsec_shape, old_section_size + shift );
180
181
1.35k
        if( section == sec_vert )
182
677
        {
183
            // move record block index and shape index.
184
677
            vs->MoveData( vs->vh.section_offsets[hsec_shape]
185
677
                          + vs->di[sec_vert].size_on_disk,
186
677
                          vs->vh.section_offsets[hsec_shape]
187
677
                          + vs->di[sec_vert].size_on_disk + shift,
188
677
                          old_section_size - size_on_disk );
189
677
        }
190
674
        else
191
674
        {
192
            // only move shape index.
193
674
            vs->MoveData( vs->vh.section_offsets[hsec_shape]
194
674
                          + vs->di[sec_vert].size_on_disk
195
674
                          + vs->di[sec_record].size_on_disk,
196
674
                          vs->vh.section_offsets[hsec_shape]
197
674
                          + vs->di[sec_vert].size_on_disk
198
674
                          + vs->di[sec_record].size_on_disk
199
674
                          + shift,
200
674
                          old_section_size
201
674
                          - vs->di[sec_vert].size_on_disk
202
674
                          - vs->di[sec_record].size_on_disk );
203
674
        }
204
205
1.35k
        if( section == sec_vert )
206
677
            vs->di[sec_record].offset_on_disk_within_section += shift;
207
1.35k
    }
208
209
    // Actually write to disk.
210
1.47k
    vs->WriteToFile( wbuf.buffer,
211
1.47k
                     offset_on_disk_within_section
212
1.47k
                     + vs->vh.section_offsets[hsec_shape],
213
1.47k
                     wbuf.buffer_size );
214
215
1.47k
    size_on_disk = wbuf.buffer_size;
216
1.47k
    dirty = false;
217
1.47k
}
218
219
/************************************************************************/
220
/*                           GetSectionEnd()                            */
221
/************************************************************************/
222
223
uint32 VecSegDataIndex::GetSectionEnd()
224
225
3.47M
{
226
3.47M
    return bytes;
227
3.47M
}
228
229
/************************************************************************/
230
/*                           SetSectionEnd()                            */
231
/************************************************************************/
232
233
void VecSegDataIndex::SetSectionEnd( uint32 new_end )
234
235
1.73M
{
236
    // should we keep track of the need to write this back to disk?
237
1.73M
    bytes = new_end;
238
1.73M
}
239
240
/************************************************************************/
241
/*                          AddBlockToIndex()                           */
242
/************************************************************************/
243
244
void VecSegDataIndex::AddBlockToIndex( uint32 block )
245
246
5.04k
{
247
5.04k
    GetIndex(); // force loading.
248
249
5.04k
    block_index.push_back( block );
250
5.04k
    block_count++;
251
5.04k
    dirty = true;
252
5.04k
}
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.70k
{
277
1.70k
    GetIndex(); // make sure loaded.
278
279
1.70k
    unsigned int i;
280
1.70k
    uint32  next_block = (uint32) (vs->GetContentSize() / block_page_size);
281
282
20.7k
    for( i = 0; i < block_count; i++ )
283
19.0k
    {
284
19.0k
        if( block_index[i] >= start && block_index[i] < start+count )
285
1.14k
        {
286
1.14k
            vs->MoveData( block_index[i] * block_page_size,
287
1.14k
                          next_block * block_page_size,
288
1.14k
                          block_page_size );
289
1.14k
            block_index[i] = next_block;
290
1.14k
            dirty = true;
291
1.14k
            next_block++;
292
1.14k
        }
293
19.0k
    }
294
1.70k
}