LCOV - code coverage report
Current view: top level - frmts/pcidsk/sdk/segment - cpcidskvectorsegment.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 509 636 80.0 %
Date: 2024-11-21 22:18:42 Functions: 35 40 87.5 %

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

Generated by: LCOV version 1.14