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
|