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