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