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 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included
22 * in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
31 ****************************************************************************/
32
33 #include "pcidsk.h"
34 #include "core/pcidsk_utils.h"
35 #include "segment/cpcidskvectorsegment.h"
36 #include <cassert>
37 #include <cstring>
38 #include <cstdio>
39 #include <limits>
40
41 using namespace PCIDSK;
42
43 /* -------------------------------------------------------------------- */
44 /* Size of a block in the record/vertex block tables. This is */
45 /* determined by the PCIDSK format and may not be changed. */
46 /* -------------------------------------------------------------------- */
47 static const int block_page_size = 8192;
48
49 /************************************************************************/
50 /* VecSegDataIndex() */
51 /************************************************************************/
52
VecSegDataIndex()53 VecSegDataIndex::VecSegDataIndex()
54
55 {
56 block_initialized = false;
57 vs = nullptr;
58 dirty = false;
59 section = 0;
60 offset_on_disk_within_section = 0;
61 size_on_disk = 0;
62 block_count = 0;
63 bytes = 0;
64 }
65
66 /************************************************************************/
67 /* ~VecSegDataIndex() */
68 /************************************************************************/
69
~VecSegDataIndex()70 VecSegDataIndex::~VecSegDataIndex()
71
72 {
73 }
74
75 /************************************************************************/
76 /* Initialize() */
77 /************************************************************************/
78
Initialize(CPCIDSKVectorSegment * vsIn,int sectionIn)79 void VecSegDataIndex::Initialize( CPCIDSKVectorSegment *vsIn, int sectionIn )
80
81 {
82 this->section = sectionIn;
83 this->vs = vsIn;
84
85 if( section == sec_vert )
86 offset_on_disk_within_section = 0;
87 else
88 offset_on_disk_within_section = vs->di[sec_vert].SerializedSize();
89
90 uint32 offset = offset_on_disk_within_section
91 + vs->vh.section_offsets[hsec_shape];
92
93 memcpy( &block_count, vs->GetData(sec_raw,offset,nullptr,4), 4);
94 memcpy( &bytes, vs->GetData(sec_raw,offset+4,nullptr,4), 4);
95
96 bool needs_swap = !BigEndianSystem();
97
98 if( needs_swap )
99 {
100 SwapData( &block_count, 4, 1 );
101 SwapData( &bytes, 4, 1 );
102 }
103
104 if( block_count > (std::numeric_limits<uint32>::max() - 8) /4 )
105 {
106 throw PCIDSKException("Invalid block_count: %u", block_count);
107 }
108
109 size_on_disk = block_count * 4 + 8;
110 }
111
112 /************************************************************************/
113 /* SerializedSize() */
114 /************************************************************************/
115
SerializedSize()116 uint32 VecSegDataIndex::SerializedSize()
117
118 {
119 return 8 + 4 * block_count;
120 }
121
122 /************************************************************************/
123 /* GetBlockIndex() */
124 /************************************************************************/
125
GetIndex()126 const std::vector<uint32> *VecSegDataIndex::GetIndex()
127
128 {
129 /* -------------------------------------------------------------------- */
130 /* Load block map if needed. */
131 /* -------------------------------------------------------------------- */
132 if( !block_initialized )
133 {
134 bool needs_swap = !BigEndianSystem();
135
136 try
137 {
138 block_index.resize( block_count );
139 }
140 catch( const std::exception& ex )
141 {
142 throw PCIDSKException("Out of memory allocating block_index(%u): %s",
143 block_count, ex.what());
144 }
145 if( block_count > 0 )
146 {
147 vs->ReadFromFile( &(block_index[0]),
148 offset_on_disk_within_section
149 + vs->vh.section_offsets[hsec_shape] + 8,
150 4 * block_count );
151
152 if( needs_swap )
153 SwapData( &(block_index[0]), 4, block_count );
154 }
155
156 block_initialized = true;
157 }
158
159 return &block_index;
160 }
161
162 /************************************************************************/
163 /* Flush() */
164 /************************************************************************/
165
Flush()166 void VecSegDataIndex::Flush()
167
168 {
169 if( !dirty )
170 return;
171
172 GetIndex(); // force loading if not already loaded!
173
174 PCIDSKBuffer wbuf( SerializedSize() );
175
176 memcpy( wbuf.buffer + 0, &block_count, 4 );
177 memcpy( wbuf.buffer + 4, &bytes, 4 );
178 memcpy( wbuf.buffer + 8, &(block_index[0]), 4*block_count );
179
180 bool needs_swap = !BigEndianSystem();
181
182 if( needs_swap )
183 SwapData( wbuf.buffer, 4, block_count+2 );
184
185 // Make sure this section of the header is large enough.
186 int32 shift = (int32) wbuf.buffer_size - (int32) size_on_disk;
187
188 if( shift != 0 )
189 {
190 uint32 old_section_size = vs->vh.section_sizes[hsec_shape];
191
192 // fprintf( stderr, "Shifting section %d by %d bytes.\n",
193 // section, shift );
194
195 vs->vh.GrowSection( hsec_shape, old_section_size + shift );
196
197 if( section == sec_vert )
198 {
199 // move record block index and shape index.
200 vs->MoveData( vs->vh.section_offsets[hsec_shape]
201 + vs->di[sec_vert].size_on_disk,
202 vs->vh.section_offsets[hsec_shape]
203 + vs->di[sec_vert].size_on_disk + shift,
204 old_section_size - size_on_disk );
205 }
206 else
207 {
208 // only move shape index.
209 vs->MoveData( vs->vh.section_offsets[hsec_shape]
210 + vs->di[sec_vert].size_on_disk
211 + vs->di[sec_record].size_on_disk,
212 vs->vh.section_offsets[hsec_shape]
213 + vs->di[sec_vert].size_on_disk
214 + vs->di[sec_record].size_on_disk
215 + shift,
216 old_section_size
217 - vs->di[sec_vert].size_on_disk
218 - vs->di[sec_record].size_on_disk );
219 }
220
221 if( section == sec_vert )
222 vs->di[sec_record].offset_on_disk_within_section += shift;
223 }
224
225 // Actually write to disk.
226 vs->WriteToFile( wbuf.buffer,
227 offset_on_disk_within_section
228 + vs->vh.section_offsets[hsec_shape],
229 wbuf.buffer_size );
230
231 size_on_disk = wbuf.buffer_size;
232 dirty = false;
233 }
234
235 /************************************************************************/
236 /* GetSectionEnd() */
237 /************************************************************************/
238
GetSectionEnd()239 uint32 VecSegDataIndex::GetSectionEnd()
240
241 {
242 return bytes;
243 }
244
245 /************************************************************************/
246 /* SetSectionEnd() */
247 /************************************************************************/
248
SetSectionEnd(uint32 new_end)249 void VecSegDataIndex::SetSectionEnd( uint32 new_end )
250
251 {
252 // should we keep track of the need to write this back to disk?
253 bytes = new_end;
254 }
255
256 /************************************************************************/
257 /* AddBlockToIndex() */
258 /************************************************************************/
259
AddBlockToIndex(uint32 block)260 void VecSegDataIndex::AddBlockToIndex( uint32 block )
261
262 {
263 GetIndex(); // force loading.
264
265 block_index.push_back( block );
266 block_count++;
267 dirty = true;
268 }
269
270 /************************************************************************/
271 /* SetDirty() */
272 /* */
273 /* This method is primarily used to mark the need to write the */
274 /* index when the location changes. */
275 /************************************************************************/
276
SetDirty()277 void VecSegDataIndex::SetDirty()
278
279 {
280 dirty = true;
281 }
282
283 /************************************************************************/
284 /* VacateBlockRange() */
285 /* */
286 /* Move any blocks in the indicated block range to the end of */
287 /* the segment to make space for a growing header. */
288 /************************************************************************/
289
VacateBlockRange(uint32 start,uint32 count)290 void VecSegDataIndex::VacateBlockRange( uint32 start, uint32 count )
291
292 {
293 GetIndex(); // make sure loaded.
294
295 unsigned int i;
296 uint32 next_block = (uint32) (vs->GetContentSize() / block_page_size);
297
298 for( i = 0; i < block_count; i++ )
299 {
300 if( block_index[i] >= start && block_index[i] < start+count )
301 {
302 vs->MoveData( block_index[i] * block_page_size,
303 next_block * block_page_size,
304 block_page_size );
305 block_index[i] = next_block;
306 dirty = true;
307 next_block++;
308 }
309 }
310 }
311