LCOV - code coverage report
Current view: top level - frmts/nitf - offsetpatcher.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 148 202 73.3 %
Date: 2026-03-05 10:33:42 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  NITF Read/Write Library
       4             :  * Purpose:  Manages writing offsets to file locations
       5             :  * Author:   Even Rouault, even dot rouault at spatialys dot com
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 2026, T-Kartor
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_error.h"
      14             : 
      15             : #include <cinttypes>
      16             : #include <limits>
      17             : 
      18             : #include "offsetpatcher.h"
      19             : 
      20             : namespace GDALOffsetPatcher
      21             : {
      22             : 
      23             : /************************************************************************/
      24             : /*                OffsetOrSizeDeclaration::SetLocation()                */
      25             : /************************************************************************/
      26             : 
      27         540 : bool OffsetOrSizeDeclaration::SetLocation(OffsetPatcherBuffer *buffer,
      28             :                                           size_t offsetInBuffer)
      29             : {
      30         540 :     if (m_location.buffer)
      31             :     {
      32           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      33             :                  "Location already declared for object %s", m_osName.c_str());
      34           0 :         return false;
      35             :     }
      36         540 :     m_location.buffer = buffer;
      37         540 :     m_location.offsetInBuffer = offsetInBuffer;
      38         540 :     return true;
      39             : }
      40             : 
      41             : /************************************************************************/
      42             : /*               OffsetOrSizeDeclaration::SetReference()                */
      43             : /************************************************************************/
      44             : 
      45        1186 : void OffsetOrSizeDeclaration::SetReference(OffsetPatcherBuffer *buffer,
      46             :                                            size_t offsetInBuffer,
      47             :                                            int objectSizeBytes,
      48             :                                            bool bEndiannessIsLittle)
      49             : {
      50        1186 :     OffsetOrSizeReference ref;
      51        1186 :     ref.buffer = buffer;
      52        1186 :     ref.offsetInBuffer = offsetInBuffer;
      53        1186 :     ref.objectSizeBytes = objectSizeBytes;
      54        1186 :     ref.bEndiannessIsLittle = bEndiannessIsLittle;
      55        1186 :     m_references.push_back(std::move(ref));
      56        1186 : }
      57             : 
      58             : /************************************************************************/
      59             : /*           OffsetPatcherBuffer::AppendUInt32RefForOffset()            */
      60             : /************************************************************************/
      61             : 
      62         557 : void OffsetPatcherBuffer::AppendUInt32RefForOffset(
      63             :     const std::string &osName, bool bRelativeToStartOfBuffer)
      64             : {
      65         557 :     auto oIter = m_offsetPatcher.m_offsets.find(osName);
      66         557 :     if (oIter == m_offsetPatcher.m_offsets.end())
      67             :     {
      68             :         auto offset = std::make_unique<OffsetOrSizeDeclaration>(
      69         548 :             osName, bRelativeToStartOfBuffer);
      70        1096 :         oIter = m_offsetPatcher.m_offsets
      71         548 :                     .insert(std::pair(osName, std::move(offset)))
      72             :                     .first;
      73             :     }
      74        1114 :     oIter->second->SetReference(this, m_abyBuffer.size(),
      75             :                                 static_cast<int>(sizeof(uint32_t)),
      76         557 :                                 m_bEndiannessIsLittle);
      77         557 :     m_abyBuffer.push_back('?');
      78         557 :     m_abyBuffer.push_back('?');
      79         557 :     m_abyBuffer.push_back('?');
      80         557 :     m_abyBuffer.push_back('?');
      81         557 : }
      82             : 
      83             : /************************************************************************/
      84             : /*        OffsetPatcherBuffer::AppendUInt16RefForSizeOfBuffer()         */
      85             : /************************************************************************/
      86             : 
      87         105 : void OffsetPatcherBuffer::AppendUInt16RefForSizeOfBuffer(
      88             :     const std::string &osBufferName)
      89             : {
      90         105 :     auto oIter = m_offsetPatcher.m_sizes.find(osBufferName);
      91         105 :     if (oIter == m_offsetPatcher.m_sizes.end())
      92             :     {
      93         105 :         auto size = std::make_unique<OffsetOrSizeDeclaration>(osBufferName);
      94         210 :         oIter = m_offsetPatcher.m_sizes
      95         105 :                     .insert(std::pair(osBufferName, std::move(size)))
      96             :                     .first;
      97             :     }
      98         210 :     oIter->second->SetReference(this, m_abyBuffer.size(),
      99             :                                 static_cast<int>(sizeof(uint16_t)),
     100         105 :                                 m_bEndiannessIsLittle);
     101         105 :     m_abyBuffer.push_back('?');
     102         105 :     m_abyBuffer.push_back('?');
     103         105 : }
     104             : 
     105             : /************************************************************************/
     106             : /*        OffsetPatcherBuffer::AppendUInt32RefForSizeOfBuffer()         */
     107             : /************************************************************************/
     108             : 
     109         524 : void OffsetPatcherBuffer::AppendUInt32RefForSizeOfBuffer(
     110             :     const std::string &osBufferName)
     111             : {
     112         524 :     auto oIter = m_offsetPatcher.m_sizes.find(osBufferName);
     113         524 :     if (oIter == m_offsetPatcher.m_sizes.end())
     114             :     {
     115         524 :         auto size = std::make_unique<OffsetOrSizeDeclaration>(osBufferName);
     116        1048 :         oIter = m_offsetPatcher.m_sizes
     117         524 :                     .insert(std::pair(osBufferName, std::move(size)))
     118             :                     .first;
     119             :     }
     120        1048 :     oIter->second->SetReference(this, m_abyBuffer.size(),
     121             :                                 static_cast<int>(sizeof(uint32_t)),
     122         524 :                                 m_bEndiannessIsLittle);
     123         524 :     m_abyBuffer.push_back('?');
     124         524 :     m_abyBuffer.push_back('?');
     125         524 :     m_abyBuffer.push_back('?');
     126         524 :     m_abyBuffer.push_back('?');
     127         524 : }
     128             : 
     129             : /************************************************************************/
     130             : /*                  OffsetPatcherBuffer::AppendByte()                   */
     131             : /************************************************************************/
     132             : 
     133     9207930 : void OffsetPatcherBuffer::AppendByte(uint8_t byVal)
     134             : {
     135     9207930 :     m_abyBuffer.push_back(byVal);
     136     9207930 : }
     137             : 
     138             : /************************************************************************/
     139             : /*                 OffsetPatcherBuffer::AppendUInt16()                  */
     140             : /************************************************************************/
     141             : 
     142        2166 : void OffsetPatcherBuffer::AppendUInt16(uint16_t nVal)
     143             : {
     144        2166 :     if (NeedByteSwap())
     145        2166 :         CPL_SWAP16PTR(&nVal);
     146        2166 :     const GByte *pabyVal = reinterpret_cast<const GByte *>(&nVal);
     147        2166 :     m_abyBuffer.insert(m_abyBuffer.end(), pabyVal, pabyVal + sizeof(nVal));
     148        2166 : }
     149             : 
     150             : /************************************************************************/
     151             : /*                 OffsetPatcherBuffer::AppendUInt32()                  */
     152             : /************************************************************************/
     153             : 
     154       26129 : void OffsetPatcherBuffer::AppendUInt32(uint32_t nVal)
     155             : {
     156       26129 :     if (NeedByteSwap())
     157       26129 :         CPL_SWAP32PTR(&nVal);
     158       26129 :     const GByte *pabyVal = reinterpret_cast<const GByte *>(&nVal);
     159       26129 :     m_abyBuffer.insert(m_abyBuffer.end(), pabyVal, pabyVal + sizeof(nVal));
     160       26129 : }
     161             : 
     162             : /************************************************************************/
     163             : /*                 OffsetPatcherBuffer::AppendFloat64()                 */
     164             : /************************************************************************/
     165             : 
     166         672 : void OffsetPatcherBuffer::AppendFloat64(double dfVal)
     167             : {
     168         672 :     if (NeedByteSwap())
     169         672 :         CPL_SWAP64PTR(&dfVal);
     170         672 :     const GByte *pabyVal = reinterpret_cast<const GByte *>(&dfVal);
     171         672 :     m_abyBuffer.insert(m_abyBuffer.end(), pabyVal, pabyVal + sizeof(dfVal));
     172         672 : }
     173             : 
     174             : /************************************************************************/
     175             : /*                 OffsetPatcherBuffer::AppendString()                  */
     176             : /************************************************************************/
     177             : 
     178         798 : void OffsetPatcherBuffer::AppendString(const std::string &s)
     179             : {
     180         798 :     const GByte *pabyVal = reinterpret_cast<const GByte *>(s.data());
     181         798 :     m_abyBuffer.insert(m_abyBuffer.end(), pabyVal, pabyVal + s.size());
     182         798 : }
     183             : 
     184             : /************************************************************************/
     185             : /*        OffsetPatcherBuffer::DeclareOffsetAtCurrentPosition()         */
     186             : /************************************************************************/
     187             : 
     188         540 : bool OffsetPatcherBuffer::DeclareOffsetAtCurrentPosition(
     189             :     const std::string &osName)
     190             : {
     191         540 :     auto oIter = m_offsetPatcher.m_offsets.find(osName);
     192         540 :     if (oIter == m_offsetPatcher.m_offsets.end())
     193             :     {
     194           0 :         auto offset = std::make_unique<OffsetOrSizeDeclaration>(osName);
     195           0 :         oIter = m_offsetPatcher.m_offsets
     196           0 :                     .insert(std::pair(osName, std::move(offset)))
     197             :                     .first;
     198             :     }
     199        1080 :     return oIter->second->SetLocation(this, m_abyBuffer.size());
     200             : }
     201             : 
     202             : /************************************************************************/
     203             : /*        OffsetPatcherBuffer::DeclareBufferWrittenAtPosition()         */
     204             : /************************************************************************/
     205             : 
     206         562 : void OffsetPatcherBuffer::DeclareBufferWrittenAtPosition(
     207             :     vsi_l_offset nFileOffset)
     208             : {
     209         562 :     m_nOffset = nFileOffset;
     210         562 : }
     211             : 
     212             : /************************************************************************/
     213             : /*                    OffsetPatcher::CreateBuffer()                     */
     214             : /************************************************************************/
     215             : 
     216         570 : OffsetPatcherBuffer *OffsetPatcher::CreateBuffer(const std::string &osName,
     217             :                                                  bool bEndiannessIsLittle)
     218             : {
     219         570 :     if (m_buffers.find(osName) != m_buffers.end())
     220             :     {
     221           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     222             :                  "Buffer with name '%s' already created", osName.c_str());
     223           0 :         return nullptr;
     224             :     }
     225             :     auto buffer = std::make_unique<OffsetPatcherBuffer>(osName, *this,
     226         570 :                                                         bEndiannessIsLittle);
     227        1140 :     return m_buffers.insert(std::pair(osName, std::move(buffer)))
     228         570 :         .first->second.get();
     229             : }
     230             : 
     231             : /************************************************************************/
     232             : /*                  OffsetPatcher::GetBufferFromName()                  */
     233             : /************************************************************************/
     234             : 
     235             : OffsetPatcherBuffer *
     236         498 : OffsetPatcher::GetBufferFromName(const std::string &osName) const
     237             : {
     238         498 :     auto oIter = m_buffers.find(osName);
     239         498 :     if (oIter != m_buffers.end())
     240         370 :         return oIter->second.get();
     241         128 :     return nullptr;
     242             : }
     243             : 
     244             : /************************************************************************/
     245             : /*                OffsetPatcher::GetOffsetDeclaration()                 */
     246             : /************************************************************************/
     247             : 
     248             : OffsetOrSizeDeclaration *
     249           0 : OffsetPatcher::GetOffsetDeclaration(const std::string &osName) const
     250             : {
     251           0 :     auto oIter = m_offsets.find(osName);
     252           0 :     if (oIter != m_offsets.end())
     253           0 :         return oIter->second.get();
     254           0 :     return nullptr;
     255             : }
     256             : 
     257             : /************************************************************************/
     258             : /*                      OffsetPatcher::Finalize()                       */
     259             : /************************************************************************/
     260             : 
     261          51 : bool OffsetPatcher::Finalize(VSILFILE *fp)
     262             : {
     263         585 :     for (const auto &[offsetName, declaration] : m_offsets)
     264             :     {
     265         534 :         if (declaration->m_location.buffer == nullptr)
     266             :         {
     267           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     268             :                      "Location for offset '%s' has not been set",
     269             :                      offsetName.c_str());
     270           0 :             return false;
     271             :         }
     272         534 :         if (declaration->m_location.buffer->m_nOffset == INVALID_OFFSET)
     273             :         {
     274           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     275             :                      "Buffer '%s' that contains location for offset '%s' has "
     276             :                      "not been written to file",
     277           0 :                      declaration->m_location.buffer->m_osName.c_str(),
     278             :                      offsetName.c_str());
     279           0 :             return false;
     280             :         }
     281             : 
     282             :         const vsi_l_offset nOffsetPosition =
     283         534 :             (declaration->m_bRelativeToStartOfBuffer
     284         534 :                  ? 0
     285         511 :                  : declaration->m_location.buffer->m_nOffset) +
     286         534 :             declaration->m_location.offsetInBuffer;
     287             : 
     288         534 :         if (nOffsetPosition > UINT32_MAX)
     289             :         {
     290           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     291             :                      "Position of offset '%s' does not fit on a uint32",
     292             :                      offsetName.c_str());
     293           0 :             return false;
     294             :         }
     295             : 
     296         534 :         if (declaration->m_references.empty())
     297             :         {
     298           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     299             :                      "No reference found to offset '%s'", offsetName.c_str());
     300             :         }
     301             :         else
     302             :         {
     303        1077 :             for (const auto &ref : declaration->m_references)
     304             :             {
     305         543 :                 if (ref.buffer->m_nOffset == INVALID_OFFSET)
     306             :                 {
     307           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     308             :                              "Buffer '%s' that contains a reference to offset "
     309             :                              "'%s' has not been written to file",
     310           0 :                              ref.buffer->m_osName.c_str(), offsetName.c_str());
     311           0 :                     return false;
     312             :                 }
     313             : 
     314         543 :                 if (ref.objectSizeBytes != 4)
     315             :                 {
     316           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     317             :                              "Buffer '%s' that contains a reference to offset "
     318             :                              "'%s' has an unsupported offset size %d",
     319           0 :                              ref.buffer->m_osName.c_str(), offsetName.c_str(),
     320           0 :                              ref.objectSizeBytes);
     321           0 :                     return false;
     322             :                 }
     323             : 
     324             : #if CPL_IS_LSB
     325         543 :                 const bool bNeedByteSwap = !ref.bEndiannessIsLittle;
     326             : #else
     327             :                 const bool bNeedByteSwap = ref.bEndiannessIsLittle;
     328             : #endif
     329             : 
     330         543 :                 uint32_t nVal = static_cast<uint32_t>(nOffsetPosition);
     331         543 :                 if (bNeedByteSwap)
     332             :                 {
     333         543 :                     CPL_SWAP32PTR(&nVal);
     334             :                 }
     335             : 
     336         543 :                 const auto nRefPosition =
     337         543 :                     ref.buffer->m_nOffset + ref.offsetInBuffer;
     338        1086 :                 if (fp->Seek(nRefPosition, SEEK_SET) != 0 ||
     339         543 :                     fp->Write(&nVal, 1, sizeof(nVal)) != sizeof(nVal))
     340             :                 {
     341           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     342             :                              "Cannot write reference to offset '%s' at "
     343             :                              "position %" PRIu64,
     344           0 :                              ref.buffer->m_osName.c_str(),
     345             :                              static_cast<uint64_t>(nRefPosition));
     346           0 :                     return false;
     347             :                 }
     348             :             }
     349             :         }
     350             :     }
     351             : 
     352         664 :     for (const auto &[bufferName, declaration] : m_sizes)
     353             :     {
     354         613 :         size_t nBufferSize = 0;
     355         613 :         if (bufferName.find('+') == std::string::npos)
     356             :         {
     357         562 :             auto oReferencedBufferIter = m_buffers.find(bufferName);
     358         562 :             if (oReferencedBufferIter == m_buffers.end())
     359             :             {
     360           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     361             :                          "No buffer registered with name '%s'",
     362             :                          bufferName.c_str());
     363           0 :                 return false;
     364             :             }
     365         562 :             const auto poReferencedBuffer = oReferencedBufferIter->second.get();
     366         562 :             if (poReferencedBuffer->m_nOffset == INVALID_OFFSET)
     367             :             {
     368           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     369             :                          "Buffer '%s' has not been written to file",
     370             :                          bufferName.c_str());
     371           0 :                 return false;
     372             :             }
     373         562 :             nBufferSize = poReferencedBuffer->GetBuffer().size();
     374             :         }
     375             :         else
     376             :         {
     377             :             const CPLStringList aosBufferNames(
     378          51 :                 CSLTokenizeString2(bufferName.c_str(), "+", 0));
     379         511 :             for (const char *pszBuffer : aosBufferNames)
     380             :             {
     381         460 :                 auto oReferencedBufferIter = m_buffers.find(pszBuffer);
     382         460 :                 if (oReferencedBufferIter == m_buffers.end())
     383             :                 {
     384           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     385             :                              "No buffer registered with name '%s'", pszBuffer);
     386           0 :                     return false;
     387             :                 }
     388             :                 const auto poReferencedBuffer =
     389         460 :                     oReferencedBufferIter->second.get();
     390         460 :                 if (poReferencedBuffer->m_nOffset == INVALID_OFFSET)
     391             :                 {
     392           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     393             :                              "Buffer '%s' has not been written to file",
     394             :                              pszBuffer);
     395           0 :                     return false;
     396             :                 }
     397         460 :                 nBufferSize += poReferencedBuffer->GetBuffer().size();
     398             :             }
     399             :         }
     400             : 
     401        1226 :         for (const auto &ref : declaration->m_references)
     402             :         {
     403         613 :             if (ref.buffer->m_nOffset == INVALID_OFFSET)
     404             :             {
     405           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     406             :                          "Buffer '%s' that contains a reference to size "
     407             :                          "'%s' has not been written to file",
     408           0 :                          ref.buffer->m_osName.c_str(), bufferName.c_str());
     409           0 :                 return false;
     410             :             }
     411             : 
     412         613 :             if (ref.objectSizeBytes != 2 && ref.objectSizeBytes != 4)
     413             :             {
     414           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     415             :                          "Buffer '%s' that contains a reference to size "
     416             :                          "'%s' has an unsupported offset size %d",
     417           0 :                          ref.buffer->m_osName.c_str(), bufferName.c_str(),
     418           0 :                          ref.objectSizeBytes);
     419           0 :                 return false;
     420             :             }
     421             : 
     422             : #if CPL_IS_LSB
     423         613 :             const bool bNeedByteSwap = !ref.bEndiannessIsLittle;
     424             : #else
     425             :             const bool bNeedByteSwap = ref.bEndiannessIsLittle;
     426             : #endif
     427             : 
     428         613 :             const auto nRefPosition =
     429         613 :                 ref.buffer->m_nOffset + ref.offsetInBuffer;
     430             : 
     431         613 :             if (ref.objectSizeBytes == 2)
     432             :             {
     433         102 :                 if (nBufferSize > std::numeric_limits<uint16_t>::max())
     434             :                 {
     435           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     436             :                              "Cannot write reference to size '%s' at "
     437             :                              "position %" PRIu64
     438             :                              " on a uint16 because the size exceeds uint16",
     439           0 :                              ref.buffer->m_osName.c_str(),
     440             :                              static_cast<uint64_t>(nRefPosition));
     441           0 :                     return false;
     442             :                 }
     443         102 :                 uint16_t nVal = static_cast<uint16_t>(nBufferSize);
     444         102 :                 if (bNeedByteSwap)
     445             :                 {
     446         102 :                     CPL_SWAP16PTR(&nVal);
     447             :                 }
     448             : 
     449         204 :                 if (fp->Seek(nRefPosition, SEEK_SET) != 0 ||
     450         102 :                     fp->Write(&nVal, 1, sizeof(nVal)) != sizeof(nVal))
     451             :                 {
     452           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     453             :                              "Cannot write reference to size '%s' at "
     454             :                              "position %" PRIu64,
     455           0 :                              ref.buffer->m_osName.c_str(),
     456             :                              static_cast<uint64_t>(nRefPosition));
     457           0 :                     return false;
     458             :                 }
     459             :             }
     460             :             else
     461             :             {
     462         511 :                 CPLAssert(ref.objectSizeBytes == 4);
     463         511 :                 uint32_t nVal = static_cast<uint32_t>(nBufferSize);
     464         511 :                 if (bNeedByteSwap)
     465             :                 {
     466         511 :                     CPL_SWAP32PTR(&nVal);
     467             :                 }
     468             : 
     469        1022 :                 if (fp->Seek(nRefPosition, SEEK_SET) != 0 ||
     470         511 :                     fp->Write(&nVal, 1, sizeof(nVal)) != sizeof(nVal))
     471             :                 {
     472           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     473             :                              "Cannot write reference to size '%s' at "
     474             :                              "position %" PRIu64,
     475           0 :                              ref.buffer->m_osName.c_str(),
     476             :                              static_cast<uint64_t>(nRefPosition));
     477           0 :                     return false;
     478             :                 }
     479             :             }
     480             :         }
     481             :     }
     482             : 
     483          51 :     return true;
     484             : }
     485             : 
     486             : }  // namespace GDALOffsetPatcher

Generated by: LCOV version 1.14