LCOV - code coverage report
Current view: top level - frmts/pcidsk/sdk/segment - cpcidsksegment.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 87 130 66.9 %
Date: 2025-07-05 13:22:42 Functions: 15 21 71.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Purpose:  Implementation of the CPCIDSKSegment 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 "segment/cpcidsksegment.h"
      13             : #include "core/metadataset.h"
      14             : #include "core/cpcidskfile.h"
      15             : #include "core/pcidsk_utils.h"
      16             : #include "pcidsk_buffer.h"
      17             : #include "pcidsk_exception.h"
      18             : #include <cassert>
      19             : #include <cstdlib>
      20             : #include <cstring>
      21             : #include <limits>
      22             : #include <vector>
      23             : #include <string>
      24             : 
      25             : using namespace PCIDSK;
      26             : 
      27             : PCIDSKSegment::~PCIDSKSegment() = default;
      28             : 
      29             : /************************************************************************/
      30             : /*                           PCIDSKSegment()                            */
      31             : /************************************************************************/
      32             : 
      33        1466 : CPCIDSKSegment::CPCIDSKSegment( PCIDSKFile *fileIn, int segmentIn,
      34        1466 :                               const char *segment_pointer )
      35             : 
      36             : {
      37        1466 :     this->file = fileIn;
      38        1466 :     this->segment = segmentIn;
      39             : 
      40        1466 :     LoadSegmentPointer( segment_pointer );
      41        1466 :     LoadSegmentHeader(); // eventually we might want to defer this.
      42             : 
      43             : /* -------------------------------------------------------------------- */
      44             : /*      Initialize the metadata object, but do not try to load till     */
      45             : /*      needed.                                                         */
      46             : /* -------------------------------------------------------------------- */
      47        1466 :     metadata = new MetadataSet;
      48        1466 :     metadata->Initialize( file, SegmentTypeName(segment_type), segment );
      49        1466 : }
      50             : 
      51             : /************************************************************************/
      52             : /*                           ~PCIDSKSegment()                           */
      53             : /************************************************************************/
      54             : 
      55        1494 : CPCIDSKSegment::~CPCIDSKSegment()
      56             : 
      57             : {
      58        1466 :     delete metadata;
      59        1494 : }
      60             : 
      61             : /************************************************************************/
      62             : /*                          GetMetadataValue()                          */
      63             : /************************************************************************/
      64        1183 : std::string CPCIDSKSegment::GetMetadataValue( const std::string &key ) const
      65             : {
      66        1183 :     return metadata->GetMetadataValue(key);
      67             : }
      68             : 
      69             : /************************************************************************/
      70             : /*                          SetMetadataValue()                          */
      71             : /************************************************************************/
      72          33 : void CPCIDSKSegment::SetMetadataValue( const std::string &key, const std::string &value )
      73             : {
      74          33 :     metadata->SetMetadataValue(key,value);
      75          33 : }
      76             : 
      77             : /************************************************************************/
      78             : /*                           GetMetadataKeys()                           */
      79             : /************************************************************************/
      80           1 : std::vector<std::string> CPCIDSKSegment::GetMetadataKeys() const
      81             : {
      82           1 :     return metadata->GetMetadataKeys();
      83             : }
      84             : 
      85             : /************************************************************************/
      86             : /*                         LoadSegmentPointer()                         */
      87             : /************************************************************************/
      88             : 
      89        2651 : void CPCIDSKSegment::LoadSegmentPointer( const char *segment_pointer )
      90             : 
      91             : {
      92        2651 :     PCIDSKBuffer segptr( segment_pointer, 32 );
      93             : 
      94        2651 :     segment_flag = segptr.buffer[0];
      95        2651 :     const int segment_type_int = atoi(segptr.Get(1,3));
      96        2651 :     segment_type = EQUAL(SegmentTypeName(segment_type_int), "UNKNOWN") ?
      97             :         SEG_UNKNOWN : static_cast<eSegType>(segment_type_int);
      98        2651 :     data_offset = atouint64(segptr.Get(12,11));
      99        2651 :     if( data_offset == 0 )
     100           0 :         data_offset = 0; // throw exception maybe ?
     101             :     else
     102             :     {
     103        2651 :         if( data_offset-1 > std::numeric_limits<uint64>::max() / 512 )
     104             :         {
     105           0 :             return ThrowPCIDSKException("too large data_offset");
     106             :         }
     107        2651 :         data_offset = (data_offset-1) * 512;
     108             :     }
     109        2651 :     data_size = atouint64(segptr.Get(23,9));
     110        2651 :     data_size_limit = 999999999ULL * 512;
     111             : 
     112        2651 :     if( data_size > 999999999ULL )
     113             :     {
     114           0 :         return ThrowPCIDSKException("too large data_size");
     115             :     }
     116        2651 :     data_size *= 512;
     117             : 
     118        2651 :     segptr.Get(4,8,segment_name);
     119             : }
     120             : 
     121             : /************************************************************************/
     122             : /*                         LoadSegmentHeader()                          */
     123             : /************************************************************************/
     124             : #include <iostream>
     125        1466 : void CPCIDSKSegment::LoadSegmentHeader()
     126             : 
     127             : {
     128        1466 :     header.SetSize(1024);
     129             : 
     130        1466 :     file->ReadFromFile( header.buffer, data_offset, 1024 );
     131             : 
     132             :     // Read the history from the segment header. PCIDSK supports
     133             :     // 8 history entries per segment.
     134        2932 :     std::string hist_msg;
     135        1466 :     history_.clear();
     136       13194 :     for (unsigned int i = 0; i < 8; i++)
     137             :     {
     138       11728 :         header.Get(384 + i * 80, 80, hist_msg);
     139             : 
     140             :         // Some programs seem to push history records with a trailing '\0'
     141             :         // so do some extra processing to cleanup.  FUN records on segment
     142             :         // 3 of eltoro.pix are an example of this.
     143       11728 :         size_t size = hist_msg.size();
     144           0 :         while( size > 0
     145       11728 :                && (hist_msg[size-1] == ' ' || hist_msg[size-1] == '\0') )
     146           0 :             size--;
     147             : 
     148       11728 :         hist_msg.resize(size);
     149             : 
     150       11728 :         history_.push_back(hist_msg);
     151             :     }
     152        1466 : }
     153             : 
     154             : /************************************************************************/
     155             : /*                            FlushHeader()                             */
     156             : /*                                                                      */
     157             : /*      This is used primarily after this class or subclasses have      */
     158             : /*      modified the header buffer and need it pushed back to disk.     */
     159             : /************************************************************************/
     160             : 
     161        1145 : void CPCIDSKSegment::FlushHeader()
     162             : 
     163             : {
     164        1145 :     file->WriteToFile( header.buffer, data_offset, 1024 );
     165        1145 : }
     166             : 
     167             : /************************************************************************/
     168             : /*                            ReadFromFile()                            */
     169             : /************************************************************************/
     170             : 
     171        2020 : void CPCIDSKSegment::ReadFromFile( void *buffer, uint64 offset, uint64 size )
     172             : 
     173             : {
     174        2020 :     if( offset+size+1024 > data_size )
     175           0 :         return ThrowPCIDSKException("Attempt to read past end of segment %d: "
     176             :                                     "Segment Size: " PCIDSK_FRMT_UINT64 ", "
     177             :                                     "Read Offset: " PCIDSK_FRMT_UINT64 ", "
     178             :                                     "Read Size: " PCIDSK_FRMT_UINT64,
     179           0 :                                     segment, data_size, offset, size);
     180             : 
     181        2020 :     file->ReadFromFile( buffer, offset + data_offset + 1024, size );
     182             : }
     183             : 
     184             : /************************************************************************/
     185             : /*                            WriteToFile()                             */
     186             : /************************************************************************/
     187             : 
     188        2064 : void CPCIDSKSegment::WriteToFile( const void *buffer, uint64 offset, uint64 size )
     189             : {
     190        2064 :     if( offset+size > data_size-1024 )
     191             :     {
     192        1168 :         CPCIDSKFile *poFile = dynamic_cast<CPCIDSKFile *>(file);
     193             : 
     194        1168 :         if (poFile == nullptr) {
     195           0 :             return ThrowPCIDSKException("Attempt to dynamic_cast the file interface "
     196             :                 "to a CPCIDSKFile failed. This is a programmer error, and should "
     197           0 :                 "be reported to your software provider.");
     198             :         }
     199             : 
     200        1168 :         uint64 blocks_to_add =
     201        1168 :             ((offset+size+511) - (data_size - 1024)) / 512;
     202             : 
     203             :         // prezero if we aren't directly writing all the new blocks.
     204        1168 :         poFile->ExtendSegment( segment, blocks_to_add,
     205        2336 :                              !(offset == data_size - 1024
     206        1168 :                                && size == blocks_to_add * 512) );
     207             :         // ExtendSegment() will call LoadSegmentPointer() to update data_size.
     208             :     }
     209             : 
     210        2064 :     assert(file); // avoid CLang Static Analyzer false positive
     211        2064 :     file->WriteToFile( buffer, offset + data_offset + 1024, size );
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                           GetDescription()                           */
     216             : /************************************************************************/
     217             : 
     218           0 : std::string CPCIDSKSegment::GetDescription()
     219             : {
     220           0 :     std::string target;
     221             : 
     222           0 :     header.Get( 0, 64, target );
     223             : 
     224           0 :     return target;
     225             : }
     226             : 
     227             : /************************************************************************/
     228             : /*                           SetDescription()                           */
     229             : /************************************************************************/
     230             : 
     231           0 : void CPCIDSKSegment::SetDescription( const std::string &description )
     232             : {
     233           0 :     header.Put( description.c_str(), 0, 64);
     234             : 
     235           0 :     file->WriteToFile( header.buffer, data_offset, 1024 );
     236           0 : }
     237             : 
     238             : /************************************************************************/
     239             : /*                              IsAtEOF()                               */
     240             : /************************************************************************/
     241             : 
     242        1176 : bool CPCIDSKSegment::IsAtEOF()
     243             : {
     244        1176 :     return data_offset + data_size == file->GetFileSize() * 512;
     245             : }
     246             : 
     247             : /************************************************************************/
     248             : /*                               CanExtend()                            */
     249             : /************************************************************************/
     250           0 : bool CPCIDSKSegment::CanExtend(uint64 size) const
     251             : {
     252           0 :     return data_size + size <= data_size_limit;
     253             : }
     254             : 
     255             : /************************************************************************/
     256             : /*                         GetHistoryEntries()                          */
     257             : /************************************************************************/
     258             : 
     259           0 : std::vector<std::string> CPCIDSKSegment::GetHistoryEntries() const
     260             : {
     261           0 :     return history_;
     262             : }
     263             : 
     264             : /************************************************************************/
     265             : /*                         SetHistoryEntries()                          */
     266             : /************************************************************************/
     267             : 
     268           0 : void CPCIDSKSegment::SetHistoryEntries(const std::vector<std::string> &entries)
     269             : 
     270             : {
     271           0 :     for( unsigned int i = 0; i < 8; i++ )
     272             :     {
     273           0 :         const char *msg = "";
     274           0 :         if( entries.size() > i )
     275           0 :             msg = entries[i].c_str();
     276             : 
     277           0 :         header.Put( msg, 384 + i * 80, 80 );
     278             :     }
     279             : 
     280           0 :     FlushHeader();
     281             : 
     282             :     // Force reloading of history_
     283           0 :     LoadSegmentHeader();
     284           0 : }
     285             : 
     286             : /************************************************************************/
     287             : /*                            PushHistory()                             */
     288             : /************************************************************************/
     289             : 
     290           0 : void CPCIDSKSegment::PushHistory( const std::string &app,
     291             :                                   const std::string &message )
     292             : 
     293             : {
     294             : #define MY_MIN(a,b)      ((a<b) ? a : b)
     295             : 
     296             :     char current_time[17];
     297             :     char history[81];
     298             : 
     299           0 :     GetCurrentDateTime( current_time );
     300             : 
     301           0 :     memset( history, ' ', 80 );
     302           0 :     history[80] = '\0';
     303             : 
     304           0 :     memcpy( history + 0, app.c_str(), MY_MIN(app.size(),7) );
     305           0 :     history[7] = ':';
     306             : 
     307           0 :     memcpy( history + 8, message.c_str(), MY_MIN(message.size(),56) );
     308           0 :     memcpy( history + 64, current_time, 16 );
     309             : 
     310           0 :     std::vector<std::string> history_entries = GetHistoryEntries();
     311             : 
     312             :     // coverity[string_null]
     313           0 :     history_entries.insert( history_entries.begin(), history );
     314           0 :     history_entries.resize(8);
     315             : 
     316           0 :     SetHistoryEntries( history_entries );
     317           0 : }
     318             : 
     319             : 
     320             : /************************************************************************/
     321             : /*                              MoveData()                              */
     322             : /*                                                                      */
     323             : /*      Move a chunk of data within a segment. Overlapping source       */
     324             : /*      and destination are permitted.                                  */
     325             : /************************************************************************/
     326             : 
     327         160 : void CPCIDSKSegment::MoveData( uint64 src_offset, uint64 dst_offset,
     328             :                                uint64 size_in_bytes )
     329             : 
     330             : {
     331         160 :     bool copy_backwards = false;
     332             : 
     333             :     // We move things backwards if the areas overlap and the destination
     334             :     // is further on in the segment.
     335         160 :     if( dst_offset > src_offset
     336         160 :         && src_offset + size_in_bytes > dst_offset )
     337          45 :         copy_backwards = true;
     338             : 
     339             : 
     340             :     // Move the segment data to the new location.
     341             :     uint8 copy_buf[16384];
     342             :     uint64 bytes_to_go;
     343             : 
     344         160 :     bytes_to_go = size_in_bytes;
     345             : 
     346         320 :     while( bytes_to_go > 0 )
     347             :     {
     348         160 :         uint64 bytes_this_chunk = sizeof(copy_buf);
     349         160 :         if( bytes_to_go < bytes_this_chunk )
     350         160 :             bytes_this_chunk = bytes_to_go;
     351             : 
     352         160 :         if( copy_backwards )
     353             :         {
     354          45 :             ReadFromFile( copy_buf,
     355          45 :                           src_offset + bytes_to_go - bytes_this_chunk,
     356          45 :                           bytes_this_chunk );
     357          45 :             WriteToFile( copy_buf,
     358          45 :                          dst_offset + bytes_to_go - bytes_this_chunk,
     359          45 :                          bytes_this_chunk );
     360             :         }
     361             :         else
     362             :         {
     363         115 :             ReadFromFile( copy_buf, src_offset, bytes_this_chunk );
     364         115 :             WriteToFile( copy_buf, dst_offset, bytes_this_chunk );
     365             : 
     366         115 :             src_offset += bytes_this_chunk;
     367         115 :             dst_offset += bytes_this_chunk;
     368             :         }
     369             : 
     370         160 :         bytes_to_go -= bytes_this_chunk;
     371             :     }
     372         160 : }

Generated by: LCOV version 1.14