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