Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of the VecSegHeader class.
4 : *
5 : * This class is used to manage reading and writing of the vector segment
6 : * header section, growing them as needed. It is exclusively a private
7 : * helper class for the CPCIDSKVectorSegment.
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2010
11 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : #include "pcidsk.h"
17 : #include "core/pcidsk_utils.h"
18 : #include "segment/vecsegheader.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 : /* VecSegHeader() */
29 : /************************************************************************/
30 :
31 1185 : VecSegHeader::VecSegHeader()
32 :
33 : {
34 1185 : vs = nullptr;
35 1185 : initialized = false;
36 1185 : needs_swap = !BigEndianSystem();
37 1185 : header_blocks = 0;
38 1185 : }
39 :
40 : /************************************************************************/
41 : /* ~VecSegHeader() */
42 : /************************************************************************/
43 :
44 1185 : VecSegHeader::~VecSegHeader()
45 :
46 : {
47 1185 : }
48 :
49 : /************************************************************************/
50 : /* InitializeNew() */
51 : /* */
52 : /* Initialize the header of a new vector segment in a */
53 : /* consistent state for an empty segment. */
54 : /************************************************************************/
55 :
56 1088 : void VecSegHeader::InitializeNew()
57 :
58 : {
59 2176 : PCIDSKBuffer header( 8 * 1024 );
60 : uint32 ivalue, hoffset;
61 :
62 1088 : memset( header.buffer, 0, header.buffer_size );
63 :
64 : // magic cookie
65 1088 : ivalue = 0xffffffff;
66 1088 : memcpy( header.buffer + 0, &ivalue, 4 );
67 1088 : memcpy( header.buffer + 4, &ivalue, 4 );
68 :
69 1088 : ivalue = 21;
70 1088 : memcpy( header.buffer + 8, &ivalue, 4 );
71 1088 : ivalue = 4;
72 1088 : memcpy( header.buffer + 12, &ivalue, 4 );
73 1088 : ivalue = 19;
74 1088 : memcpy( header.buffer + 16, &ivalue, 4 );
75 1088 : ivalue = 69;
76 1088 : memcpy( header.buffer + 20, &ivalue, 4 );
77 1088 : ivalue = 1;
78 1088 : memcpy( header.buffer + 24, &ivalue, 4 );
79 :
80 : // blocks in header.
81 1088 : ivalue = 1;
82 1088 : memcpy( header.buffer + 68, &ivalue, 4 );
83 :
84 : // offset to Projection
85 1088 : hoffset = 88;
86 1088 : memcpy( header.buffer + 72, &hoffset, 4 );
87 :
88 : // Project segment
89 : double dvalue;
90 1088 : dvalue = 0.0;
91 1088 : memcpy( header.buffer + hoffset, &dvalue, 8 );
92 1088 : memcpy( header.buffer + hoffset+8, &dvalue, 8 );
93 1088 : dvalue = 1.0;
94 1088 : memcpy( header.buffer + hoffset+16, &dvalue, 8 );
95 1088 : memcpy( header.buffer + hoffset+24, &dvalue, 8 );
96 1088 : if( needs_swap )
97 1088 : SwapData( header.buffer + hoffset, 8, 4 );
98 1088 : hoffset += 33;
99 :
100 : // offset to RST
101 1088 : memcpy( header.buffer + 76, &hoffset, 4 );
102 :
103 : // RST - two zeros means no rst + empty string.
104 1088 : hoffset += 9;
105 :
106 : // offset to Records
107 1088 : memcpy( header.buffer + 80, &hoffset, 4 );
108 :
109 : // Records - zeros means no fields.
110 1088 : hoffset += 4;
111 :
112 : // offset to Shapes
113 1088 : memcpy( header.buffer + 84, &hoffset, 4 );
114 :
115 : // Shapes - zero means no shapes.
116 1088 : hoffset += 4;
117 :
118 1088 : if( needs_swap )
119 1088 : SwapData( header.buffer, 4, 22 );
120 :
121 1088 : vs->WriteToFile( header.buffer, 0, header.buffer_size );
122 1088 : }
123 :
124 : /************************************************************************/
125 : /* InitializeExisting() */
126 : /* */
127 : /* Establish the location and sizes of the various header */
128 : /* sections. */
129 : /************************************************************************/
130 :
131 1183 : void VecSegHeader::InitializeExisting()
132 :
133 : {
134 1183 : if( initialized )
135 0 : return;
136 :
137 1183 : initialized = true;
138 :
139 : /* -------------------------------------------------------------------- */
140 : /* Check fixed portion of the header to ensure this is a V6 */
141 : /* style vector segment. */
142 : /* -------------------------------------------------------------------- */
143 : static const unsigned char magic[24] =
144 : { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
145 : 0, 0, 0, 21, 0, 0, 0, 4, 0, 0, 0, 19, 0, 0, 0, 69 };
146 :
147 1183 : if( memcmp( vs->GetData( sec_raw, 0, nullptr, 24 ), magic, 24 ) != 0 )
148 : {
149 0 : return ThrowPCIDSKException( "Unexpected vector header values, possibly it is not a V6 vector segment?" );
150 : }
151 :
152 : /* -------------------------------------------------------------------- */
153 : /* Establish how big the header is currently. */
154 : /* -------------------------------------------------------------------- */
155 1183 : memcpy( &header_blocks, vs->GetData( sec_raw, 68, nullptr, 4 ), 4 );
156 1183 : if( needs_swap )
157 1183 : SwapData( &header_blocks, 4, 1 );
158 :
159 : /* -------------------------------------------------------------------- */
160 : /* Load section offsets. */
161 : /* -------------------------------------------------------------------- */
162 1183 : memcpy( section_offsets, vs->GetData( sec_raw, 72, nullptr, 16 ), 16 );
163 1183 : if( needs_swap )
164 1183 : SwapData( section_offsets, 4, 4 );
165 :
166 : /* -------------------------------------------------------------------- */
167 : /* Determine the size of the projection section. */
168 : /* -------------------------------------------------------------------- */
169 1183 : ShapeField work_value;
170 1183 : uint32 next_off = section_offsets[hsec_proj];
171 :
172 1183 : next_off += 32; // xoff/yoff/xsize/ysize values.
173 :
174 1183 : next_off = vs->ReadField( next_off, work_value, FieldTypeString, sec_raw );
175 1183 : section_sizes[hsec_proj] = next_off - section_offsets[hsec_proj];
176 :
177 : /* -------------------------------------------------------------------- */
178 : /* Determine the size of the RST. */
179 : /* -------------------------------------------------------------------- */
180 : // yikes, not too sure! for now assume it is empty.
181 1183 : section_sizes[hsec_rst] = 8;
182 :
183 : /* -------------------------------------------------------------------- */
184 : /* Load the field definitions. */
185 : /* -------------------------------------------------------------------- */
186 : int field_count, i;
187 :
188 1183 : next_off = section_offsets[hsec_record];
189 :
190 1183 : next_off = vs->ReadField( next_off, work_value, FieldTypeInteger, sec_raw );
191 1183 : field_count = work_value.GetValueInteger();
192 :
193 1333 : for( i = 0; i < field_count; i++ )
194 : {
195 150 : next_off = vs->ReadField( next_off, work_value, FieldTypeString, sec_raw );
196 150 : field_names.push_back( work_value.GetValueString() );
197 :
198 150 : next_off = vs->ReadField( next_off, work_value, FieldTypeString, sec_raw );
199 150 : field_descriptions.push_back( work_value.GetValueString() );
200 :
201 150 : next_off = vs->ReadField( next_off, work_value, FieldTypeInteger, sec_raw );
202 150 : int field_type = work_value.GetValueInteger();
203 150 : if( field_type < 0 || field_type > FieldTypeCountedInt )
204 0 : return ThrowPCIDSKException( "Invalid field type: %d", field_type );
205 150 : field_types.push_back( static_cast<ShapeFieldType> (field_type) );
206 :
207 150 : next_off = vs->ReadField( next_off, work_value, FieldTypeString, sec_raw );
208 150 : field_formats.push_back( work_value.GetValueString() );
209 :
210 150 : next_off = vs->ReadField( next_off, work_value, field_types[i], sec_raw );
211 150 : field_defaults.push_back( work_value );
212 : }
213 :
214 1183 : section_sizes[hsec_record] = next_off - section_offsets[hsec_record];
215 :
216 : /* -------------------------------------------------------------------- */
217 : /* Fetch the vertex block basics. */
218 : /* -------------------------------------------------------------------- */
219 1183 : next_off = section_offsets[hsec_shape];
220 :
221 1183 : vs->di[sec_vert].Initialize( vs, sec_vert );
222 1183 : next_off += vs->di[sec_vert].SerializedSize();
223 :
224 : /* -------------------------------------------------------------------- */
225 : /* Fetch the record block basics. */
226 : /* -------------------------------------------------------------------- */
227 1183 : vs->di[sec_record].Initialize( vs, sec_record );
228 1183 : next_off += vs->di[sec_record].SerializedSize();
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* Fetch the shapeid basics. */
232 : /* -------------------------------------------------------------------- */
233 1183 : memcpy( &(vs->total_shape_count), vs->GetData(sec_raw,next_off,nullptr,4), 4);
234 1183 : if( needs_swap )
235 1183 : SwapData(&(vs->total_shape_count), 4, 1);
236 1183 : if( vs->total_shape_count < 0 )
237 0 : return ThrowPCIDSKException( "Invalid shape_count: %d", vs->total_shape_count );
238 :
239 1183 : next_off += 4;
240 1183 : vs->shape_index_start = 0;
241 :
242 1183 : uint64 section_size = next_off - section_offsets[hsec_shape]
243 1183 : + static_cast<uint64>(vs->total_shape_count) * 12;
244 1183 : if( section_size > std::numeric_limits<uint32>::max() )
245 0 : return ThrowPCIDSKException( "Invalid section_size" );
246 :
247 1183 : section_sizes[hsec_shape] = static_cast<uint32>(section_size);
248 : }
249 :
250 : /************************************************************************/
251 : /* WriteFieldDefinitions() */
252 : /************************************************************************/
253 :
254 35 : void VecSegHeader::WriteFieldDefinitions()
255 :
256 : {
257 70 : PCIDSKBuffer hbuf( 1000 );
258 35 : uint32 offset = 0, i;
259 35 : ShapeField wrkfield;
260 :
261 35 : wrkfield.SetValue( (int32) field_names.size() );
262 35 : offset = vs->WriteField( offset, wrkfield, hbuf );
263 :
264 138 : for( i = 0; i < field_names.size(); i++ )
265 : {
266 103 : wrkfield.SetValue( field_names[i] );
267 103 : offset = vs->WriteField( offset, wrkfield, hbuf );
268 :
269 103 : wrkfield.SetValue( field_descriptions[i] );
270 103 : offset = vs->WriteField( offset, wrkfield, hbuf );
271 :
272 103 : wrkfield.SetValue( (int32) field_types[i] );
273 103 : offset = vs->WriteField( offset, wrkfield, hbuf );
274 :
275 103 : wrkfield.SetValue( field_formats[i] );
276 103 : offset = vs->WriteField( offset, wrkfield, hbuf );
277 :
278 103 : offset = vs->WriteField( offset, field_defaults[i], hbuf );
279 : }
280 :
281 35 : hbuf.SetSize( offset );
282 :
283 35 : GrowSection( hsec_record, hbuf.buffer_size );
284 35 : vs->WriteToFile( hbuf.buffer, section_offsets[hsec_record],
285 35 : hbuf.buffer_size );
286 :
287 : // invalidate the raw buffer.
288 35 : vs->raw_loaded_data.buffer_size = 0;
289 35 : }
290 :
291 : /************************************************************************/
292 : /* GrowSection() */
293 : /* */
294 : /* If necessary grow/move the header section specified to have */
295 : /* the desired amount of room. Returns true if the header */
296 : /* section has moved. */
297 : /************************************************************************/
298 :
299 252 : bool VecSegHeader::GrowSection( int hsec, uint32 new_size )
300 :
301 : {
302 : /* -------------------------------------------------------------------- */
303 : /* Trivial case. */
304 : /* -------------------------------------------------------------------- */
305 252 : if( section_sizes[hsec] >= new_size )
306 : {
307 86 : section_sizes[hsec] = new_size;
308 86 : return false;
309 : }
310 :
311 : /* -------------------------------------------------------------------- */
312 : /* Can we grow the section in its current location without */
313 : /* overlapping anything else? */
314 : /* -------------------------------------------------------------------- */
315 : int ihsec;
316 166 : bool grow_ok = true;
317 166 : uint32 last_used = 0;
318 :
319 830 : for( ihsec = 0; ihsec < 4; ihsec++ )
320 : {
321 664 : if( ihsec == hsec )
322 166 : continue;
323 :
324 498 : if( section_offsets[ihsec] + section_sizes[ihsec] > last_used )
325 482 : last_used = section_offsets[ihsec] + section_sizes[ihsec];
326 :
327 904 : if( section_offsets[hsec] >=
328 498 : section_offsets[ihsec] + section_sizes[ihsec] )
329 406 : continue;
330 :
331 92 : if( section_offsets[ihsec] >= section_offsets[hsec] + new_size )
332 0 : continue;
333 :
334 : // apparent overlap
335 92 : grow_ok = false;
336 : }
337 :
338 : /* -------------------------------------------------------------------- */
339 : /* If we can grow in place and have space there is nothing to do. */
340 : /* -------------------------------------------------------------------- */
341 166 : if( grow_ok
342 86 : && section_offsets[hsec] + new_size
343 86 : < header_blocks * block_page_size )
344 : {
345 86 : section_sizes[hsec] = new_size;
346 86 : return false;
347 : }
348 :
349 : /* -------------------------------------------------------------------- */
350 : /* Where will the section be positioned after grow? It might */
351 : /* be nice to search for a big enough hole in the existing area */
352 : /* to fit the section. */
353 : /* -------------------------------------------------------------------- */
354 : uint32 new_base;
355 :
356 80 : if( grow_ok )
357 0 : new_base = section_offsets[hsec];
358 : else
359 80 : new_base = last_used;
360 :
361 : /* -------------------------------------------------------------------- */
362 : /* Does the header need to grow? */
363 : /* -------------------------------------------------------------------- */
364 80 : if( new_base + new_size > header_blocks * block_page_size )
365 : {
366 0 : GrowHeader( (new_base+new_size+block_page_size-1) / block_page_size
367 0 : - header_blocks );
368 : }
369 :
370 : /* -------------------------------------------------------------------- */
371 : /* Move the old section to the new location. */
372 : /* -------------------------------------------------------------------- */
373 80 : bool actual_move = false;
374 :
375 80 : if( new_base != section_offsets[hsec] )
376 : {
377 80 : vs->MoveData( section_offsets[hsec], new_base, section_sizes[hsec] );
378 80 : actual_move = true;
379 : }
380 :
381 80 : section_sizes[hsec] = new_size;
382 80 : section_offsets[hsec] = new_base;
383 :
384 : /* -------------------------------------------------------------------- */
385 : /* Update the section offsets list. */
386 : /* -------------------------------------------------------------------- */
387 80 : if( actual_move )
388 : {
389 80 : uint32 new_offset = section_offsets[hsec];
390 80 : if( needs_swap )
391 80 : SwapData( &new_offset, 4, 1 );
392 80 : vs->WriteToFile( &new_offset, 72 + hsec * 4, 4 );
393 : }
394 :
395 80 : return true;
396 : }
397 :
398 : /************************************************************************/
399 : /* GrowBlockIndex() */
400 : /* */
401 : /* Allocate the requested number of additional blocks to the */
402 : /* data block index. */
403 : /************************************************************************/
404 :
405 80 : void VecSegHeader::GrowBlockIndex( int section, int new_blocks )
406 :
407 : {
408 80 : if( new_blocks == 0 )
409 0 : return;
410 :
411 80 : uint32 next_block = (uint32) (vs->GetContentSize() / block_page_size);
412 :
413 160 : while( new_blocks > 0 )
414 : {
415 80 : vs->di[section].AddBlockToIndex( next_block++ );
416 80 : new_blocks--;
417 : }
418 :
419 80 : if( GrowSection( hsec_shape, section_sizes[hsec_shape] + 4*new_blocks ) )
420 : {
421 0 : vs->di[sec_vert].SetDirty();
422 0 : vs->di[sec_record].SetDirty();
423 0 : vs->shape_index_page_dirty = true; // we need to rewrite at new location
424 : }
425 : }
426 :
427 : /************************************************************************/
428 : /* ShapeIndexPrepare() */
429 : /* */
430 : /* When CPCIDSKVectorSegment::FlushLoadedShapeIndex() needs to */
431 : /* write out all the shapeid's and offsets, it calls this */
432 : /* method to find the offset from the start of the segment at */
433 : /* which it should do the writing. */
434 : /* */
435 : /* We use this opportunity to flush out the vertex, and record */
436 : /* block offsets if necessary, and to grow the header if needed */
437 : /* to hold the proposed shapeindex size. The passed in size */
438 : /* is the size in bytes from "Number of Shapes" on in the */
439 : /* "Shape section" of the header. */
440 : /************************************************************************/
441 :
442 51 : uint32 VecSegHeader::ShapeIndexPrepare( uint32 size )
443 :
444 : {
445 51 : GrowSection( hsec_shape,
446 : size
447 51 : + vs->di[sec_vert].size_on_disk
448 51 : + vs->di[sec_record].size_on_disk );
449 :
450 51 : return section_offsets[hsec_shape]
451 51 : + vs->di[sec_vert].size_on_disk
452 51 : + vs->di[sec_record].size_on_disk;
453 : }
454 :
455 : /************************************************************************/
456 : /* GrowHeader() */
457 : /* */
458 : /* Grow the header by the requested number of blocks. This */
459 : /* will often involve migrating existing vector or record */
460 : /* section blocks on to make space since the header must be */
461 : /* contiguous. */
462 : /************************************************************************/
463 :
464 0 : void VecSegHeader::GrowHeader( uint32 new_blocks )
465 :
466 : {
467 : // fprintf( stderr, "GrowHeader(%d) to %d\n",
468 : // new_blocks, header_blocks + new_blocks );
469 :
470 : /* -------------------------------------------------------------------- */
471 : /* Process the two existing block maps, moving stuff on if */
472 : /* needed. */
473 : /* -------------------------------------------------------------------- */
474 0 : vs->di[sec_vert].VacateBlockRange( header_blocks, new_blocks );
475 0 : vs->di[sec_record].VacateBlockRange( header_blocks, new_blocks );
476 :
477 : /* -------------------------------------------------------------------- */
478 : /* Write to ensure the segment is the new size. */
479 : /* -------------------------------------------------------------------- */
480 0 : vs->WriteToFile( "\0", (header_blocks+new_blocks) * block_page_size - 1, 1);
481 :
482 : /* -------------------------------------------------------------------- */
483 : /* Update to new header size. */
484 : /* -------------------------------------------------------------------- */
485 0 : header_blocks += new_blocks;
486 :
487 0 : uint32 header_block_buf = header_blocks;
488 :
489 0 : if( needs_swap )
490 0 : SwapData( &header_block_buf, 4, 1 );
491 :
492 0 : vs->WriteToFile( &header_block_buf, 68, 4 );
493 0 : }
494 :
|