Coverage Report

Created: 2026-08-14 09:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/pcidsk/sdk/segment/cpcidskvectorsegment.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Purpose:  Implementation of the CPCIDSKVectorSegment class.
4
 *
5
 ******************************************************************************
6
 * Copyright (c) 2009
7
 * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8
 *
9
 * SPDX-License-Identifier: MIT
10
 ****************************************************************************/
11
12
#include "pcidsk_file.h"
13
#include "pcidsk_exception.h"
14
#include "core/pcidsk_utils.h"
15
#include "segment/cpcidskvectorsegment.h"
16
#include <cassert>
17
#include <cstring>
18
#include <cstdio>
19
#include <algorithm>
20
#include <limits>
21
22
using namespace PCIDSK;
23
24
/* -------------------------------------------------------------------- */
25
/*      Size of one page of loaded shapeids.  This is not related to    */
26
/*      the file format, and may be changed to alter the number of      */
27
/*      shapeid pointers kept in RAM at one time from the shape         */
28
/*      index.                                                          */
29
/* -------------------------------------------------------------------- */
30
static const int shapeid_page_size = 1024;
31
32
4.84k
PCIDSKVectorSegment::~PCIDSKVectorSegment() = default;
33
34
/************************************************************************/
35
/*                        CPCIDSKVectorSegment()                        */
36
/************************************************************************/
37
38
CPCIDSKVectorSegment::CPCIDSKVectorSegment( PCIDSKFile *fileIn, int segmentIn,
39
                                            const char *segment_pointer )
40
4.84k
        : CPCIDSKSegment( fileIn, segmentIn, segment_pointer )
41
42
4.84k
{
43
4.84k
    base_initialized = false;
44
4.84k
    needs_swap = false;
45
46
4.84k
    total_shape_count = 0;
47
4.84k
    valid_shape_count = 0;
48
49
4.84k
    last_shapes_id = NullShapeId;
50
4.84k
    last_shapes_index = -1;
51
52
4.84k
    raw_loaded_data_offset = 0;
53
4.84k
    vert_loaded_data_offset = 0;
54
4.84k
    record_loaded_data_offset = 0;
55
4.84k
    raw_loaded_data_dirty = false;
56
4.84k
    vert_loaded_data_dirty = false;
57
4.84k
    record_loaded_data_dirty = false;
58
59
4.84k
    shape_index_start = 0;
60
4.84k
    shape_index_page_dirty = false;
61
62
4.84k
    shapeid_map_active = false;
63
4.84k
    shapeid_pages_certainly_mapped = -1;
64
65
4.84k
    vh.vs = this;
66
67
4.84k
    highest_shapeid_used = NullShapeId;
68
4.84k
}
Unexecuted instantiation: PCIDSK::CPCIDSKVectorSegment::CPCIDSKVectorSegment(PCIDSK::PCIDSKFile*, int, char const*)
PCIDSK::CPCIDSKVectorSegment::CPCIDSKVectorSegment(PCIDSK::PCIDSKFile*, int, char const*)
Line
Count
Source
40
4.84k
        : CPCIDSKSegment( fileIn, segmentIn, segment_pointer )
41
42
4.84k
{
43
4.84k
    base_initialized = false;
44
4.84k
    needs_swap = false;
45
46
4.84k
    total_shape_count = 0;
47
4.84k
    valid_shape_count = 0;
48
49
4.84k
    last_shapes_id = NullShapeId;
50
4.84k
    last_shapes_index = -1;
51
52
4.84k
    raw_loaded_data_offset = 0;
53
4.84k
    vert_loaded_data_offset = 0;
54
4.84k
    record_loaded_data_offset = 0;
55
4.84k
    raw_loaded_data_dirty = false;
56
4.84k
    vert_loaded_data_dirty = false;
57
4.84k
    record_loaded_data_dirty = false;
58
59
4.84k
    shape_index_start = 0;
60
4.84k
    shape_index_page_dirty = false;
61
62
4.84k
    shapeid_map_active = false;
63
4.84k
    shapeid_pages_certainly_mapped = -1;
64
65
4.84k
    vh.vs = this;
66
67
4.84k
    highest_shapeid_used = NullShapeId;
68
4.84k
}
69
70
/************************************************************************/
71
/*                       ~CPCIDSKVectorSegment()                        */
72
/************************************************************************/
73
74
CPCIDSKVectorSegment::~CPCIDSKVectorSegment()
75
76
4.84k
{
77
4.84k
    try
78
4.84k
    {
79
4.84k
        Synchronize();
80
4.84k
    }
81
4.84k
    catch( const PCIDSKException& e )
82
4.84k
    {
83
0
        fprintf(stderr, "Exception in ~CPCIDSKVectorSegment(): %s", e.what()); // ok
84
0
    }
85
4.84k
}
86
87
/************************************************************************/
88
/*                            Synchronize()                             */
89
/************************************************************************/
90
91
void CPCIDSKVectorSegment::Synchronize()
92
11.0k
{
93
11.0k
    if( base_initialized )
94
11.0k
    {
95
11.0k
        FlushSegHeaderIfNeeded();
96
97
11.0k
        FlushDataBuffer( sec_vert );
98
11.0k
        FlushDataBuffer( sec_record );
99
100
11.0k
        di[sec_vert].Flush();
101
11.0k
        di[sec_record].Flush();
102
103
11.0k
        FlushLoadedShapeIndex();
104
105
11.0k
        if( GetHeader().GetInt( 192, 16 ) != total_shape_count
106
1.50k
            && file->GetUpdatable() )
107
1.50k
        {
108
1.50k
            GetHeader().Put( total_shape_count, 192, 16 );
109
1.50k
            FlushHeader();
110
1.50k
        }
111
11.0k
    }
112
11.0k
}
113
114
/************************************************************************/
115
/*                             Initialize()                             */
116
/*                                                                      */
117
/*      Initialize the header of a new vector segment in a              */
118
/*      consistent state for an empty segment.                          */
119
/************************************************************************/
120
121
void CPCIDSKVectorSegment::Initialize()
122
123
2.06k
{
124
2.06k
    needs_swap = !BigEndianSystem();
125
126
/* -------------------------------------------------------------------- */
127
/*      Initialize the header that occurs within the regular segment    */
128
/*      data.                                                           */
129
/* -------------------------------------------------------------------- */
130
2.06k
    vh.InitializeNew();
131
132
/* -------------------------------------------------------------------- */
133
/*      Initialize the values in the generic segment header.            */
134
/* -------------------------------------------------------------------- */
135
2.06k
    PCIDSKBuffer &head = GetHeader();
136
137
2.06k
    head.Put( "METRE", 160, 16 );
138
2.06k
    head.Put( 1.0, 176, 16 );
139
2.06k
    head.Put( 0, 192, 16 );
140
2.06k
    head.Put( 0, 208, 16 );
141
2.06k
    head.Put( 0, 224, 16 );
142
2.06k
    head.Put( "", 240, 16 );
143
2.06k
    head.Put( 0, 256, 16 );
144
2.06k
    head.Put( 0, 272, 16 );
145
146
#ifdef PCIMAJORVERSION
147
    PCIDSK::ShapeField oFieldsDefault;
148
    oFieldsDefault.SetValue(std::vector<int32>{});
149
150
    // Add the RingStart field, because it can't be added after
151
    // shapes have been added. This is a bug that should be properly fixed
152
    AddField(ATT_RINGSTART,
153
             PCIDSK::FieldTypeCountedInt,
154
             "", "",
155
             &oFieldsDefault);
156
#endif
157
158
2.06k
    FlushHeader();
159
2.06k
}
160
161
/************************************************************************/
162
/*                             LoadHeader()                             */
163
/*                                                                      */
164
/*      Initialize minimum information from the vector segment          */
165
/*      header.  We defer this till an actual vector related action     */
166
/*      is taken.                                                       */
167
/************************************************************************/
168
169
void CPCIDSKVectorSegment::LoadHeader()
170
171
19.9M
{
172
19.9M
    if( base_initialized )
173
19.9M
        return;
174
175
4.84k
    base_initialized = true;
176
177
4.84k
    needs_swap = !BigEndianSystem();
178
179
4.84k
    vh.InitializeExisting();
180
181
    // When the IDB code deletes a shape, it simply writes a -1
182
    // into the index. We need to know how many actual valid shapes
183
    // there are in the segment, so count them
184
4.84k
    valid_shape_count = 0;
185
4.84k
    ShapeId iShape = FindFirst();
186
965k
    while (iShape != NullShapeId)
187
960k
    {
188
960k
        ++valid_shape_count;
189
960k
        iShape = FindNext(iShape);
190
960k
    }
191
4.84k
}
192
193
/************************************************************************/
194
/*                             ReadField()                              */
195
/*                                                                      */
196
/*      Read a value from the indicated offset in a section of the      */
197
/*      vector segment, and place the value into a ShapeField           */
198
/*      structure based on the passed in field type.                    */
199
/************************************************************************/
200
201
uint32 CPCIDSKVectorSegment::ReadField( uint32 offset, ShapeField& field,
202
                                        ShapeFieldType field_type,
203
                                        int section )
204
205
485k
{
206
485k
    switch( field_type )
207
485k
    {
208
98.9k
      case FieldTypeInteger:
209
98.9k
      {
210
98.9k
          int32 value;
211
98.9k
          memcpy( &value, GetData( section, offset, nullptr, 4), 4 );
212
98.9k
          if( needs_swap )
213
98.9k
              SwapData( &value, 4, 1 );
214
98.9k
          field.SetValue( value );
215
98.9k
          return offset + 4;
216
0
      }
217
218
0
      case FieldTypeFloat:
219
0
      {
220
0
          float value;
221
0
          memcpy( &value, GetData( section, offset, nullptr, 4), 4 );
222
0
          if( needs_swap )
223
0
              SwapData( &value, 4, 1 );
224
0
          field.SetValue( value );
225
0
          return offset + 4;
226
0
      }
227
228
62
      case FieldTypeDouble:
229
62
      {
230
62
          double value;
231
62
          memcpy( &value, GetData( section, offset, nullptr, 8), 8 );
232
62
          if( needs_swap )
233
62
              SwapData( &value, 8, 1 );
234
62
          field.SetValue( value );
235
62
          return offset + 8;
236
0
      }
237
238
386k
      case FieldTypeString:
239
386k
      {
240
386k
          int available;
241
386k
          char *srcdata = GetData( section, offset, &available, 1 );
242
243
          // Simple case -- all initially available.
244
386k
          int string_len = 0;
245
246
1.52M
          while( srcdata[string_len] != '\0' && available - string_len > 0 )
247
1.13M
              string_len++;
248
249
386k
          if( string_len < available && srcdata[string_len] == '\0' )
250
386k
          {
251
386k
              std::string value( srcdata, string_len );
252
386k
              field.SetValue( value );
253
386k
              return offset + string_len + 1;
254
386k
          }
255
256
82
          std::string value;
257
258
2.16k
          while( *srcdata != '\0' )
259
2.08k
          {
260
2.08k
              value += *(srcdata++);
261
2.08k
              offset++;
262
2.08k
              available--;
263
2.08k
              if( available == 0 )
264
82
                  srcdata = GetData( section, offset, &available, 1 );
265
2.08k
          }
266
267
82
          field.SetValue( value );
268
82
          return offset+1;
269
386k
      }
270
271
0
      case FieldTypeCountedInt:
272
0
      {
273
0
          std::vector<int32> value;
274
0
          int32 count;
275
0
          char *srcdata = GetData( section, offset, nullptr, 4 );
276
0
          memcpy( &count, srcdata, 4 );
277
0
          if( needs_swap )
278
0
              SwapData( &count, 4, 1 );
279
280
0
          value.resize( count );
281
0
          if( count > 0 )
282
0
          {
283
0
              if( offset > std::numeric_limits<uint32>::max() - 8 )
284
0
                  return ThrowPCIDSKException(0, "Invalid offset = %u", offset);
285
0
              memcpy( &(value[0]), GetData(section,offset+4,nullptr,4*count), 4*count );
286
0
              if( needs_swap )
287
0
                  SwapData( &(value[0]), 4, count );
288
0
          }
289
290
0
          field.SetValue( value );
291
0
          return offset + 4 + 4*count;
292
0
      }
293
294
0
      default:
295
0
        return ThrowPCIDSKException(0, "Unhandled field type %d", field_type);
296
485k
    }
297
485k
}
298
299
/************************************************************************/
300
/*                             WriteField()                             */
301
/*                                                                      */
302
/*      Write a field value into a buffer, growing the buffer if        */
303
/*      needed to hold the value.                                       */
304
/************************************************************************/
305
306
uint32 CPCIDSKVectorSegment::WriteField( uint32 offset,
307
                                         const ShapeField& field,
308
                                         PCIDSKBuffer& buffer )
309
310
3.77M
{
311
/* -------------------------------------------------------------------- */
312
/*      How much space do we need for this value?                       */
313
/* -------------------------------------------------------------------- */
314
3.77M
    uint32 item_size = 0;
315
316
3.77M
    switch( field.GetType() )
317
3.77M
    {
318
62.3k
      case FieldTypeInteger:
319
62.3k
        item_size = 4;
320
62.3k
        break;
321
322
0
      case FieldTypeFloat:
323
0
        item_size = 4;
324
0
        break;
325
326
35.8k
      case FieldTypeDouble:
327
35.8k
        item_size = 8;
328
35.8k
        break;
329
330
3.68M
      case FieldTypeString:
331
3.68M
        item_size = static_cast<uint32>(field.GetValueString().size()) + 1;
332
3.68M
        break;
333
334
0
      case FieldTypeCountedInt:
335
0
        item_size = static_cast<uint32>(field.GetValueCountedInt().size()) * 4 + 4;
336
0
        break;
337
338
0
      default:
339
0
        assert( 0 );
340
0
        item_size = 0;
341
0
        break;
342
3.77M
    }
343
344
/* -------------------------------------------------------------------- */
345
/*      Do we need to grow the buffer to hold this?  Try to make it     */
346
/*      plenty larger.                                                  */
347
/* -------------------------------------------------------------------- */
348
3.77M
    if( item_size + offset > (uint32) buffer.buffer_size )
349
815k
        buffer.SetSize( buffer.buffer_size*2 + item_size );
350
351
/* -------------------------------------------------------------------- */
352
/*      Write to the buffer, and byte swap if needed.                   */
353
/* -------------------------------------------------------------------- */
354
3.77M
    switch( field.GetType() )
355
3.77M
    {
356
62.3k
      case FieldTypeInteger:
357
62.3k
      {
358
62.3k
          int32 value = field.GetValueInteger();
359
62.3k
          if( needs_swap )
360
62.3k
              SwapData( &value, 4, 1 );
361
62.3k
          memcpy( buffer.buffer+offset, &value, 4 );
362
62.3k
          break;
363
0
      }
364
365
0
      case FieldTypeFloat:
366
0
      {
367
0
          float value = field.GetValueFloat();
368
0
          if( needs_swap )
369
0
              SwapData( &value, 4, 1 );
370
0
          memcpy( buffer.buffer+offset, &value, 4 );
371
0
          break;
372
0
      }
373
374
35.8k
      case FieldTypeDouble:
375
35.8k
      {
376
35.8k
          double value = field.GetValueDouble();
377
35.8k
          if( needs_swap )
378
35.8k
              SwapData( &value, 8, 1 );
379
35.8k
          memcpy( buffer.buffer+offset, &value, 8 );
380
35.8k
          break;
381
0
      }
382
383
3.68M
      case FieldTypeString:
384
3.68M
      {
385
3.68M
          std::string value = field.GetValueString();
386
3.68M
          memcpy( buffer.buffer+offset, value.c_str(), item_size );
387
3.68M
          break;
388
0
      }
389
390
0
      case FieldTypeCountedInt:
391
0
      {
392
0
          std::vector<int32> value = field.GetValueCountedInt();
393
0
          uint32 count = static_cast<uint32>(value.size());
394
0
          memcpy( buffer.buffer+offset, &count, 4 );
395
0
          if( count > 0 )
396
0
          {
397
0
              memcpy( buffer.buffer+offset+4, &(value[0]), count * 4 );
398
0
              if( needs_swap )
399
0
                  SwapData( buffer.buffer+offset, 4, count+1 );
400
0
          }
401
0
          break;
402
0
      }
403
404
0
      default:
405
0
        assert( 0 );
406
0
        break;
407
3.77M
    }
408
409
3.77M
    return offset + item_size;
410
3.77M
}
411
412
/************************************************************************/
413
/*                              GetData()                               */
414
/************************************************************************/
415
416
char *CPCIDSKVectorSegment::GetData( int section, uint32 offset,
417
                                     int *bytes_available, int min_bytes,
418
                                     bool update )
419
420
1.89M
{
421
1.89M
    if( min_bytes == 0 )
422
0
        min_bytes = 1;
423
424
/* -------------------------------------------------------------------- */
425
/*      Select the section to act on.                                   */
426
/* -------------------------------------------------------------------- */
427
1.89M
    PCIDSKBuffer *pbuf = nullptr;
428
1.89M
    uint32       *pbuf_offset = nullptr;
429
1.89M
    bool         *pbuf_dirty = nullptr;
430
431
1.89M
    if( section == sec_raw )
432
523k
    {
433
523k
        pbuf = &raw_loaded_data;
434
523k
        pbuf_offset = &raw_loaded_data_offset;
435
523k
        pbuf_dirty = &raw_loaded_data_dirty;
436
523k
    }
437
1.36M
    else if( section == sec_vert )
438
685k
    {
439
685k
        pbuf = &vert_loaded_data;
440
685k
        pbuf_offset = &vert_loaded_data_offset;
441
685k
        pbuf_dirty = &vert_loaded_data_dirty;
442
685k
    }
443
681k
    else if( section == sec_record )
444
681k
    {
445
681k
        pbuf = &record_loaded_data;
446
681k
        pbuf_offset = &record_loaded_data_offset;
447
681k
        pbuf_dirty = &record_loaded_data_dirty;
448
681k
    }
449
0
    else
450
0
    {
451
0
        return (char*)ThrowPCIDSKExceptionPtr("Unexpected case");
452
0
    }
453
454
1.89M
    if( offset > std::numeric_limits<uint32>::max() - static_cast<uint32>(min_bytes) )
455
0
        return (char*)ThrowPCIDSKExceptionPtr("Invalid offset : %u", offset);
456
457
/* -------------------------------------------------------------------- */
458
/*      If the desired data is not within our loaded section, reload    */
459
/*      one or more blocks around the request.                          */
460
/* -------------------------------------------------------------------- */
461
1.89M
    if( offset < *pbuf_offset
462
1.89M
        || offset+static_cast<uint32>(min_bytes) > *pbuf_offset + static_cast<uint32>(pbuf->buffer_size) )
463
10.4k
    {
464
10.4k
        if( *pbuf_dirty )
465
2.47k
            FlushDataBuffer( section );
466
467
        // we want whole 8K blocks around the target region.
468
10.4k
        uint32 load_offset = offset - (offset % block_page_size);
469
10.4k
        int size = (offset + static_cast<uint32>(min_bytes) - load_offset + block_page_size - 1);
470
471
10.4k
        size -= (size % block_page_size);
472
473
        // If the request goes beyond the end of the file, and we are
474
        // in update mode, grow the segment by writing at the end of
475
        // the requested section.  This will throw an exception if we
476
        // are unable to grow the file.
477
10.4k
        if( section != sec_raw
478
5.41k
            && load_offset + size > di[section].GetIndex()->size() * block_page_size
479
5.41k
            && update )
480
5.41k
        {
481
5.41k
            PCIDSKBuffer zerobuf(block_page_size);
482
483
5.41k
            memset( zerobuf.buffer, 0, block_page_size );
484
5.41k
            WriteSecToFile( section, zerobuf.buffer,
485
5.41k
                            (load_offset + size) / block_page_size - 1, 1 );
486
5.41k
        }
487
488
10.4k
        *pbuf_offset = load_offset;
489
10.4k
        pbuf->SetSize( size );
490
491
10.4k
        ReadSecFromFile( section, pbuf->buffer,
492
10.4k
                         load_offset / block_page_size, size / block_page_size );
493
10.4k
    }
494
495
/* -------------------------------------------------------------------- */
496
/*      If an update request goes beyond the end of the last data       */
497
/*      byte in a data section, then update the bytes used.  Now        */
498
/*      read into our buffer.                                           */
499
/* -------------------------------------------------------------------- */
500
1.89M
    if( section != sec_raw
501
1.36M
        && offset + min_bytes > di[section].GetSectionEnd() )
502
1.36M
        di[section].SetSectionEnd( offset + min_bytes );
503
504
/* -------------------------------------------------------------------- */
505
/*      Return desired info.                                            */
506
/* -------------------------------------------------------------------- */
507
1.89M
    if( bytes_available != nullptr )
508
386k
        *bytes_available = *pbuf_offset + pbuf->buffer_size - offset;
509
510
1.89M
    if( update )
511
1.36M
        *pbuf_dirty = true;
512
513
1.89M
    return pbuf->buffer + offset - *pbuf_offset;
514
1.89M
}
515
516
/************************************************************************/
517
/*                          ReadSecFromFile()                           */
518
/*                                                                      */
519
/*      Read one or more blocks from the desired "section" of the       */
520
/*      segment data, going through the block pointer map for           */
521
/*      vect/record sections.                                           */
522
/************************************************************************/
523
524
void CPCIDSKVectorSegment::ReadSecFromFile( int section, char *buffer,
525
                                            int block_offset,
526
                                            int block_count )
527
528
10.4k
{
529
/* -------------------------------------------------------------------- */
530
/*      Raw is a simple case, directly gulp.                            */
531
/* -------------------------------------------------------------------- */
532
10.4k
    if( section == sec_raw )
533
5.04k
    {
534
5.04k
        ReadFromFile( buffer, static_cast<uint64>(block_offset)*static_cast<uint32>(block_page_size),
535
5.04k
                      block_count*block_page_size );
536
5.04k
        return;
537
5.04k
    }
538
539
/* -------------------------------------------------------------------- */
540
/*      Process one 8K block at a time in case they are discontiguous   */
541
/*      which they often are.                                           */
542
/* -------------------------------------------------------------------- */
543
5.41k
    int i;
544
5.41k
    const std::vector<uint32> *block_map = di[section].GetIndex();
545
546
5.41k
    if(  block_count + block_offset > (int) block_map->size() )
547
0
    {
548
0
        return ThrowPCIDSKException("Assertion failed: block_count(=%d) + block_offset(=%d) <= block_map->size()(=%d)",
549
0
                                    block_count, block_offset, (int) block_map->size() );
550
0
    }
551
552
12.8k
    for( i = 0; i < block_count; i++ )
553
7.44k
    {
554
7.44k
        ReadFromFile( buffer + i * block_page_size,
555
7.44k
                      block_page_size * static_cast<uint64>((*block_map)[block_offset+i]),
556
7.44k
                      block_page_size );
557
7.44k
    }
558
5.41k
}
559
560
/************************************************************************/
561
/*                          FlushDataBuffer()                           */
562
/*                                                                      */
563
/*      Flush the indicated data buffer to disk if it is marked         */
564
/*      dirty.                                                          */
565
/************************************************************************/
566
567
void CPCIDSKVectorSegment::FlushDataBuffer( int section )
568
569
24.5k
{
570
/* -------------------------------------------------------------------- */
571
/*      Select the section to act on.                                   */
572
/* -------------------------------------------------------------------- */
573
24.5k
    PCIDSKBuffer *pbuf = nullptr;
574
24.5k
    uint32       *pbuf_offset = nullptr;
575
24.5k
    bool         *pbuf_dirty = nullptr;
576
577
24.5k
    if( section == sec_raw )
578
0
    {
579
0
        pbuf = &raw_loaded_data;
580
0
        pbuf_offset = &raw_loaded_data_offset;
581
0
        pbuf_dirty = &raw_loaded_data_dirty;
582
0
    }
583
24.5k
    else if( section == sec_vert )
584
11.5k
    {
585
11.5k
        pbuf = &vert_loaded_data;
586
11.5k
        pbuf_offset = &vert_loaded_data_offset;
587
11.5k
        pbuf_dirty = &vert_loaded_data_dirty;
588
11.5k
    }
589
13.0k
    else if( section == sec_record )
590
13.0k
    {
591
13.0k
        pbuf = &record_loaded_data;
592
13.0k
        pbuf_offset = &record_loaded_data_offset;
593
13.0k
        pbuf_dirty = &record_loaded_data_dirty;
594
13.0k
    }
595
0
    else
596
0
    {
597
0
        return ThrowPCIDSKException("Unexpected case");
598
0
    }
599
600
24.5k
    if( ! *pbuf_dirty || pbuf->buffer_size == 0 )
601
19.1k
        return;
602
603
/* -------------------------------------------------------------------- */
604
/*      We need to write something.                                     */
605
/* -------------------------------------------------------------------- */
606
24.5k
    assert( (pbuf->buffer_size % block_page_size) == 0 );
607
5.41k
    assert( (*pbuf_offset % block_page_size) == 0 );
608
609
5.41k
    WriteSecToFile( section, pbuf->buffer,
610
5.41k
                    *pbuf_offset / block_page_size,
611
5.41k
                    pbuf->buffer_size / block_page_size );
612
613
5.41k
    *pbuf_dirty = false;
614
5.41k
}
615
616
/************************************************************************/
617
/*                           WriteSecToFile()                           */
618
/*                                                                      */
619
/*      Read one or more blocks from the desired "section" of the       */
620
/*      segment data, going through the block pointer map for           */
621
/*      vect/record sections.                                           */
622
/************************************************************************/
623
624
void CPCIDSKVectorSegment::WriteSecToFile( int section, char *buffer,
625
                                           int block_offset,
626
                                           int block_count )
627
628
10.8k
{
629
/* -------------------------------------------------------------------- */
630
/*      Raw is a simple case, directly gulp.                            */
631
/* -------------------------------------------------------------------- */
632
10.8k
    if( section == sec_raw )
633
0
    {
634
0
        WriteToFile( buffer, block_offset*block_page_size,
635
0
                     block_count*block_page_size );
636
0
        return;
637
0
    }
638
639
/* -------------------------------------------------------------------- */
640
/*      Do we need to grow this data section to be able to do the       */
641
/*      write?                                                          */
642
/* -------------------------------------------------------------------- */
643
10.8k
    const std::vector<uint32> *block_map = di[section].GetIndex();
644
645
10.8k
    if( block_count + block_offset > (int) block_map->size() )
646
5.41k
    {
647
5.41k
        vh.GrowBlockIndex( section,
648
5.41k
                           block_count + block_offset - static_cast<int>(block_map->size()) );
649
5.41k
    }
650
651
/* -------------------------------------------------------------------- */
652
/*      Process one 8K block at a time in case they are discontiguous   */
653
/*      which they often are.                                           */
654
/* -------------------------------------------------------------------- */
655
10.8k
    int i;
656
23.6k
    for( i = 0; i < block_count; i++ )
657
12.8k
    {
658
12.8k
        WriteToFile( buffer + i * block_page_size,
659
12.8k
                     block_page_size * (*block_map)[block_offset+i],
660
12.8k
                     block_page_size );
661
12.8k
    }
662
10.8k
}
663
664
/************************************************************************/
665
/*                           GetProjection()                            */
666
/************************************************************************/
667
668
std::vector<double> CPCIDSKVectorSegment::GetProjection( std::string &geosys )
669
670
4.77k
{
671
4.77k
    LoadHeader();
672
673
/* -------------------------------------------------------------------- */
674
/*      Fetch the projparms string from the proj section of the         */
675
/*      vector segment header.                                          */
676
/* -------------------------------------------------------------------- */
677
4.77k
    ShapeField projparms;
678
679
4.77k
    ReadField( vh.section_offsets[hsec_proj]+32, projparms,
680
4.77k
               FieldTypeString, sec_raw );
681
682
/* -------------------------------------------------------------------- */
683
/*      Read the geosys (units) string from SDH5.VEC1 in the segment    */
684
/*      header.                                                         */
685
/* -------------------------------------------------------------------- */
686
4.77k
    GetHeader().Get( 160, 16, geosys, 0 ); // do not unpad!
687
688
4.77k
    return ProjParamsFromText( geosys, projparms.GetValueString() );
689
4.77k
}
690
691
/************************************************************************/
692
/*                           SetProjection()                            */
693
/************************************************************************/
694
695
void CPCIDSKVectorSegment::SetProjection( const std::string& geosys,
696
                                          const std::vector<double>& params )
697
698
298
{
699
298
    LoadHeader();
700
701
/* -------------------------------------------------------------------- */
702
/*      Apply parameters in the vector segment "proj" header section.   */
703
/* -------------------------------------------------------------------- */
704
298
    PCIDSKBuffer proj(32);
705
298
    uint32       proj_size;
706
298
    ShapeField   value;
707
708
298
    value.SetValue( ProjParamsToText( params ) );
709
710
298
    ReadFromFile( proj.buffer, vh.section_offsets[hsec_proj], 32 );
711
298
    proj_size = WriteField( 32, value, proj );
712
713
298
    vh.GrowSection( hsec_proj, proj_size );
714
298
    WriteToFile( proj.buffer, vh.section_offsets[hsec_proj], proj_size );
715
716
/* -------------------------------------------------------------------- */
717
/*      Write the geosys string to the generic segment header.          */
718
/* -------------------------------------------------------------------- */
719
298
    GetHeader().Put( geosys.c_str(), 160, 16 );
720
298
    FlushHeader();
721
298
}
722
723
/************************************************************************/
724
/*                          IndexFromShapeId()                          */
725
/*                                                                      */
726
/*      Translate a shapeid into a shape index.  Several mechanisms     */
727
/*      are used to accelerate this when possible.                      */
728
/************************************************************************/
729
730
int CPCIDSKVectorSegment::IndexFromShapeId( ShapeId id )
731
732
2.32M
{
733
2.32M
    if( id == NullShapeId )
734
0
        return -1;
735
736
2.32M
    LoadHeader();
737
738
/* -------------------------------------------------------------------- */
739
/*      Does this match our last lookup?                                */
740
/* -------------------------------------------------------------------- */
741
2.32M
    if( id == last_shapes_id )
742
1.56M
        return last_shapes_index;
743
744
/* -------------------------------------------------------------------- */
745
/*      Is this the next shapeid in sequence, and is it in our          */
746
/*      loaded index cache?                                             */
747
/* -------------------------------------------------------------------- */
748
758k
    if( id == last_shapes_id + 1
749
613k
        && last_shapes_index + 1 >= shape_index_start
750
613k
        && last_shapes_index + 1 < shape_index_start + (int) shape_index_ids.size() )
751
613k
    {
752
613k
        last_shapes_index++;
753
613k
        last_shapes_id++;
754
613k
        return last_shapes_index;
755
613k
    }
756
757
/* -------------------------------------------------------------------- */
758
/*      Activate the shapeid map, if it is not already active.          */
759
/* -------------------------------------------------------------------- */
760
145k
    if( !shapeid_map_active )
761
147
    {
762
147
        PopulateShapeIdMap();
763
147
    }
764
765
/* -------------------------------------------------------------------- */
766
/*      Is this already in our shapeid map?                             */
767
/* -------------------------------------------------------------------- */
768
145k
    if( shapeid_map.count( id ) == 1 )
769
145k
        return shapeid_map[id];
770
771
0
    return -1;
772
145k
}
773
774
/************************************************************************/
775
/*                          LoadShapeIdPage()                           */
776
/************************************************************************/
777
778
void CPCIDSKVectorSegment::LoadShapeIdPage( int page )
779
780
3.18k
{
781
/* -------------------------------------------------------------------- */
782
/*      Load a chunk of shape index information into a                  */
783
/*      PCIDSKBuffer.                                                   */
784
/* -------------------------------------------------------------------- */
785
3.18k
    uint32 shape_index_byte_offset =
786
3.18k
        vh.section_offsets[hsec_shape]
787
3.18k
        + di[sec_record].offset_on_disk_within_section
788
3.18k
        + di[sec_record].size_on_disk + 4;
789
790
3.18k
    int entries_to_load = shapeid_page_size;
791
792
3.18k
    shape_index_start = page * shapeid_page_size;
793
3.18k
    if( shape_index_start + entries_to_load > total_shape_count )
794
2.48k
        entries_to_load = total_shape_count - shape_index_start;
795
796
3.18k
    PCIDSKBuffer wrk_index;
797
3.18k
    if( entries_to_load < 0 || entries_to_load > std::numeric_limits<int>::max() / 12 )
798
0
        return ThrowPCIDSKException("Invalid entries_to_load = %d", entries_to_load);
799
3.18k
    wrk_index.SetSize( entries_to_load * 12 );
800
801
3.18k
    ReadFromFile( wrk_index.buffer,
802
3.18k
                  shape_index_byte_offset + static_cast<uint64>(shape_index_start)*12,
803
3.18k
                  wrk_index.buffer_size );
804
805
/* -------------------------------------------------------------------- */
806
/*      Parse into the vectors for easier use.                          */
807
/* -------------------------------------------------------------------- */
808
3.18k
    int i;
809
810
3.18k
    shape_index_ids.resize( entries_to_load );
811
3.18k
    shape_index_vertex_off.resize( entries_to_load );
812
3.18k
    shape_index_record_off.resize( entries_to_load );
813
814
963k
    for( i = 0; i < entries_to_load; i++ )
815
960k
    {
816
960k
        memcpy( &(shape_index_ids[i]), wrk_index.buffer + i*12, 4 );
817
960k
        memcpy( &(shape_index_vertex_off[i]), wrk_index.buffer + i*12+4, 4 );
818
960k
        memcpy( &(shape_index_record_off[i]), wrk_index.buffer + i*12+8, 4 );
819
960k
    }
820
821
3.18k
    if( needs_swap && entries_to_load > 0 )
822
2.71k
    {
823
2.71k
        SwapData( &(shape_index_ids[0]), 4, entries_to_load );
824
2.71k
        SwapData( &(shape_index_vertex_off[0]), 4, entries_to_load );
825
2.71k
        SwapData( &(shape_index_record_off[0]), 4, entries_to_load );
826
2.71k
    }
827
828
3.18k
    PushLoadedIndexIntoMap();
829
3.18k
}
830
831
/************************************************************************/
832
/*                         AccessShapeByIndex()                         */
833
/*                                                                      */
834
/*      This method is responsible for loading the set of               */
835
/*      information for shape "shape_index" into the shape_index data   */
836
/*      structures if it is not already there.                          */
837
/************************************************************************/
838
839
void CPCIDSKVectorSegment::AccessShapeByIndex( int shape_index )
840
841
3.01M
{
842
3.01M
    LoadHeader();
843
844
/* -------------------------------------------------------------------- */
845
/*      Is the requested index already loaded?                          */
846
/* -------------------------------------------------------------------- */
847
3.01M
    if( shape_index >= shape_index_start
848
3.01M
        && shape_index < shape_index_start + (int) shape_index_ids.size() )
849
2.32M
        return;
850
851
    // this is for requesting the next shapeindex after shapecount on
852
    // a partial page.
853
688k
    if( shape_index == total_shape_count
854
685k
        && (int) shape_index_ids.size() < shapeid_page_size
855
685k
        && total_shape_count == (int) shape_index_ids.size() + shape_index_start )
856
685k
        return;
857
858
/* -------------------------------------------------------------------- */
859
/*      If the currently loaded shapeindex is dirty, we should write    */
860
/*      it now.                                                         */
861
/* -------------------------------------------------------------------- */
862
3.18k
    FlushLoadedShapeIndex();
863
864
/* -------------------------------------------------------------------- */
865
/*      Load the page of shapeid information for this shape index.      */
866
/* -------------------------------------------------------------------- */
867
3.18k
    LoadShapeIdPage( shape_index / shapeid_page_size );
868
3.18k
}
869
870
/************************************************************************/
871
/*                       PushLoadedIndexIntoMap()                       */
872
/************************************************************************/
873
874
void CPCIDSKVectorSegment::PushLoadedIndexIntoMap()
875
876
3.33k
{
877
/* -------------------------------------------------------------------- */
878
/*      If the shapeid map is active, apply the current pages           */
879
/*      shapeids if it does not already appear to have been             */
880
/*      applied.                                                        */
881
/* -------------------------------------------------------------------- */
882
3.33k
    int loaded_page = shape_index_start / shapeid_page_size;
883
884
3.33k
    if( shapeid_map_active && !shape_index_ids.empty() )
885
147
    {
886
147
        unsigned int i;
887
888
294
        for( i = 0; i < shape_index_ids.size(); i++ )
889
147
        {
890
147
            if( shape_index_ids[i] != NullShapeId )
891
147
                shapeid_map[shape_index_ids[i]] = i+shape_index_start;
892
147
        }
893
894
147
        if( loaded_page == shapeid_pages_certainly_mapped+1 )
895
147
            shapeid_pages_certainly_mapped++;
896
147
    }
897
3.33k
}
898
899
/************************************************************************/
900
/*                         PopulateShapeIdMap()                         */
901
/*                                                                      */
902
/*      Completely populate the shapeid->index map.                     */
903
/************************************************************************/
904
905
void CPCIDSKVectorSegment::PopulateShapeIdMap()
906
907
147
{
908
/* -------------------------------------------------------------------- */
909
/*      Enable shapeid_map mode, and load the current page.             */
910
/* -------------------------------------------------------------------- */
911
147
    if( !shapeid_map_active )
912
147
    {
913
147
        shapeid_map_active = true;
914
147
        PushLoadedIndexIntoMap();
915
147
    }
916
917
/* -------------------------------------------------------------------- */
918
/*      Load all outstanding pages.                                     */
919
/* -------------------------------------------------------------------- */
920
147
    int shapeid_pages = DIV_ROUND_UP(total_shape_count, shapeid_page_size);
921
922
147
    while( shapeid_pages_certainly_mapped+1 < shapeid_pages )
923
0
    {
924
0
        LoadShapeIdPage( shapeid_pages_certainly_mapped+1 );
925
0
    }
926
147
}
927
928
/************************************************************************/
929
/*                        FindNextValidByIndex()                        */
930
/************************************************************************/
931
/**
932
  * Find the next shape and the given shape index in the segment
933
  * (including deleted shapes), if the shape at nIndex is NullShapeId then
934
  * return the nexrt valid shape ID
935
  *
936
  * @param nIndex the index into
937
  */
938
ShapeId CPCIDSKVectorSegment::FindNextValidByIndex(int nIndex)
939
965k
{
940
965k
    LoadHeader();
941
942
965k
    if (total_shape_count == 0 || nIndex >= total_shape_count)
943
4.84k
        return NullShapeId;
944
945
946
960k
    for (int nShapeIndex = nIndex; nShapeIndex < total_shape_count; ++nShapeIndex)
947
960k
    {
948
        // set up shape_index_ids array
949
960k
        AccessShapeByIndex(nShapeIndex);
950
951
960k
        int32 nNextShapeId = shape_index_ids[nShapeIndex - shape_index_start];
952
960k
        if (nNextShapeId != NullShapeId)
953
960k
        {
954
960k
            last_shapes_id = nNextShapeId;
955
960k
            last_shapes_index = nShapeIndex;
956
960k
            return last_shapes_id;
957
960k
        }
958
960k
    }
959
960
0
    return NullShapeId;
961
960k
}
962
963
/************************************************************************/
964
/*                             FindFirst()                              */
965
/************************************************************************/
966
967
ShapeId CPCIDSKVectorSegment::FindFirst()
968
4.84k
{
969
4.84k
    return FindNextValidByIndex(0);
970
4.84k
}
971
972
/************************************************************************/
973
/*                              FindNext()                              */
974
/************************************************************************/
975
976
ShapeId CPCIDSKVectorSegment::FindNext( ShapeId previous_id )
977
960k
{
978
960k
    if( previous_id == NullShapeId )
979
0
        return FindFirst();
980
981
960k
    int previous_index = IndexFromShapeId( previous_id );
982
983
960k
    return FindNextValidByIndex(previous_index+1);
984
960k
}
985
986
/************************************************************************/
987
/*                           GetShapeCount()                            */
988
/************************************************************************/
989
990
int CPCIDSKVectorSegment::GetShapeCount()
991
992
0
{
993
0
    LoadHeader();
994
995
0
    return valid_shape_count;
996
0
}
997
998
/************************************************************************/
999
/*                            GetVertices()                             */
1000
/************************************************************************/
1001
1002
void CPCIDSKVectorSegment::GetVertices( ShapeId shape_id,
1003
                                        std::vector<ShapeVertex> &vertices )
1004
1005
0
{
1006
0
    int shape_index = IndexFromShapeId( shape_id );
1007
1008
0
    if( shape_index == -1 )
1009
0
        return ThrowPCIDSKException( "Attempt to call GetVertices() on non-existing shape id '%d'.",
1010
0
                              (int) shape_id );
1011
1012
0
    AccessShapeByIndex( shape_index );
1013
1014
0
    uint32 vert_off = shape_index_vertex_off[shape_index - shape_index_start];
1015
0
    uint32 vertex_count;
1016
1017
0
    if( vert_off == 0xffffffff )
1018
0
    {
1019
0
        vertices.resize(0);
1020
0
        return;
1021
0
    }
1022
1023
0
    if( vert_off > std::numeric_limits<uint32>::max() - 4 )
1024
0
        return ThrowPCIDSKException( "Invalid vert_off = %u", vert_off);
1025
0
    memcpy( &vertex_count, GetData( sec_vert, vert_off+4, nullptr, 4 ), 4 );
1026
0
    if( needs_swap )
1027
0
        SwapData( &vertex_count, 4, 1 );
1028
1029
0
    try
1030
0
    {
1031
0
        vertices.resize( vertex_count );
1032
0
    }
1033
0
    catch( const std::exception& ex )
1034
0
    {
1035
0
        return ThrowPCIDSKException("Out of memory allocating vertices(%u): %s",
1036
0
                                    vertex_count, ex.what());
1037
0
    }
1038
1039
    // We ought to change this to process the available data and
1040
    // then request more.
1041
0
    if( vertex_count > 0 )
1042
0
    {
1043
0
        if( vert_off > std::numeric_limits<uint32>::max() - 8 )
1044
0
            return ThrowPCIDSKException( "Invalid vert_off = %u", vert_off);
1045
0
        memcpy( &(vertices[0]),
1046
0
                GetData( sec_vert, vert_off+8, nullptr, vertex_count*24),
1047
0
                vertex_count * 24 );
1048
0
        if( needs_swap )
1049
0
            SwapData( &(vertices[0]), 8, vertex_count*3 );
1050
0
    }
1051
0
}
1052
1053
/************************************************************************/
1054
/*                           GetFieldCount()                            */
1055
/************************************************************************/
1056
1057
int CPCIDSKVectorSegment::GetFieldCount()
1058
1059
5.67M
{
1060
5.67M
    LoadHeader();
1061
1062
5.67M
    return static_cast<int>(vh.field_names.size());
1063
5.67M
}
1064
1065
/************************************************************************/
1066
/*                            GetFieldName()                            */
1067
/************************************************************************/
1068
1069
std::string CPCIDSKVectorSegment::GetFieldName( int field_index )
1070
1071
3.61M
{
1072
3.61M
    LoadHeader();
1073
1074
3.61M
    return vh.field_names[field_index];
1075
3.61M
}
1076
1077
/************************************************************************/
1078
/*                        GetFieldDescription()                         */
1079
/************************************************************************/
1080
1081
std::string CPCIDSKVectorSegment::GetFieldDescription( int field_index )
1082
1083
0
{
1084
0
    LoadHeader();
1085
1086
0
    return vh.field_descriptions[field_index];
1087
0
}
1088
1089
/************************************************************************/
1090
/*                            GetFieldType()                            */
1091
/************************************************************************/
1092
1093
ShapeFieldType CPCIDSKVectorSegment::GetFieldType( int field_index )
1094
1095
3.61M
{
1096
3.61M
    LoadHeader();
1097
1098
3.61M
    return vh.field_types[field_index];
1099
3.61M
}
1100
1101
/************************************************************************/
1102
/*                           GetFieldFormat()                           */
1103
/************************************************************************/
1104
1105
std::string CPCIDSKVectorSegment::GetFieldFormat( int field_index )
1106
1107
0
{
1108
0
    LoadHeader();
1109
1110
0
    return vh.field_formats[field_index];
1111
0
}
1112
1113
/************************************************************************/
1114
/*                          GetFieldDefault()                           */
1115
/************************************************************************/
1116
1117
ShapeField CPCIDSKVectorSegment::GetFieldDefault( int field_index )
1118
1119
0
{
1120
0
    LoadHeader();
1121
1122
0
    return vh.field_defaults[field_index];
1123
0
}
1124
1125
/************************************************************************/
1126
/*                             GetFields()                              */
1127
/************************************************************************/
1128
1129
void CPCIDSKVectorSegment::GetFields( ShapeId id,
1130
                                      std::vector<ShapeField>& list )
1131
1132
0
{
1133
0
    unsigned int i;
1134
0
    int shape_index = IndexFromShapeId( id );
1135
1136
0
    if( shape_index == -1 )
1137
0
        return ThrowPCIDSKException( "Attempt to call GetFields() on non-existing shape id '%d'.",
1138
0
                              (int) id );
1139
1140
0
    AccessShapeByIndex( shape_index );
1141
1142
0
    uint32 offset = shape_index_record_off[shape_index - shape_index_start];
1143
1144
0
    list.resize(vh.field_names.size());
1145
1146
0
    if( offset == 0xffffffff )
1147
0
    {
1148
0
        for( i = 0; i < vh.field_names.size(); i++ )
1149
0
            list[i] = vh.field_defaults[i];
1150
0
    }
1151
0
    else
1152
0
    {
1153
0
        offset += 4; // skip size
1154
1155
0
        for( i = 0; i < vh.field_names.size(); i++ )
1156
0
            offset = ReadField( offset, list[i], vh.field_types[i], sec_record );
1157
0
    }
1158
0
}
1159
1160
/************************************************************************/
1161
/*                              AddField()                              */
1162
/************************************************************************/
1163
1164
void CPCIDSKVectorSegment::AddField( const std::string& name, ShapeFieldType type,
1165
                                     const std::string& description,
1166
                                     const std::string& format,
1167
                                     ShapeField *default_value )
1168
1169
52.1k
{
1170
52.1k
    ShapeField fallback_default;
1171
1172
52.1k
    LoadHeader();
1173
1174
/* -------------------------------------------------------------------- */
1175
/*      If we have existing features, we should go through adding       */
1176
/*      this new field.                                                 */
1177
/* -------------------------------------------------------------------- */
1178
52.1k
    if( total_shape_count > 0 )
1179
64
    {
1180
64
        return ThrowPCIDSKException( "Support for adding fields in populated layers "
1181
64
                              "has not yet been implemented." );
1182
64
    }
1183
1184
/* -------------------------------------------------------------------- */
1185
/*      If no default is provided, use the obvious value.               */
1186
/* -------------------------------------------------------------------- */
1187
52.0k
    if( default_value == nullptr )
1188
52.0k
    {
1189
52.0k
        switch( type )
1190
52.0k
        {
1191
0
          case FieldTypeFloat:
1192
0
            fallback_default.SetValue( (float) 0.0 );
1193
0
            break;
1194
63
          case FieldTypeDouble:
1195
63
            fallback_default.SetValue( (double) 0.0 );
1196
63
            break;
1197
42
          case FieldTypeInteger:
1198
42
            fallback_default.SetValue( (int32) 0 );
1199
42
            break;
1200
0
          case FieldTypeCountedInt:
1201
0
          {
1202
0
            std::vector<int32> empty_list;
1203
0
            fallback_default.SetValue( empty_list );
1204
0
            break;
1205
0
          }
1206
51.9k
          case FieldTypeString:
1207
51.9k
            fallback_default.SetValue( "" );
1208
51.9k
            break;
1209
1210
0
          case FieldTypeNone:
1211
0
            break;
1212
52.0k
        }
1213
1214
52.0k
        default_value = &fallback_default;
1215
52.0k
    }
1216
1217
/* -------------------------------------------------------------------- */
1218
/*      Make sure the default field is of the correct type.             */
1219
/* -------------------------------------------------------------------- */
1220
52.0k
    if( default_value->GetType() != type )
1221
0
    {
1222
0
        return ThrowPCIDSKException( "Attempt to add field with a default value of "
1223
0
                              "a different type than the field." );
1224
0
    }
1225
1226
52.0k
    if( type == FieldTypeNone )
1227
0
    {
1228
0
        return ThrowPCIDSKException( "Creating fields of type None not supported." );
1229
0
    }
1230
1231
/* -------------------------------------------------------------------- */
1232
/*      Add the field to the definition list.                           */
1233
/* -------------------------------------------------------------------- */
1234
52.0k
    vh.field_names.push_back( name );
1235
52.0k
    vh.field_types.push_back( type );
1236
52.0k
    vh.field_descriptions.push_back( description );
1237
52.0k
    vh.field_formats.push_back( format );
1238
52.0k
    vh.field_defaults.push_back( *default_value );
1239
1240
52.0k
    vh_dirty = true;
1241
52.0k
}
1242
1243
/************************************************************************/
1244
/*                       FlushSegHeaderIfNeeded()                       */
1245
/************************************************************************/
1246
1247
void CPCIDSKVectorSegment::FlushSegHeaderIfNeeded()
1248
2.06M
{
1249
2.06M
    if( vh_dirty )
1250
1.95k
    {
1251
1.95k
        vh.WriteFieldDefinitions();
1252
1.95k
        vh_dirty = false;
1253
1.95k
    }
1254
2.06M
}
1255
1256
/************************************************************************/
1257
/*                            CreateShape()                             */
1258
/************************************************************************/
1259
1260
ShapeId CPCIDSKVectorSegment::CreateShape( ShapeId id )
1261
1262
685k
{
1263
685k
    LoadHeader();
1264
685k
    FlushSegHeaderIfNeeded();
1265
1266
/* -------------------------------------------------------------------- */
1267
/*      Make sure we have the last shapeid index page loaded.           */
1268
/* -------------------------------------------------------------------- */
1269
685k
    AccessShapeByIndex( total_shape_count );
1270
1271
    // if highest_shapeid_used is unset, then look at all Ids
1272
685k
    if (highest_shapeid_used == NullShapeId &&!shape_index_ids.empty())
1273
0
    {
1274
0
        auto it = std::max_element(shape_index_ids.begin(), shape_index_ids.end());
1275
0
        highest_shapeid_used = *it;
1276
0
    }
1277
1278
/* -------------------------------------------------------------------- */
1279
/*      Do we need to assign a shapeid?                                 */
1280
/* -------------------------------------------------------------------- */
1281
685k
    if( id == NullShapeId )
1282
613k
    {
1283
613k
        if( highest_shapeid_used == NullShapeId )
1284
1.35k
            id = 0;
1285
611k
        else
1286
611k
            id = highest_shapeid_used + 1;
1287
613k
    }
1288
685k
    if( id > highest_shapeid_used )
1289
685k
        highest_shapeid_used = id;
1290
0
    else
1291
0
    {
1292
0
        PopulateShapeIdMap();
1293
0
        if( shapeid_map.count(id) > 0 )
1294
0
        {
1295
0
            return ThrowPCIDSKException( 0, "Attempt to create a shape with id '%d', but that already exists.", id );
1296
0
        }
1297
0
    }
1298
1299
/* -------------------------------------------------------------------- */
1300
/*      Push this new shape on to our list of shapeids in the           */
1301
/*      current page, and mark the page as dirty.                       */
1302
/* -------------------------------------------------------------------- */
1303
685k
    shape_index_ids.push_back( id );
1304
685k
    shape_index_record_off.push_back( 0xffffffff );
1305
685k
    shape_index_vertex_off.push_back( 0xffffffff );
1306
685k
    shape_index_page_dirty = true;
1307
1308
685k
    if( shapeid_map_active )
1309
72.5k
        shapeid_map[id] = total_shape_count;
1310
1311
685k
    total_shape_count++;
1312
685k
    valid_shape_count++;
1313
1314
685k
    return id;
1315
685k
}
1316
1317
/************************************************************************/
1318
/*                            DeleteShape()                             */
1319
/*                                                                      */
1320
/*      Delete a shape by shapeid.                                      */
1321
/************************************************************************/
1322
1323
void CPCIDSKVectorSegment::DeleteShape( ShapeId id )
1324
1325
0
{
1326
0
    FlushSegHeaderIfNeeded();
1327
0
    int shape_index = IndexFromShapeId( id );
1328
1329
0
    if( shape_index == -1 )
1330
0
        return ThrowPCIDSKException( "Attempt to call DeleteShape() on non-existing shape '%d'.",
1331
0
                              (int) id );
1332
1333
/* ==================================================================== */
1334
/*      Our strategy is to move the last shape in our index down to     */
1335
/*      replace the shape that we are deleting.  Unfortunately this     */
1336
/*      will result in an out of sequence shapeid, but it is hard to    */
1337
/*      avoid that without potentially rewriting much of the shape      */
1338
/*      index.                                                          */
1339
/*                                                                      */
1340
/*      Note that the following sequence *does* work for special        */
1341
/*      cases like deleting the last shape in the list, or deleting     */
1342
/*      a shape on the same page as the last shape.   At worst a wee    */
1343
/*      bit of extra work is done.                                      */
1344
/* ==================================================================== */
1345
1346
/* -------------------------------------------------------------------- */
1347
/*      Load the page of shapeids containing the last shape in our      */
1348
/*      index, capture the last shape's details, and remove it.         */
1349
/* -------------------------------------------------------------------- */
1350
1351
0
    uint32 vert_off, rec_off;
1352
0
    ShapeId  last_id;
1353
1354
0
    AccessShapeByIndex( total_shape_count-1 );
1355
1356
0
    last_id = shape_index_ids[total_shape_count-1-shape_index_start];
1357
0
    vert_off = shape_index_vertex_off[total_shape_count-1-shape_index_start];
1358
0
    rec_off = shape_index_record_off[total_shape_count-1-shape_index_start];
1359
1360
    // We don't actually have to modify this area of the index on disk.
1361
    // Some of the stuff at the end just becomes unreferenced when we
1362
    // decrement total_shape_count.
1363
1364
/* -------------------------------------------------------------------- */
1365
/*      Load the page with the shape we are deleting, and put last      */
1366
/*      the shapes information over it.                                 */
1367
/* -------------------------------------------------------------------- */
1368
0
    AccessShapeByIndex( shape_index );
1369
1370
0
    shape_index_ids[shape_index-shape_index_start] = last_id;
1371
0
    shape_index_vertex_off[shape_index-shape_index_start] = vert_off;
1372
0
    shape_index_record_off[shape_index-shape_index_start] = rec_off;
1373
1374
0
    shape_index_page_dirty = true;
1375
1376
0
    if( shapeid_map_active )
1377
0
        shapeid_map.erase( id );
1378
1379
    // if the highest shape_id is the one that was deleted,
1380
    // then reset highest_shapeid_used
1381
0
    if (id == highest_shapeid_used)
1382
0
        highest_shapeid_used = NullShapeId;
1383
0
    total_shape_count--;
1384
0
    valid_shape_count--;
1385
0
}
1386
1387
/************************************************************************/
1388
/*                            SetVertices()                             */
1389
/************************************************************************/
1390
1391
void CPCIDSKVectorSegment::SetVertices( ShapeId id,
1392
                                        const std::vector<ShapeVertex>& list )
1393
1394
685k
{
1395
685k
    FlushSegHeaderIfNeeded();
1396
685k
    int shape_index = IndexFromShapeId( id );
1397
1398
685k
    if( shape_index == -1 )
1399
0
        return ThrowPCIDSKException( "Attempt to call SetVertices() on non-existing shape '%d'.",
1400
0
                              (int) id );
1401
1402
685k
    PCIDSKBuffer vbuf( static_cast<int>(list.size()) * 24 + 8 );
1403
1404
685k
    AccessShapeByIndex( shape_index );
1405
1406
/* -------------------------------------------------------------------- */
1407
/*      Is the current space big enough to hold the new vertex set?     */
1408
/* -------------------------------------------------------------------- */
1409
685k
    uint32 vert_off = shape_index_vertex_off[shape_index - shape_index_start];
1410
685k
    uint32 chunk_size = 0;
1411
1412
685k
    if( vert_off != 0xffffffff )
1413
0
    {
1414
0
        memcpy( &chunk_size, GetData( sec_vert, vert_off, nullptr, 4 ), 4 );
1415
0
        if( needs_swap )
1416
0
            SwapData( &chunk_size, 4, 1 );
1417
1418
0
        if( chunk_size < (uint32) vbuf.buffer_size )
1419
0
        {
1420
0
            vert_off = 0xffffffff;
1421
0
        }
1422
0
    }
1423
1424
/* -------------------------------------------------------------------- */
1425
/*      Do we need to put this at the end of the section?               */
1426
/* -------------------------------------------------------------------- */
1427
685k
    if( vert_off == 0xffffffff )
1428
685k
    {
1429
685k
        vert_off = di[sec_vert].GetSectionEnd();
1430
685k
        chunk_size = vbuf.buffer_size;
1431
685k
    }
1432
1433
/* -------------------------------------------------------------------- */
1434
/*      Format the vertices in a buffer.                                */
1435
/* -------------------------------------------------------------------- */
1436
685k
    uint32 vert_count = static_cast<uint32>(list.size());
1437
685k
    unsigned int i;
1438
1439
685k
    memcpy( vbuf.buffer, &chunk_size, 4 );
1440
685k
    memcpy( vbuf.buffer+4, &vert_count, 4 );
1441
685k
    if( needs_swap )
1442
685k
        SwapData( vbuf.buffer, 4, 2 );
1443
1444
690k
    for( i = 0; i < vert_count; i++ )
1445
4.14k
    {
1446
4.14k
        memcpy( vbuf.buffer + 8 + i*24 +  0, &(list[i].x), 8 );
1447
4.14k
        memcpy( vbuf.buffer + 8 + i*24 +  8, &(list[i].y), 8 );
1448
4.14k
        memcpy( vbuf.buffer + 8 + i*24 + 16, &(list[i].z), 8 );
1449
4.14k
    }
1450
1451
685k
    if( needs_swap )
1452
685k
        SwapData( vbuf.buffer + 8, 8, 3*vert_count );
1453
1454
/* -------------------------------------------------------------------- */
1455
/*      Write the data into the working buffer.                         */
1456
/* -------------------------------------------------------------------- */
1457
685k
    memcpy( GetData( sec_vert, vert_off, nullptr, vbuf.buffer_size, true ),
1458
685k
            vbuf.buffer, vbuf.buffer_size );
1459
1460
/* -------------------------------------------------------------------- */
1461
/*      Record the offset                                               */
1462
/* -------------------------------------------------------------------- */
1463
685k
    if( shape_index_vertex_off[shape_index - shape_index_start] != vert_off )
1464
685k
    {
1465
685k
        shape_index_vertex_off[shape_index - shape_index_start] = vert_off;
1466
685k
        shape_index_page_dirty = true;
1467
685k
    }
1468
685k
}
1469
1470
/************************************************************************/
1471
/*                             SetFields()                              */
1472
/************************************************************************/
1473
1474
void CPCIDSKVectorSegment::SetFields( ShapeId id,
1475
                                      const std::vector<ShapeField>& list_in )
1476
1477
681k
{
1478
681k
    FlushSegHeaderIfNeeded();
1479
681k
    uint32 i;
1480
681k
    int shape_index = IndexFromShapeId( id );
1481
681k
    std::vector<ShapeField> full_list;
1482
681k
    const std::vector<ShapeField> *listp = nullptr;
1483
1484
681k
    if( shape_index == -1 )
1485
0
        return ThrowPCIDSKException( "Attempt to call SetFields() on non-existing shape id '%d'.",
1486
0
                              (int) id );
1487
1488
681k
    if( list_in.size() > vh.field_names.size() )
1489
0
    {
1490
0
        return ThrowPCIDSKException(
1491
0
            "Attempt to write %d fields to a layer with only %d fields.",
1492
0
            static_cast<int>(list_in.size()), static_cast<int>(vh.field_names.size()) );
1493
0
    }
1494
1495
681k
    if( list_in.size() < vh.field_names.size() )
1496
0
    {
1497
0
        full_list = list_in;
1498
1499
        // fill out missing fields in list with defaults.
1500
0
        for( i = static_cast<uint32>(list_in.size()); i < static_cast<uint32>(vh.field_names.size()); i++ )
1501
0
            full_list[i] = vh.field_defaults[i];
1502
1503
0
        listp = &full_list;
1504
0
    }
1505
681k
    else
1506
681k
        listp = &list_in;
1507
1508
681k
    AccessShapeByIndex( shape_index );
1509
1510
/* -------------------------------------------------------------------- */
1511
/*      Format the fields in the buffer.                                */
1512
/* -------------------------------------------------------------------- */
1513
681k
    PCIDSKBuffer fbuf(4);
1514
681k
    uint32 offset = 4;
1515
1516
4.19M
    for( i = 0; i < listp->size(); i++ )
1517
3.51M
        offset = WriteField( offset, (*listp)[i], fbuf );
1518
1519
681k
    fbuf.SetSize( offset );
1520
1521
/* -------------------------------------------------------------------- */
1522
/*      Is the current space big enough to hold the new field set?      */
1523
/* -------------------------------------------------------------------- */
1524
681k
    uint32 rec_off = shape_index_record_off[shape_index - shape_index_start];
1525
681k
    uint32 chunk_size = offset;
1526
1527
681k
    if( rec_off != 0xffffffff )
1528
0
    {
1529
0
        memcpy( &chunk_size, GetData( sec_record, rec_off, nullptr, 4 ), 4 );
1530
0
        if( needs_swap )
1531
0
            SwapData( &chunk_size, 4, 1 );
1532
1533
0
        if( chunk_size < (uint32) fbuf.buffer_size )
1534
0
        {
1535
0
            rec_off = 0xffffffff;
1536
0
        }
1537
0
    }
1538
1539
/* -------------------------------------------------------------------- */
1540
/*      Do we need to put this at the end of the section?               */
1541
/* -------------------------------------------------------------------- */
1542
681k
    if( rec_off == 0xffffffff )
1543
681k
    {
1544
681k
        rec_off = di[sec_record].GetSectionEnd();
1545
681k
        chunk_size = fbuf.buffer_size;
1546
681k
    }
1547
1548
/* -------------------------------------------------------------------- */
1549
/*      Set the chunk size, and number of fields.                       */
1550
/* -------------------------------------------------------------------- */
1551
681k
    memcpy( fbuf.buffer + 0, &chunk_size, 4 );
1552
1553
681k
    if( needs_swap )
1554
681k
        SwapData( fbuf.buffer, 4, 1 );
1555
1556
/* -------------------------------------------------------------------- */
1557
/*      Write the data into the working buffer.                         */
1558
/* -------------------------------------------------------------------- */
1559
681k
    memcpy( GetData( sec_record, rec_off, nullptr, fbuf.buffer_size, true ),
1560
681k
            fbuf.buffer, fbuf.buffer_size );
1561
1562
/* -------------------------------------------------------------------- */
1563
/*      Record the offset                                               */
1564
/* -------------------------------------------------------------------- */
1565
681k
    if( shape_index_record_off[shape_index - shape_index_start] != rec_off )
1566
681k
    {
1567
681k
        shape_index_record_off[shape_index - shape_index_start] = rec_off;
1568
681k
        shape_index_page_dirty = true;
1569
681k
    }
1570
681k
}
1571
1572
/************************************************************************/
1573
/*                       FlushLoadedShapeIndex()                        */
1574
/************************************************************************/
1575
1576
void CPCIDSKVectorSegment::FlushLoadedShapeIndex()
1577
1578
14.2k
{
1579
14.2k
    if( !shape_index_page_dirty )
1580
12.2k
        return;
1581
1582
1.97k
    uint32 offset = vh.ShapeIndexPrepare( total_shape_count * 12 + 4 );
1583
1584
1.97k
    PCIDSKBuffer write_buffer( shapeid_page_size * 12 );
1585
1586
    // Update the count field.
1587
1.97k
    memcpy( write_buffer.buffer, &total_shape_count, 4 );
1588
1.97k
    if( needs_swap )
1589
1.97k
        SwapData( write_buffer.buffer, 4, 1 );
1590
1.97k
    WriteToFile( write_buffer.buffer, offset, 4 );
1591
1592
    // Write out the page of shapeid information.
1593
1.97k
    unsigned int i;
1594
687k
    for( i = 0; i < shape_index_ids.size(); i++ )
1595
685k
    {
1596
685k
        memcpy( write_buffer.buffer + 12*i,
1597
685k
                &(shape_index_ids[i]), 4 );
1598
685k
        memcpy( write_buffer.buffer + 12*i + 4,
1599
685k
                &(shape_index_vertex_off[i]), 4 );
1600
685k
        memcpy( write_buffer.buffer + 12*i + 8,
1601
685k
                &(shape_index_record_off[i]), 4 );
1602
685k
    }
1603
1604
1.97k
    if( needs_swap )
1605
1.97k
        SwapData( write_buffer.buffer, 4, static_cast<int>(shape_index_ids.size()) * 3 );
1606
1607
1.97k
    WriteToFile( write_buffer.buffer,
1608
1.97k
                 offset + 4 + shape_index_start * 12,
1609
1.97k
                 12 * shape_index_ids.size() );
1610
1611
    // invalidate the raw buffer.
1612
1.97k
    raw_loaded_data.buffer_size = 0;
1613
1614
1615
1.97k
    shape_index_page_dirty = false;
1616
1.97k
}
1617