Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Generic Raw Binary Driver
4 : * Purpose: Implementation of RawDataset and RawRasterBand classes.
5 : * Author: Frank Warmerdam, warmerda@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2007-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "cpl_vax.h"
16 : #include "rawdataset.h"
17 :
18 : #include <cassert>
19 : #include <cinttypes>
20 : #include <climits>
21 : #include <cmath>
22 : #include <cstddef>
23 : #include <cstdint>
24 : #include <cstdlib>
25 : #include <cstring>
26 : #include <algorithm>
27 : #include <limits>
28 : #include <vector>
29 :
30 : #include "cpl_conv.h"
31 : #include "cpl_error.h"
32 : #include "cpl_progress.h"
33 : #include "cpl_string.h"
34 : #include "cpl_virtualmem.h"
35 : #include "cpl_vsi.h"
36 : #include "cpl_safemaths.hpp"
37 : #include "gdal.h"
38 : #include "gdal_priv.h"
39 :
40 : /************************************************************************/
41 : /* RawRasterBand() */
42 : /************************************************************************/
43 :
44 938 : RawRasterBand::RawRasterBand(GDALDataset *poDSIn, int nBandIn,
45 : VSILFILE *fpRawLIn, vsi_l_offset nImgOffsetIn,
46 : int nPixelOffsetIn, int nLineOffsetIn,
47 : GDALDataType eDataTypeIn, int bNativeOrderIn,
48 938 : OwnFP bOwnsFPIn)
49 : : RawRasterBand(poDSIn, nBandIn, fpRawLIn, nImgOffsetIn, nPixelOffsetIn,
50 : nLineOffsetIn, eDataTypeIn,
51 : #ifdef CPL_LSB
52 938 : bNativeOrderIn ? ByteOrder::ORDER_LITTLE_ENDIAN
53 : : ByteOrder::ORDER_BIG_ENDIAN,
54 : #else
55 : bNativeOrderIn ? ByteOrder::ORDER_BIG_ENDIAN
56 : : ByteOrder::ORDER_LITTLE_ENDIAN,
57 : #endif
58 938 : bOwnsFPIn)
59 : {
60 938 : }
61 :
62 : /************************************************************************/
63 : /* RawRasterBand() */
64 : /************************************************************************/
65 :
66 4112 : RawRasterBand::RawRasterBand(GDALDataset *poDSIn, int nBandIn,
67 : VSILFILE *fpRawLIn, vsi_l_offset nImgOffsetIn,
68 : int nPixelOffsetIn, int nLineOffsetIn,
69 : GDALDataType eDataTypeIn, ByteOrder eByteOrderIn,
70 4112 : OwnFP bOwnsFPIn)
71 : : fpRawL(fpRawLIn), nImgOffset(nImgOffsetIn), nPixelOffset(nPixelOffsetIn),
72 : nLineOffset(nLineOffsetIn), eByteOrder(eByteOrderIn),
73 4112 : bOwnsFP(bOwnsFPIn == OwnFP::YES)
74 : {
75 4112 : poDS = poDSIn;
76 4112 : nBand = nBandIn;
77 4112 : eDataType = eDataTypeIn;
78 4112 : nRasterXSize = poDSIn->GetRasterXSize();
79 4112 : nRasterYSize = poDSIn->GetRasterYSize();
80 :
81 8224 : CPLDebug("GDALRaw",
82 : "RawRasterBand(%p,%d,%p,\n"
83 : " Off=%d,PixOff=%d,LineOff=%d,%s,%d)",
84 4112 : poDS, nBand, fpRawL, static_cast<unsigned int>(nImgOffset),
85 : nPixelOffset, nLineOffset, GDALGetDataTypeName(eDataType),
86 4112 : static_cast<int>(eByteOrder));
87 :
88 : // Treat one scanline as the block size.
89 4112 : nBlockXSize = poDS->GetRasterXSize();
90 4112 : nBlockYSize = 1;
91 :
92 : // Initialize other fields, and setup the line buffer.
93 4112 : Initialize();
94 4112 : }
95 :
96 : /************************************************************************/
97 : /* RawRasterBand::Create() */
98 : /************************************************************************/
99 :
100 : std::unique_ptr<RawRasterBand>
101 1234 : RawRasterBand::Create(GDALDataset *poDSIn, int nBandIn, VSILFILE *fpRawLIn,
102 : vsi_l_offset nImgOffsetIn, int nPixelOffsetIn,
103 : int nLineOffsetIn, GDALDataType eDataTypeIn,
104 : ByteOrder eByteOrderIn, OwnFP bOwnsFPIn)
105 : {
106 : auto poBand = std::make_unique<RawRasterBand>(
107 : poDSIn, nBandIn, fpRawLIn, nImgOffsetIn, nPixelOffsetIn, nLineOffsetIn,
108 2468 : eDataTypeIn, eByteOrderIn, bOwnsFPIn);
109 1234 : if (!poBand->IsValid())
110 0 : return nullptr;
111 1234 : return poBand;
112 : }
113 :
114 : /************************************************************************/
115 : /* RawRasterBand() */
116 : /************************************************************************/
117 :
118 9 : RawRasterBand::RawRasterBand(VSILFILE *fpRawLIn, vsi_l_offset nImgOffsetIn,
119 : int nPixelOffsetIn, int nLineOffsetIn,
120 : GDALDataType eDataTypeIn, int bNativeOrderIn,
121 9 : int nXSize, int nYSize, OwnFP bOwnsFPIn)
122 : : RawRasterBand(fpRawLIn, nImgOffsetIn, nPixelOffsetIn, nLineOffsetIn,
123 : eDataTypeIn,
124 : #ifdef CPL_LSB
125 9 : bNativeOrderIn ? ByteOrder::ORDER_LITTLE_ENDIAN
126 : : ByteOrder::ORDER_BIG_ENDIAN,
127 : #else
128 : bNativeOrderIn ? ByteOrder::ORDER_BIG_ENDIAN
129 : : ByteOrder::ORDER_LITTLE_ENDIAN,
130 : #endif
131 9 : nXSize, nYSize, bOwnsFPIn)
132 : {
133 9 : }
134 :
135 : /************************************************************************/
136 : /* RawRasterBand::Create() */
137 : /************************************************************************/
138 :
139 : std::unique_ptr<RawRasterBand>
140 31 : RawRasterBand::Create(VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
141 : int nPixelOffsetIn, int nLineOffsetIn,
142 : GDALDataType eDataTypeIn, ByteOrder eByteOrderIn,
143 : int nXSizeIn, int nYSizeIn, OwnFP bOwnsFPIn)
144 : {
145 : auto poBand = std::make_unique<RawRasterBand>(
146 : fpRawIn, nImgOffsetIn, nPixelOffsetIn, nLineOffsetIn, eDataTypeIn,
147 62 : eByteOrderIn, nXSizeIn, nYSizeIn, bOwnsFPIn);
148 31 : if (!poBand->IsValid())
149 0 : return nullptr;
150 31 : return poBand;
151 : }
152 :
153 : /************************************************************************/
154 : /* RawRasterBand() */
155 : /************************************************************************/
156 :
157 40 : RawRasterBand::RawRasterBand(VSILFILE *fpRawLIn, vsi_l_offset nImgOffsetIn,
158 : int nPixelOffsetIn, int nLineOffsetIn,
159 : GDALDataType eDataTypeIn, ByteOrder eByteOrderIn,
160 40 : int nXSize, int nYSize, OwnFP bOwnsFPIn)
161 : : fpRawL(fpRawLIn), nImgOffset(nImgOffsetIn), nPixelOffset(nPixelOffsetIn),
162 : nLineOffset(nLineOffsetIn), eByteOrder(eByteOrderIn),
163 40 : bOwnsFP(bOwnsFPIn == OwnFP::YES)
164 : {
165 40 : poDS = nullptr;
166 40 : nBand = 1;
167 40 : eDataType = eDataTypeIn;
168 :
169 80 : CPLDebug("GDALRaw",
170 : "RawRasterBand(floating,Off=%d,PixOff=%d,LineOff=%d,%s,%d)",
171 40 : static_cast<unsigned int>(nImgOffset), nPixelOffset, nLineOffset,
172 40 : GDALGetDataTypeName(eDataType), static_cast<int>(eByteOrder));
173 :
174 : // Treat one scanline as the block size.
175 40 : nBlockXSize = nXSize;
176 40 : nBlockYSize = 1;
177 40 : nRasterXSize = nXSize;
178 40 : nRasterYSize = nYSize;
179 40 : if (!GDALCheckDatasetDimensions(nXSize, nYSize))
180 : {
181 0 : return;
182 : }
183 :
184 : // Initialize other fields, and setup the line buffer.
185 40 : Initialize();
186 : }
187 :
188 : /************************************************************************/
189 : /* Initialize() */
190 : /************************************************************************/
191 :
192 4152 : void RawRasterBand::Initialize()
193 :
194 : {
195 4152 : vsi_l_offset nSmallestOffset = nImgOffset;
196 4152 : vsi_l_offset nLargestOffset = nImgOffset;
197 4152 : if (nLineOffset < 0)
198 : {
199 76 : const auto nDelta =
200 76 : static_cast<vsi_l_offset>(-static_cast<GIntBig>(nLineOffset)) *
201 76 : (nRasterYSize - 1);
202 76 : if (nDelta > nImgOffset)
203 : {
204 0 : CPLError(CE_Failure, CPLE_AppDefined,
205 : "Inconsistent nLineOffset, nRasterYSize and nImgOffset");
206 0 : return;
207 : }
208 76 : nSmallestOffset -= nDelta;
209 : }
210 : else
211 : {
212 8152 : if (nImgOffset >
213 4076 : std::numeric_limits<vsi_l_offset>::max() -
214 4076 : static_cast<vsi_l_offset>(nLineOffset) * (nRasterYSize - 1))
215 : {
216 0 : CPLError(CE_Failure, CPLE_AppDefined,
217 : "Inconsistent nLineOffset, nRasterYSize and nImgOffset");
218 0 : return;
219 : }
220 4076 : nLargestOffset +=
221 4076 : static_cast<vsi_l_offset>(nLineOffset) * (nRasterYSize - 1);
222 : }
223 4152 : if (nPixelOffset < 0)
224 : {
225 17 : if (static_cast<vsi_l_offset>(-static_cast<GIntBig>(nPixelOffset)) *
226 17 : (nRasterXSize - 1) >
227 : nSmallestOffset)
228 : {
229 0 : CPLError(CE_Failure, CPLE_AppDefined,
230 : "Inconsistent nPixelOffset, nRasterXSize and nImgOffset");
231 0 : return;
232 : }
233 : }
234 : else
235 : {
236 4135 : if (nLargestOffset >
237 4135 : std::numeric_limits<vsi_l_offset>::max() -
238 4135 : static_cast<vsi_l_offset>(nPixelOffset) * (nRasterXSize - 1))
239 : {
240 0 : CPLError(CE_Failure, CPLE_AppDefined,
241 : "Inconsistent nPixelOffset, nRasterXSize and nImgOffset");
242 0 : return;
243 : }
244 4135 : nLargestOffset +=
245 4135 : static_cast<vsi_l_offset>(nPixelOffset) * (nRasterXSize - 1);
246 : }
247 4152 : if (nLargestOffset > static_cast<vsi_l_offset>(GINTBIG_MAX))
248 : {
249 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too big largest offset");
250 0 : return;
251 : }
252 :
253 4152 : const int nDTSize = GDALGetDataTypeSizeBytes(GetRasterDataType());
254 :
255 : // Allocate working scanline.
256 4152 : const bool bIsBIP = IsBIP();
257 4152 : if (bIsBIP)
258 : {
259 1250 : if (nBand == 1)
260 : {
261 187 : nLineSize = nPixelOffset * nBlockXSize;
262 187 : pLineBuffer = VSIMalloc(nLineSize);
263 : }
264 : else
265 : {
266 : // Band > 1 : share the same buffer as band 1
267 1063 : pLineBuffer = nullptr;
268 : const auto poFirstBand =
269 1063 : cpl::down_cast<RawRasterBand *>(poDS->GetRasterBand(1));
270 1063 : if (poFirstBand->pLineBuffer != nullptr)
271 1063 : pLineStart = static_cast<char *>(poFirstBand->pLineBuffer) +
272 1063 : (nBand - 1) * nDTSize;
273 1063 : return;
274 : }
275 : }
276 8706 : else if (nBlockXSize <= 0 ||
277 2902 : (nBlockXSize > 1 &&
278 2406 : std::abs(nPixelOffset) >
279 8210 : std::numeric_limits<int>::max() / (nBlockXSize - 1)) ||
280 2902 : std::abs(nPixelOffset) * (nBlockXSize - 1) >
281 2902 : std::numeric_limits<int>::max() - nDTSize)
282 : {
283 0 : nLineSize = 0;
284 0 : pLineBuffer = nullptr;
285 : }
286 : else
287 : {
288 2902 : nLineSize = std::abs(nPixelOffset) * (nBlockXSize - 1) + nDTSize;
289 2902 : pLineBuffer = VSIMalloc(nLineSize);
290 : }
291 :
292 3089 : if (pLineBuffer == nullptr)
293 : {
294 0 : nLineSize = 0;
295 0 : CPLError(CE_Failure, CPLE_AppDefined,
296 : "Could not allocate line buffer: "
297 : "nPixelOffset=%d, nBlockXSize=%d",
298 : nPixelOffset, nBlockXSize);
299 0 : return;
300 : }
301 :
302 3089 : if (nPixelOffset >= 0)
303 3072 : pLineStart = pLineBuffer;
304 : else
305 17 : pLineStart = static_cast<char *>(pLineBuffer) +
306 17 : static_cast<std::ptrdiff_t>(std::abs(nPixelOffset)) *
307 17 : (nBlockXSize - 1);
308 : }
309 :
310 : /************************************************************************/
311 : /* ~RawRasterBand() */
312 : /************************************************************************/
313 :
314 5421 : RawRasterBand::~RawRasterBand()
315 :
316 : {
317 4152 : if (poCT)
318 4 : delete poCT;
319 :
320 4152 : CSLDestroy(papszCategoryNames);
321 :
322 4152 : RawRasterBand::FlushCache(true);
323 :
324 4152 : if (bOwnsFP)
325 : {
326 20 : if (VSIFCloseL(fpRawL) != 0)
327 : {
328 0 : CPLError(CE_Failure, CPLE_FileIO, "I/O error");
329 : }
330 : }
331 :
332 4152 : CPLFree(pLineBuffer);
333 5421 : }
334 :
335 : /************************************************************************/
336 : /* IsBIP() */
337 : /************************************************************************/
338 :
339 21697 : bool RawRasterBand::IsBIP() const
340 : {
341 21697 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
342 21697 : const bool bIsRawDataset = dynamic_cast<RawDataset *>(poDS) != nullptr;
343 21697 : if (bIsRawDataset && nPixelOffset > nDTSize &&
344 6587 : nLineOffset == static_cast<int64_t>(nPixelOffset) * nRasterXSize)
345 : {
346 6587 : if (nBand == 1)
347 : {
348 710 : return true;
349 : }
350 : const auto poFirstBand =
351 5877 : dynamic_cast<RawRasterBand *>(poDS->GetRasterBand(1));
352 5877 : if (poFirstBand && eDataType == poFirstBand->eDataType &&
353 5877 : eByteOrder == poFirstBand->eByteOrder &&
354 5877 : nPixelOffset == poFirstBand->nPixelOffset &&
355 5877 : nLineOffset == poFirstBand->nLineOffset &&
356 5877 : nImgOffset == poFirstBand->nImgOffset +
357 5877 : static_cast<vsi_l_offset>(nBand - 1) * nDTSize)
358 : {
359 5877 : return true;
360 : }
361 : }
362 15110 : return false;
363 : }
364 :
365 : /************************************************************************/
366 : /* SetAccess() */
367 : /************************************************************************/
368 :
369 265 : void RawRasterBand::SetAccess(GDALAccess eAccessIn)
370 : {
371 265 : eAccess = eAccessIn;
372 265 : }
373 :
374 : /************************************************************************/
375 : /* FlushCache() */
376 : /* */
377 : /* We override this so we have the opportunity to call */
378 : /* fflush(). We don't want to do this all the time in the */
379 : /* write block function as it is kind of expensive. */
380 : /************************************************************************/
381 :
382 8647 : CPLErr RawRasterBand::FlushCache(bool bAtClosing)
383 :
384 : {
385 8647 : if (bAtClosing)
386 : {
387 8253 : if (bFlushCacheAtClosingHasRun)
388 4101 : return CE_None;
389 4152 : bFlushCacheAtClosingHasRun = true;
390 : }
391 4546 : CPLErr eErr = GDALRasterBand::FlushCache(bAtClosing);
392 4546 : if (eErr != CE_None)
393 : {
394 0 : bNeedFileFlush = false;
395 0 : return eErr;
396 : }
397 :
398 4546 : RawRasterBand *masterBand = this;
399 4546 : if (nBand > 1 && poDS != nullptr && poDS->GetRasterCount() > 1 && IsBIP())
400 : {
401 : // can't be null as IsBIP() checks that the first band is not null,
402 : // which could happen during dataset destruction.
403 1099 : masterBand = cpl::down_cast<RawRasterBand *>(poDS->GetRasterBand(1));
404 : }
405 :
406 4546 : if (!masterBand->FlushCurrentLine(false))
407 : {
408 0 : masterBand->bNeedFileFlush = false;
409 0 : bNeedFileFlush = false;
410 0 : return CE_Failure;
411 : }
412 :
413 : // If we have unflushed raw, flush it to disk now.
414 4546 : if (masterBand->bNeedFileFlush)
415 : {
416 346 : int nRet = VSIFFlushL(fpRawL);
417 :
418 346 : masterBand->bNeedFileFlush = false;
419 346 : bNeedFileFlush = false;
420 346 : if (nRet < 0)
421 0 : return CE_Failure;
422 : }
423 :
424 4546 : bNeedFileFlush = false;
425 :
426 4546 : return CE_None;
427 : }
428 :
429 : /************************************************************************/
430 : /* NeedsByteOrderChange() */
431 : /************************************************************************/
432 :
433 76097 : bool RawRasterBand::NeedsByteOrderChange() const
434 : {
435 : #ifdef CPL_LSB
436 86923 : return eDataType != GDT_UInt8 &&
437 86923 : eByteOrder != RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
438 : #else
439 : return eDataType != GDT_UInt8 &&
440 : eByteOrder != RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
441 : #endif
442 : }
443 :
444 : /************************************************************************/
445 : /* DoByteSwap() */
446 : /************************************************************************/
447 :
448 6102 : void RawRasterBand::DoByteSwap(void *pBuffer, size_t nValues, int nByteSkip,
449 : bool bDiskToCPU) const
450 : {
451 6102 : if (eByteOrder != RawRasterBand::ByteOrder::ORDER_VAX)
452 : {
453 6063 : if (GDALDataTypeIsComplex(eDataType))
454 : {
455 0 : const int nWordSize = GDALGetDataTypeSizeBytes(eDataType) / 2;
456 0 : GDALSwapWordsEx(pBuffer, nWordSize, nValues, nByteSkip);
457 0 : GDALSwapWordsEx(static_cast<GByte *>(pBuffer) + nWordSize,
458 : nWordSize, nValues, nByteSkip);
459 : }
460 : else
461 : {
462 6063 : GDALSwapWordsEx(pBuffer, GDALGetDataTypeSizeBytes(eDataType),
463 : nValues, nByteSkip);
464 : }
465 : }
466 39 : else if (eDataType == GDT_Float16 || eDataType == GDT_CFloat16)
467 : {
468 : // No VAX support for GFloat16
469 0 : std::abort();
470 : }
471 39 : else if (eDataType == GDT_Float32 || eDataType == GDT_CFloat32)
472 : {
473 30 : GByte *pPtr = static_cast<GByte *>(pBuffer);
474 39 : for (int k = 0; k < 2; k++)
475 : {
476 39 : if (bDiskToCPU)
477 : {
478 228 : for (size_t i = 0; i < nValues; i++, pPtr += nByteSkip)
479 : {
480 192 : CPLVaxToIEEEFloat(pPtr);
481 : }
482 : }
483 : else
484 : {
485 39 : for (size_t i = 0; i < nValues; i++, pPtr += nByteSkip)
486 : {
487 36 : CPLIEEEToVaxFloat(pPtr);
488 : }
489 : }
490 39 : if (k == 0 && eDataType == GDT_CFloat32)
491 9 : pPtr = static_cast<GByte *>(pBuffer) + sizeof(float);
492 : else
493 : break;
494 30 : }
495 : }
496 9 : else if (eDataType == GDT_Float64 || eDataType == GDT_CFloat64)
497 : {
498 9 : GByte *pPtr = static_cast<GByte *>(pBuffer);
499 9 : for (int k = 0; k < 2; k++)
500 : {
501 9 : if (bDiskToCPU)
502 : {
503 56 : for (size_t i = 0; i < nValues; i++, pPtr += nByteSkip)
504 : {
505 48 : CPLVaxToIEEEDouble(pPtr);
506 : }
507 : }
508 : else
509 : {
510 13 : for (size_t i = 0; i < nValues; i++, pPtr += nByteSkip)
511 : {
512 12 : CPLIEEEToVaxDouble(pPtr);
513 : }
514 : }
515 9 : if (k == 0 && eDataType == GDT_CFloat64)
516 0 : pPtr = static_cast<GByte *>(pBuffer) + sizeof(double);
517 : else
518 : break;
519 : }
520 : }
521 6102 : }
522 :
523 : /************************************************************************/
524 : /* ComputeFileOffset() */
525 : /************************************************************************/
526 :
527 47567 : vsi_l_offset RawRasterBand::ComputeFileOffset(int iLine) const
528 : {
529 : // Write formulas such that unsigned int overflow doesn't occur
530 47567 : vsi_l_offset nOffset = nImgOffset;
531 47567 : if (nLineOffset >= 0)
532 : {
533 46033 : nOffset += static_cast<GUIntBig>(nLineOffset) * iLine;
534 : }
535 : else
536 : {
537 1534 : nOffset -=
538 1534 : static_cast<GUIntBig>(-static_cast<GIntBig>(nLineOffset)) * iLine;
539 : }
540 47567 : if (nPixelOffset < 0)
541 : {
542 24 : const GUIntBig nPixelOffsetToSubtract =
543 24 : static_cast<GUIntBig>(-static_cast<GIntBig>(nPixelOffset)) *
544 24 : (nBlockXSize - 1);
545 24 : nOffset -= nPixelOffsetToSubtract;
546 : }
547 47567 : return nOffset;
548 : }
549 :
550 : /************************************************************************/
551 : /* AccessLine() */
552 : /************************************************************************/
553 :
554 30476 : CPLErr RawRasterBand::AccessLine(int iLine)
555 :
556 : {
557 30476 : if (pLineBuffer == nullptr)
558 : {
559 300 : if (nBand > 1 && pLineStart != nullptr)
560 : {
561 : // BIP interleaved
562 : auto poFirstBand =
563 300 : cpl::down_cast<RawRasterBand *>(poDS->GetRasterBand(1));
564 300 : CPLAssert(poFirstBand);
565 300 : return poFirstBand->AccessLine(iLine);
566 : }
567 0 : return CE_Failure;
568 : }
569 :
570 30176 : if (nLoadedScanline == iLine)
571 : {
572 0 : return CE_None;
573 : }
574 :
575 30176 : if (!FlushCurrentLine(false))
576 : {
577 0 : return CE_Failure;
578 : }
579 :
580 : // Figure out where to start reading.
581 30176 : const vsi_l_offset nReadStart = ComputeFileOffset(iLine);
582 :
583 : // Seek to the correct line.
584 30176 : if (Seek(nReadStart, SEEK_SET) == -1)
585 : {
586 0 : if (poDS != nullptr && poDS->GetAccess() == GA_ReadOnly)
587 : {
588 0 : CPLError(CE_Failure, CPLE_FileIO,
589 : "Failed to seek to scanline %d @ " CPL_FRMT_GUIB ".",
590 : iLine, nReadStart);
591 0 : return CE_Failure;
592 : }
593 : else
594 : {
595 0 : memset(pLineBuffer, 0, nLineSize);
596 0 : nLoadedScanline = iLine;
597 0 : return CE_None;
598 : }
599 : }
600 :
601 : // Read the line. Take care not to request any more bytes than
602 : // are needed, and not to lose a partially successful scanline read.
603 30176 : const size_t nBytesToRead = nLineSize;
604 30176 : const size_t nBytesActuallyRead = Read(pLineBuffer, 1, nBytesToRead);
605 30176 : if (nBytesActuallyRead < nBytesToRead)
606 : {
607 6240 : if (!bTruncatedFileAllowed)
608 : {
609 6 : CPLError(CE_Failure, CPLE_FileIO, "Failed to read scanline %d.",
610 : iLine);
611 6 : return CE_Failure;
612 : }
613 : else
614 : {
615 6234 : memset(static_cast<GByte *>(pLineBuffer) + nBytesActuallyRead, 0,
616 : nBytesToRead - nBytesActuallyRead);
617 : }
618 : }
619 :
620 : // Byte swap the interesting data, if required.
621 30170 : if (NeedsByteOrderChange())
622 : {
623 2299 : if (poDS != nullptr && poDS->GetRasterCount() > 1 && IsBIP())
624 : {
625 0 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
626 0 : DoByteSwap(pLineBuffer,
627 0 : static_cast<size_t>(nBlockXSize) *
628 0 : poDS->GetRasterCount(),
629 : nDTSize, true);
630 : }
631 : else
632 2299 : DoByteSwap(pLineBuffer, nBlockXSize, std::abs(nPixelOffset), true);
633 : }
634 :
635 30170 : nLoadedScanline = iLine;
636 :
637 30170 : return CE_None;
638 : }
639 :
640 : /************************************************************************/
641 : /* IReadBlock() */
642 : /************************************************************************/
643 :
644 27118 : CPLErr RawRasterBand::IReadBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
645 : void *pImage)
646 : {
647 27118 : CPLAssert(nBlockXOff == 0);
648 :
649 27118 : const CPLErr eErr = AccessLine(nBlockYOff);
650 27118 : if (eErr == CE_Failure)
651 6 : return eErr;
652 :
653 : // Copy data from disk buffer to user block buffer.
654 27112 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
655 27112 : GDALCopyWords64(pLineStart, eDataType, nPixelOffset, pImage, eDataType,
656 27112 : nDTSize, nBlockXSize);
657 :
658 : // Pre-cache block cache of other bands
659 27112 : if (poDS != nullptr && poDS->GetRasterCount() > 1 && IsBIP())
660 : {
661 4262 : for (int iBand = 1; iBand <= poDS->GetRasterCount(); iBand++)
662 : {
663 3439 : if (iBand != nBand)
664 : {
665 : auto poOtherBand =
666 2616 : cpl::down_cast<RawRasterBand *>(poDS->GetRasterBand(iBand));
667 : GDALRasterBlock *poBlock =
668 2616 : poOtherBand->TryGetLockedBlockRef(0, nBlockYOff);
669 2616 : if (poBlock != nullptr)
670 : {
671 0 : poBlock->DropLock();
672 0 : continue;
673 : }
674 2616 : poBlock = poOtherBand->GetLockedBlockRef(0, nBlockYOff, true);
675 2616 : if (poBlock != nullptr)
676 : {
677 2616 : GDALCopyWords64(poOtherBand->pLineStart, eDataType,
678 : nPixelOffset, poBlock->GetDataRef(),
679 2616 : eDataType, nDTSize, nBlockXSize);
680 2616 : poBlock->DropLock();
681 : }
682 : }
683 : }
684 : }
685 :
686 27112 : return eErr;
687 : }
688 :
689 : /************************************************************************/
690 : /* BIPWriteBlock() */
691 : /************************************************************************/
692 :
693 2014 : CPLErr RawRasterBand::BIPWriteBlock(int nBlockYOff, int nCallingBand,
694 : const void *pImage)
695 : {
696 2014 : if (nLoadedScanline != nBlockYOff)
697 : {
698 2014 : if (!FlushCurrentLine(false))
699 0 : return CE_Failure;
700 : }
701 :
702 2014 : const int nBands = poDS->GetRasterCount();
703 4028 : std::vector<GDALRasterBlock *> apoBlocks(nBands);
704 2014 : bool bAllBlocksDirty = true;
705 2014 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
706 :
707 : /* -------------------------------------------------------------------- */
708 : /* If all blocks are cached and dirty then we do not need to reload */
709 : /* the scanline from disk */
710 : /* -------------------------------------------------------------------- */
711 104086 : for (int iBand = 0; iBand < nBands; ++iBand)
712 : {
713 102072 : if (iBand + 1 != nCallingBand)
714 : {
715 200116 : apoBlocks[iBand] =
716 100058 : cpl::down_cast<RawRasterBand *>(poDS->GetRasterBand(iBand + 1))
717 100058 : ->TryGetLockedBlockRef(0, nBlockYOff);
718 :
719 100058 : if (apoBlocks[iBand] == nullptr)
720 : {
721 99034 : bAllBlocksDirty = false;
722 : }
723 1024 : else if (!apoBlocks[iBand]->GetDirty())
724 : {
725 0 : apoBlocks[iBand]->DropLock();
726 0 : apoBlocks[iBand] = nullptr;
727 0 : bAllBlocksDirty = false;
728 : }
729 : }
730 : else
731 2014 : apoBlocks[iBand] = nullptr;
732 : }
733 :
734 2014 : if (!bAllBlocksDirty)
735 : {
736 : // We only to read the scanline if we don't have data for all bands.
737 2014 : if (AccessLine(nBlockYOff) != CE_None)
738 : {
739 0 : for (int iBand = 0; iBand < nBands; ++iBand)
740 : {
741 0 : if (apoBlocks[iBand] != nullptr)
742 0 : apoBlocks[iBand]->DropLock();
743 : }
744 0 : return CE_Failure;
745 : }
746 : }
747 :
748 104086 : for (int iBand = 0; iBand < nBands; ++iBand)
749 : {
750 102072 : const GByte *pabyThisImage = nullptr;
751 102072 : GDALRasterBlock *poBlock = nullptr;
752 :
753 102072 : if (iBand + 1 == nCallingBand)
754 : {
755 2014 : pabyThisImage = static_cast<const GByte *>(pImage);
756 : }
757 : else
758 : {
759 100058 : poBlock = apoBlocks[iBand];
760 100058 : if (poBlock == nullptr)
761 99034 : continue;
762 :
763 1024 : if (!poBlock->GetDirty())
764 : {
765 0 : poBlock->DropLock();
766 0 : continue;
767 : }
768 :
769 1024 : pabyThisImage = static_cast<const GByte *>(poBlock->GetDataRef());
770 : }
771 :
772 3038 : GByte *pabyOut = static_cast<GByte *>(pLineStart) + iBand * nDTSize;
773 :
774 3038 : GDALCopyWords64(pabyThisImage, eDataType, nDTSize, pabyOut, eDataType,
775 3038 : nPixelOffset, nBlockXSize);
776 :
777 3038 : if (poBlock != nullptr)
778 : {
779 1024 : poBlock->MarkClean();
780 1024 : poBlock->DropLock();
781 : }
782 : }
783 :
784 2014 : nLoadedScanline = nBlockYOff;
785 2014 : bLoadedScanlineDirty = true;
786 :
787 2014 : if (bAllBlocksDirty)
788 : {
789 0 : return FlushCurrentLine(true) ? CE_None : CE_Failure;
790 : }
791 :
792 2014 : bNeedFileFlush = true;
793 2014 : return CE_None;
794 : }
795 :
796 : /************************************************************************/
797 : /* IWriteBlock() */
798 : /************************************************************************/
799 :
800 17391 : CPLErr RawRasterBand::IWriteBlock(CPL_UNUSED int nBlockXOff, int nBlockYOff,
801 : void *pImage)
802 : {
803 17391 : CPLAssert(nBlockXOff == 0);
804 :
805 17391 : if (pLineBuffer == nullptr)
806 : {
807 2014 : if (poDS != nullptr && poDS->GetRasterCount() > 1 && IsBIP())
808 : {
809 : auto poFirstBand =
810 2014 : (nBand == 1)
811 2014 : ? this
812 2014 : : cpl::down_cast<RawRasterBand *>(poDS->GetRasterBand(1));
813 2014 : CPLAssert(poFirstBand);
814 2014 : return poFirstBand->BIPWriteBlock(nBlockYOff, nBand, pImage);
815 : }
816 :
817 0 : return CE_Failure;
818 : }
819 :
820 15377 : if (nLoadedScanline != nBlockYOff)
821 : {
822 15374 : if (!FlushCurrentLine(false))
823 0 : return CE_Failure;
824 : }
825 :
826 : // If the data for this band is completely contiguous, we don't
827 : // have to worry about pre-reading from disk.
828 15377 : CPLErr eErr = CE_None;
829 15377 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
830 15377 : if (std::abs(nPixelOffset) > nDTSize)
831 1044 : eErr = AccessLine(nBlockYOff);
832 :
833 : // Copy data from user buffer into disk buffer.
834 15377 : GDALCopyWords64(pImage, eDataType, nDTSize, pLineStart, eDataType,
835 15377 : nPixelOffset, nBlockXSize);
836 :
837 15377 : nLoadedScanline = nBlockYOff;
838 15377 : bLoadedScanlineDirty = true;
839 :
840 15377 : return eErr == CE_None && FlushCurrentLine(true) ? CE_None : CE_Failure;
841 : }
842 :
843 : /************************************************************************/
844 : /* FlushCurrentLine() */
845 : /************************************************************************/
846 :
847 67487 : bool RawRasterBand::FlushCurrentLine(bool bNeedUsableBufferAfter)
848 : {
849 67487 : if (!bLoadedScanlineDirty)
850 50096 : return true;
851 17391 : assert(pLineBuffer);
852 :
853 17391 : bLoadedScanlineDirty = false;
854 :
855 17391 : bool ok = true;
856 :
857 : // Byte swap (if necessary) back into disk order before writing.
858 17391 : if (NeedsByteOrderChange())
859 : {
860 1509 : if (poDS != nullptr && poDS->GetRasterCount() > 1 && IsBIP())
861 : {
862 0 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
863 0 : DoByteSwap(pLineBuffer,
864 0 : static_cast<size_t>(nBlockXSize) *
865 0 : poDS->GetRasterCount(),
866 : nDTSize, false);
867 : }
868 : else
869 1509 : DoByteSwap(pLineBuffer, nBlockXSize, std::abs(nPixelOffset), false);
870 : }
871 :
872 : // Figure out where to start reading.
873 17391 : const vsi_l_offset nWriteStart = ComputeFileOffset(nLoadedScanline);
874 :
875 : // Seek to correct location.
876 17391 : if (Seek(nWriteStart, SEEK_SET) == -1)
877 : {
878 0 : CPLError(CE_Failure, CPLE_FileIO,
879 : "Failed to seek to scanline %d @ " CPL_FRMT_GUIB
880 : " to write to file.",
881 : nLoadedScanline, nWriteStart);
882 :
883 0 : ok = false;
884 : }
885 :
886 : // Write data buffer.
887 17391 : const int nBytesToWrite = nLineSize;
888 34782 : if (ok && Write(pLineBuffer, 1, nBytesToWrite) <
889 17391 : static_cast<size_t>(nBytesToWrite))
890 : {
891 0 : CPLError(CE_Failure, CPLE_FileIO,
892 : "Failed to write scanline %d to file.", nLoadedScanline);
893 :
894 0 : ok = false;
895 : }
896 :
897 : // Byte swap (if necessary) back into machine order so the
898 : // buffer is still usable for reading purposes, unless this is not needed.
899 17391 : if (bNeedUsableBufferAfter && NeedsByteOrderChange())
900 : {
901 1509 : if (poDS != nullptr && poDS->GetRasterCount() > 1 && IsBIP())
902 : {
903 0 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
904 0 : DoByteSwap(pLineBuffer,
905 0 : static_cast<size_t>(nBlockXSize) *
906 0 : poDS->GetRasterCount(),
907 : nDTSize, true);
908 : }
909 : else
910 1509 : DoByteSwap(pLineBuffer, nBlockXSize, std::abs(nPixelOffset), true);
911 : }
912 :
913 17391 : bNeedFileFlush = true;
914 :
915 17391 : return ok;
916 : }
917 :
918 : /************************************************************************/
919 : /* AccessBlock() */
920 : /************************************************************************/
921 :
922 8188 : CPLErr RawRasterBand::AccessBlock(vsi_l_offset nBlockOff, size_t nBlockSize,
923 : void *pData, size_t nValues)
924 : {
925 : // Seek to the correct block.
926 8188 : if (Seek(nBlockOff, SEEK_SET) == -1)
927 : {
928 0 : memset(pData, 0, nBlockSize);
929 0 : return CE_None;
930 : }
931 :
932 : // Read the block.
933 8188 : const size_t nBytesActuallyRead = Read(pData, 1, nBlockSize);
934 8188 : if (nBytesActuallyRead < nBlockSize)
935 : {
936 464 : if (!bTruncatedFileAllowed)
937 : {
938 109 : CPLError(CE_Failure, CPLE_FileIO,
939 : "Failed to read block at offset %" PRIu64,
940 : static_cast<uint64_t>(nBlockOff));
941 109 : return CE_Failure;
942 : }
943 : else
944 : {
945 355 : memset(static_cast<GByte *>(pData) + nBytesActuallyRead, 0,
946 : nBlockSize - nBytesActuallyRead);
947 : }
948 : }
949 :
950 : // Byte swap the interesting data, if required.
951 8079 : if (NeedsByteOrderChange())
952 : {
953 707 : DoByteSwap(pData, nValues, std::abs(nPixelOffset), true);
954 : }
955 :
956 8079 : return CE_None;
957 : }
958 :
959 : /************************************************************************/
960 : /* IsSignificantNumberOfLinesLoaded() */
961 : /* */
962 : /* Check if there is a significant number of scanlines (>20%) from the */
963 : /* specified block of lines already cached. */
964 : /************************************************************************/
965 :
966 0 : int RawRasterBand::IsSignificantNumberOfLinesLoaded(int nLineOff, int nLines)
967 : {
968 0 : int nCountLoaded = 0;
969 :
970 0 : for (int iLine = nLineOff; iLine < nLineOff + nLines; iLine++)
971 : {
972 0 : GDALRasterBlock *poBlock = TryGetLockedBlockRef(0, iLine);
973 0 : if (poBlock != nullptr)
974 : {
975 0 : poBlock->DropLock();
976 0 : nCountLoaded++;
977 0 : if (nCountLoaded > nLines / 20)
978 : {
979 0 : return TRUE;
980 : }
981 : }
982 : }
983 :
984 0 : return FALSE;
985 : }
986 :
987 : /************************************************************************/
988 : /* CanUseDirectIO() */
989 : /************************************************************************/
990 :
991 13360 : int RawRasterBand::CanUseDirectIO(int /* nXOff */, int nYOff, int nXSize,
992 : int nYSize, GDALDataType /* eBufType*/,
993 : GDALRasterIOExtraArg *psExtraArg)
994 : {
995 13360 : bool result = FALSE;
996 :
997 : // Use direct IO without caching if:
998 : //
999 : // GDAL_ONE_BIG_READ is enabled
1000 : //
1001 : // or
1002 : //
1003 : // the raster width is so small that the cost of a GDALRasterBlock is
1004 : // significant
1005 : //
1006 : // or
1007 : //
1008 : // the length of a scanline on disk is more than 50000 bytes, and the
1009 : // width of the requested chunk is less than 40% of the whole scanline and
1010 : // no significant number of requested scanlines are already in the cache.
1011 :
1012 13360 : if (nPixelOffset < 0 || psExtraArg->eResampleAlg != GRIORA_NearestNeighbour)
1013 : {
1014 5 : return FALSE;
1015 : }
1016 :
1017 13355 : RawDataset *rawDataset = dynamic_cast<RawDataset *>(this->GetDataset());
1018 13355 : int oldCachedCPLOneBigReadOption = 0;
1019 13355 : if (rawDataset != nullptr)
1020 : {
1021 12895 : oldCachedCPLOneBigReadOption = rawDataset->cachedCPLOneBigReadOption;
1022 : }
1023 :
1024 : const char *pszGDAL_ONE_BIG_READ =
1025 13355 : !(oldCachedCPLOneBigReadOption & 0xff) // Test valid
1026 25319 : ? CPLGetConfigOption("GDAL_ONE_BIG_READ", nullptr)
1027 23928 : : (((oldCachedCPLOneBigReadOption >> 8) & 0xff) == 0) ? "0"
1028 11964 : : (((oldCachedCPLOneBigReadOption >> 8) & 0xff) == 1) ? "1"
1029 13355 : : nullptr;
1030 13355 : if (pszGDAL_ONE_BIG_READ == nullptr)
1031 : {
1032 13289 : const int newCachedCPLOneBigReadOption = (0xff << 8) | 1;
1033 13289 : if (rawDataset != nullptr)
1034 : {
1035 12829 : rawDataset->cachedCPLOneBigReadOption.compare_exchange_strong(
1036 : oldCachedCPLOneBigReadOption, newCachedCPLOneBigReadOption);
1037 : }
1038 :
1039 13289 : if (nRasterXSize <= 64)
1040 : {
1041 6015 : return TRUE;
1042 : }
1043 :
1044 7274 : if (nLineSize < 50000 || nXSize > nLineSize / nPixelOffset / 5 * 2 ||
1045 0 : IsSignificantNumberOfLinesLoaded(nYOff, nYSize))
1046 : {
1047 7274 : return FALSE;
1048 : }
1049 0 : return TRUE;
1050 : }
1051 :
1052 66 : result = CPLTestBool(pszGDAL_ONE_BIG_READ);
1053 :
1054 66 : const int newCachedCPLOneBigReadOption = (result ? 1 : 0) << 8 | 1;
1055 66 : if (rawDataset != nullptr)
1056 : {
1057 66 : rawDataset->cachedCPLOneBigReadOption.compare_exchange_strong(
1058 : oldCachedCPLOneBigReadOption, newCachedCPLOneBigReadOption);
1059 : }
1060 :
1061 66 : return result;
1062 : }
1063 :
1064 : /************************************************************************/
1065 : /* IRasterIO() */
1066 : /************************************************************************/
1067 :
1068 13289 : CPLErr RawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1069 : int nXSize, int nYSize, void *pData,
1070 : int nBufXSize, int nBufYSize,
1071 : GDALDataType eBufType, GSpacing nPixelSpace,
1072 : GSpacing nLineSpace,
1073 : GDALRasterIOExtraArg *psExtraArg)
1074 :
1075 : {
1076 13289 : const int nBandDataSize = GDALGetDataTypeSizeBytes(eDataType);
1077 : #ifdef DEBUG
1078 : // Otherwise Coverity thinks that a divide by zero is possible in
1079 : // AccessBlock() in the complex data type wapping case.
1080 13289 : if (nBandDataSize == 0)
1081 0 : return CE_Failure;
1082 : #endif
1083 13289 : const int nBufDataSize = GDALGetDataTypeSizeBytes(eBufType);
1084 :
1085 13289 : if (!CanUseDirectIO(nXOff, nYOff, nXSize, nYSize, eBufType, psExtraArg))
1086 : {
1087 7279 : return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1088 : pData, nBufXSize, nBufYSize, eBufType,
1089 7279 : nPixelSpace, nLineSpace, psExtraArg);
1090 : }
1091 :
1092 6010 : CPLDebug("RAW", "Using direct IO implementation");
1093 :
1094 6010 : if (pLineBuffer == nullptr)
1095 : {
1096 1401 : if (poDS != nullptr && poDS->GetRasterCount() > 1 && IsBIP())
1097 : {
1098 : auto poFirstBand =
1099 1401 : (nBand == 1)
1100 1401 : ? this
1101 1401 : : cpl::down_cast<RawRasterBand *>(poDS->GetRasterBand(1));
1102 1401 : CPLAssert(poFirstBand);
1103 1401 : if (poFirstBand->bNeedFileFlush)
1104 17 : RawRasterBand::FlushCache(false);
1105 : }
1106 : }
1107 6010 : if (bNeedFileFlush)
1108 4 : RawRasterBand::FlushCache(false);
1109 :
1110 : // Needed for ICC fast math approximations
1111 6010 : constexpr double EPS = 1e-10;
1112 :
1113 : // Read data.
1114 6010 : if (eRWFlag == GF_Read)
1115 : {
1116 : // Do we have overviews that are appropriate to satisfy this request?
1117 5560 : if ((nBufXSize < nXSize || nBufYSize < nYSize) &&
1118 2 : GetOverviewCount() > 0)
1119 : {
1120 0 : if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1121 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
1122 0 : nLineSpace, psExtraArg) == CE_None)
1123 0 : return CE_None;
1124 : }
1125 :
1126 : // 1. Simplest case when we should get contiguous block
1127 : // of uninterleaved pixels.
1128 11109 : if (nXSize == GetXSize() && nXSize == nBufXSize &&
1129 5549 : nYSize == nBufYSize && eBufType == eDataType &&
1130 501 : nPixelOffset == nBandDataSize && nPixelSpace == nBufDataSize &&
1131 11582 : nLineSpace == nPixelSpace * nXSize &&
1132 473 : nLineOffset == nPixelOffset * nXSize)
1133 : {
1134 441 : vsi_l_offset nOffset = nImgOffset;
1135 441 : if (nLineOffset >= 0)
1136 441 : nOffset += nYOff * static_cast<vsi_l_offset>(nLineOffset);
1137 : else
1138 0 : nOffset -= nYOff * static_cast<vsi_l_offset>(-nLineOffset);
1139 :
1140 441 : const size_t nValues = static_cast<size_t>(nXSize) * nYSize;
1141 441 : const size_t nBytesToRead = nValues * nBandDataSize;
1142 441 : AccessBlock(nOffset, nBytesToRead, pData, nValues);
1143 : }
1144 : // 2. Case when we need deinterleave and/or subsample data.
1145 : else
1146 : {
1147 5117 : const double dfSrcXInc = static_cast<double>(nXSize) / nBufXSize;
1148 5117 : const double dfSrcYInc = static_cast<double>(nYSize) / nBufYSize;
1149 :
1150 : const size_t nBytesToRW =
1151 5117 : static_cast<size_t>(nPixelOffset) * (nXSize - 1) +
1152 5117 : GDALGetDataTypeSizeBytes(eDataType);
1153 : GByte *pabyData =
1154 5117 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBytesToRW));
1155 5117 : if (pabyData == nullptr)
1156 0 : return CE_Failure;
1157 :
1158 11491 : for (int iLine = 0; iLine < nBufYSize; iLine++)
1159 : {
1160 6374 : const vsi_l_offset nLine =
1161 6374 : static_cast<vsi_l_offset>(nYOff) +
1162 6374 : static_cast<vsi_l_offset>(iLine * dfSrcYInc + EPS);
1163 6374 : vsi_l_offset nOffset = nImgOffset;
1164 6374 : if (nLineOffset >= 0)
1165 6215 : nOffset += nLine * nLineOffset;
1166 : else
1167 159 : nOffset -= nLine * static_cast<vsi_l_offset>(-nLineOffset);
1168 6374 : if (nPixelOffset >= 0)
1169 6374 : nOffset += nXOff * static_cast<vsi_l_offset>(nPixelOffset);
1170 : else
1171 0 : nOffset -= nXOff * static_cast<vsi_l_offset>(-nPixelOffset);
1172 6374 : AccessBlock(nOffset, nBytesToRW, pabyData, nXSize);
1173 : // Copy data from disk buffer to user block buffer and
1174 : // subsample, if needed.
1175 6374 : if (nXSize == nBufXSize && nYSize == nBufYSize)
1176 : {
1177 6347 : GDALCopyWords64(
1178 : pabyData, eDataType, nPixelOffset,
1179 6347 : static_cast<GByte *>(pData) + iLine * nLineSpace,
1180 : eBufType, static_cast<int>(nPixelSpace), nXSize);
1181 : }
1182 : else
1183 : {
1184 262 : for (int iPixel = 0; iPixel < nBufXSize; iPixel++)
1185 : {
1186 235 : GDALCopyWords64(
1187 235 : pabyData + static_cast<vsi_l_offset>(
1188 235 : iPixel * dfSrcXInc + EPS) *
1189 235 : nPixelOffset,
1190 : eDataType, nPixelOffset,
1191 235 : static_cast<GByte *>(pData) + iLine * nLineSpace +
1192 235 : iPixel * nPixelSpace,
1193 : eBufType, static_cast<int>(nPixelSpace), 1);
1194 : }
1195 : }
1196 :
1197 6849 : if (psExtraArg->pfnProgress != nullptr &&
1198 475 : !psExtraArg->pfnProgress(1.0 * (iLine + 1) / nBufYSize, "",
1199 : psExtraArg->pProgressData))
1200 : {
1201 0 : CPLFree(pabyData);
1202 0 : return CE_Failure;
1203 : }
1204 : }
1205 :
1206 5117 : CPLFree(pabyData);
1207 : }
1208 : }
1209 : // Write data.
1210 : else
1211 : {
1212 : // 1. Simplest case when we should write contiguous block of
1213 : // uninterleaved pixels.
1214 904 : if (nXSize == GetXSize() && nXSize == nBufXSize &&
1215 450 : nYSize == nBufYSize && eBufType == eDataType &&
1216 449 : nPixelOffset == nBandDataSize && nPixelSpace == nBufDataSize &&
1217 1297 : nLineSpace == nPixelSpace * nXSize &&
1218 393 : nLineOffset == nPixelOffset * nXSize)
1219 : {
1220 333 : const size_t nValues = static_cast<size_t>(nXSize) * nYSize;
1221 :
1222 : // Byte swap the data buffer, if required.
1223 333 : if (NeedsByteOrderChange())
1224 : {
1225 12 : DoByteSwap(pData, nValues, std::abs(nPixelOffset), false);
1226 : }
1227 :
1228 : // Seek to the correct block.
1229 333 : vsi_l_offset nOffset = nImgOffset;
1230 333 : if (nLineOffset >= 0)
1231 333 : nOffset += nYOff * static_cast<vsi_l_offset>(nLineOffset);
1232 : else
1233 0 : nOffset -= nYOff * static_cast<vsi_l_offset>(-nLineOffset);
1234 :
1235 333 : if (Seek(nOffset, SEEK_SET) == -1)
1236 : {
1237 0 : CPLError(CE_Failure, CPLE_FileIO,
1238 : "Failed to seek to " CPL_FRMT_GUIB " to write data.",
1239 : nOffset);
1240 :
1241 0 : return CE_Failure;
1242 : }
1243 :
1244 : // Write the block.
1245 333 : const size_t nBytesToRW = nValues * nBandDataSize;
1246 :
1247 333 : const size_t nBytesActuallyWritten = Write(pData, 1, nBytesToRW);
1248 333 : if (nBytesActuallyWritten < nBytesToRW)
1249 : {
1250 23 : CPLError(CE_Failure, CPLE_FileIO,
1251 : "Failed to write " CPL_FRMT_GUIB
1252 : " bytes to file. " CPL_FRMT_GUIB " bytes written",
1253 : static_cast<GUIntBig>(nBytesToRW),
1254 : static_cast<GUIntBig>(nBytesActuallyWritten));
1255 :
1256 23 : return CE_Failure;
1257 : }
1258 :
1259 : // Byte swap (if necessary) back into machine order so the
1260 : // buffer is still usable for reading purposes.
1261 310 : if (NeedsByteOrderChange())
1262 : {
1263 12 : DoByteSwap(pData, nValues, std::abs(nPixelOffset), true);
1264 : }
1265 : }
1266 : // 2. Case when we need deinterleave and/or subsample data.
1267 : else
1268 : {
1269 119 : const double dfSrcXInc = static_cast<double>(nXSize) / nBufXSize;
1270 119 : const double dfSrcYInc = static_cast<double>(nYSize) / nBufYSize;
1271 :
1272 : const size_t nBytesToRW =
1273 119 : static_cast<size_t>(nPixelOffset) * (nXSize - 1) +
1274 119 : GDALGetDataTypeSizeBytes(eDataType);
1275 : GByte *pabyData =
1276 119 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(nBytesToRW));
1277 119 : if (pabyData == nullptr)
1278 0 : return CE_Failure;
1279 :
1280 2328 : for (int iLine = 0; iLine < nBufYSize; iLine++)
1281 : {
1282 2218 : const vsi_l_offset nLine =
1283 2218 : static_cast<vsi_l_offset>(nYOff) +
1284 2218 : static_cast<vsi_l_offset>(iLine * dfSrcYInc + EPS);
1285 2218 : vsi_l_offset nOffset = nImgOffset;
1286 2218 : if (nLineOffset >= 0)
1287 2199 : nOffset += nLine * static_cast<vsi_l_offset>(nLineOffset);
1288 : else
1289 19 : nOffset -= nLine * static_cast<vsi_l_offset>(-nLineOffset);
1290 2218 : if (nPixelOffset >= 0)
1291 2218 : nOffset += nXOff * static_cast<vsi_l_offset>(nPixelOffset);
1292 : else
1293 0 : nOffset -= nXOff * static_cast<vsi_l_offset>(-nPixelOffset);
1294 :
1295 : // If the data for this band is completely contiguous we don't
1296 : // have to worry about pre-reading from disk.
1297 2218 : if (nPixelOffset > nBandDataSize)
1298 1373 : AccessBlock(nOffset, nBytesToRW, pabyData, nXSize);
1299 :
1300 : // Copy data from user block buffer to disk buffer and
1301 : // subsample, if needed.
1302 2218 : if (nXSize == nBufXSize && nYSize == nBufYSize)
1303 : {
1304 2178 : GDALCopyWords64(static_cast<GByte *>(pData) +
1305 2178 : iLine * nLineSpace,
1306 : eBufType, static_cast<int>(nPixelSpace),
1307 : pabyData, eDataType, nPixelOffset, nXSize);
1308 : }
1309 : else
1310 : {
1311 840 : for (int iPixel = 0; iPixel < nBufXSize; iPixel++)
1312 : {
1313 800 : GDALCopyWords64(
1314 800 : static_cast<GByte *>(pData) + iLine * nLineSpace +
1315 800 : iPixel * nPixelSpace,
1316 : eBufType, static_cast<int>(nPixelSpace),
1317 800 : pabyData + static_cast<vsi_l_offset>(
1318 800 : iPixel * dfSrcXInc + EPS) *
1319 800 : nPixelOffset,
1320 : eDataType, nPixelOffset, 1);
1321 : }
1322 : }
1323 :
1324 : // Byte swap the data buffer, if required.
1325 2218 : if (NeedsByteOrderChange())
1326 : {
1327 204 : if (GDALDataTypeIsComplex(eDataType))
1328 : {
1329 : const int nWordSize =
1330 0 : GDALGetDataTypeSizeBytes(eDataType) / 2;
1331 0 : GDALSwapWords(pabyData, nWordSize, nXSize,
1332 : nPixelOffset);
1333 0 : GDALSwapWords(static_cast<GByte *>(pabyData) +
1334 0 : nWordSize,
1335 : nWordSize, nXSize, nPixelOffset);
1336 : }
1337 : else
1338 : {
1339 204 : GDALSwapWords(pabyData, nBandDataSize, nXSize,
1340 : nPixelOffset);
1341 : }
1342 : }
1343 :
1344 : // Seek to the right line in block.
1345 2218 : if (Seek(nOffset, SEEK_SET) == -1)
1346 : {
1347 0 : CPLError(CE_Failure, CPLE_FileIO,
1348 : "Failed to seek to " CPL_FRMT_GUIB " to read.",
1349 : nOffset);
1350 0 : CPLFree(pabyData);
1351 0 : return CE_Failure;
1352 : }
1353 :
1354 : // Write the line of block.
1355 : const size_t nBytesActuallyWritten =
1356 2218 : Write(pabyData, 1, nBytesToRW);
1357 2218 : if (nBytesActuallyWritten < nBytesToRW)
1358 : {
1359 9 : CPLError(CE_Failure, CPLE_FileIO,
1360 : "Failed to write " CPL_FRMT_GUIB
1361 : " bytes to file. " CPL_FRMT_GUIB " bytes written",
1362 : static_cast<GUIntBig>(nBytesToRW),
1363 : static_cast<GUIntBig>(nBytesActuallyWritten));
1364 9 : CPLFree(pabyData);
1365 9 : return CE_Failure;
1366 : }
1367 :
1368 : // Byte swap (if necessary) back into machine order so the
1369 : // buffer is still usable for reading purposes.
1370 2209 : if (NeedsByteOrderChange())
1371 : {
1372 195 : if (GDALDataTypeIsComplex(eDataType))
1373 : {
1374 : const int nWordSize =
1375 0 : GDALGetDataTypeSizeBytes(eDataType) / 2;
1376 0 : GDALSwapWords(pabyData, nWordSize, nXSize,
1377 : nPixelOffset);
1378 0 : GDALSwapWords(static_cast<GByte *>(pabyData) +
1379 0 : nWordSize,
1380 : nWordSize, nXSize, nPixelOffset);
1381 : }
1382 : else
1383 : {
1384 195 : GDALSwapWords(pabyData, nBandDataSize, nXSize,
1385 : nPixelOffset);
1386 : }
1387 : }
1388 : }
1389 :
1390 110 : bNeedFileFlush = TRUE;
1391 110 : CPLFree(pabyData);
1392 : }
1393 : }
1394 :
1395 5978 : return CE_None;
1396 : }
1397 :
1398 : /************************************************************************/
1399 : /* Seek() */
1400 : /************************************************************************/
1401 :
1402 58306 : int RawRasterBand::Seek(vsi_l_offset nOffset, int nSeekMode)
1403 :
1404 : {
1405 58306 : return VSIFSeekL(fpRawL, nOffset, nSeekMode);
1406 : }
1407 :
1408 : /************************************************************************/
1409 : /* Read() */
1410 : /************************************************************************/
1411 :
1412 38364 : size_t RawRasterBand::Read(void *pBuffer, size_t nSize, size_t nCount)
1413 :
1414 : {
1415 38364 : return VSIFReadL(pBuffer, nSize, nCount, fpRawL);
1416 : }
1417 :
1418 : /************************************************************************/
1419 : /* Write() */
1420 : /************************************************************************/
1421 :
1422 19942 : size_t RawRasterBand::Write(void *pBuffer, size_t nSize, size_t nCount)
1423 :
1424 : {
1425 19942 : return VSIFWriteL(pBuffer, nSize, nCount, fpRawL);
1426 : }
1427 :
1428 : /************************************************************************/
1429 : /* StoreNoDataValue() */
1430 : /* */
1431 : /* This is a helper function for datasets to associate a no */
1432 : /* data value with this band, it isn't intended to be called by */
1433 : /* applications. */
1434 : /************************************************************************/
1435 :
1436 0 : void RawRasterBand::StoreNoDataValue(double dfValue)
1437 :
1438 : {
1439 0 : SetNoDataValue(dfValue);
1440 0 : }
1441 :
1442 : /************************************************************************/
1443 : /* GetCategoryNames() */
1444 : /************************************************************************/
1445 :
1446 275 : char **RawRasterBand::GetCategoryNames()
1447 : {
1448 275 : return papszCategoryNames;
1449 : }
1450 :
1451 : /************************************************************************/
1452 : /* SetCategoryNames() */
1453 : /************************************************************************/
1454 :
1455 4 : CPLErr RawRasterBand::SetCategoryNames(char **papszNewNames)
1456 :
1457 : {
1458 4 : CSLDestroy(papszCategoryNames);
1459 4 : papszCategoryNames = CSLDuplicate(papszNewNames);
1460 :
1461 4 : return CE_None;
1462 : }
1463 :
1464 : /************************************************************************/
1465 : /* SetColorTable() */
1466 : /************************************************************************/
1467 :
1468 4 : CPLErr RawRasterBand::SetColorTable(GDALColorTable *poNewCT)
1469 :
1470 : {
1471 4 : if (poCT)
1472 0 : delete poCT;
1473 4 : if (poNewCT == nullptr)
1474 0 : poCT = nullptr;
1475 : else
1476 4 : poCT = poNewCT->Clone();
1477 :
1478 4 : return CE_None;
1479 : }
1480 :
1481 : /************************************************************************/
1482 : /* GetColorTable() */
1483 : /************************************************************************/
1484 :
1485 57 : GDALColorTable *RawRasterBand::GetColorTable()
1486 : {
1487 57 : return poCT;
1488 : }
1489 :
1490 : /************************************************************************/
1491 : /* SetColorInterpretation() */
1492 : /************************************************************************/
1493 :
1494 299 : CPLErr RawRasterBand::SetColorInterpretation(GDALColorInterp eNewInterp)
1495 :
1496 : {
1497 299 : eInterp = eNewInterp;
1498 :
1499 299 : return CE_None;
1500 : }
1501 :
1502 : /************************************************************************/
1503 : /* GetColorInterpretation() */
1504 : /************************************************************************/
1505 :
1506 588 : GDALColorInterp RawRasterBand::GetColorInterpretation()
1507 : {
1508 588 : return eInterp;
1509 : }
1510 :
1511 : /************************************************************************/
1512 : /* GetVirtualMemAuto() */
1513 : /************************************************************************/
1514 :
1515 13 : CPLVirtualMem *RawRasterBand::GetVirtualMemAuto(GDALRWFlag eRWFlag,
1516 : int *pnPixelSpace,
1517 : GIntBig *pnLineSpace,
1518 : CSLConstList papszOptions)
1519 : {
1520 13 : CPLAssert(pnPixelSpace);
1521 13 : CPLAssert(pnLineSpace);
1522 :
1523 : const vsi_l_offset nSize =
1524 13 : static_cast<vsi_l_offset>(nRasterYSize - 1) * nLineOffset +
1525 13 : static_cast<vsi_l_offset>(nRasterXSize - 1) * nPixelOffset +
1526 13 : GDALGetDataTypeSizeBytes(eDataType);
1527 :
1528 13 : const char *pszImpl = CSLFetchNameValueDef(
1529 : papszOptions, "USE_DEFAULT_IMPLEMENTATION", "AUTO");
1530 13 : if (VSIFGetNativeFileDescriptorL(fpRawL) == nullptr ||
1531 6 : !CPLIsVirtualMemFileMapAvailable() || NeedsByteOrderChange() ||
1532 6 : static_cast<size_t>(nSize) != nSize || nPixelOffset < 0 ||
1533 6 : nLineOffset < 0 || EQUAL(pszImpl, "YES") || EQUAL(pszImpl, "ON") ||
1534 19 : EQUAL(pszImpl, "1") || EQUAL(pszImpl, "TRUE"))
1535 : {
1536 7 : return GDALRasterBand::GetVirtualMemAuto(eRWFlag, pnPixelSpace,
1537 7 : pnLineSpace, papszOptions);
1538 : }
1539 :
1540 6 : FlushCache(false);
1541 :
1542 6 : CPLVirtualMem *pVMem = CPLVirtualMemFileMapNew(
1543 : fpRawL, nImgOffset, nSize,
1544 : (eRWFlag == GF_Write) ? VIRTUALMEM_READWRITE : VIRTUALMEM_READONLY,
1545 : nullptr, nullptr);
1546 6 : if (pVMem == nullptr)
1547 : {
1548 0 : if (EQUAL(pszImpl, "NO") || EQUAL(pszImpl, "OFF") ||
1549 0 : EQUAL(pszImpl, "0") || EQUAL(pszImpl, "FALSE"))
1550 : {
1551 0 : return nullptr;
1552 : }
1553 0 : return GDALRasterBand::GetVirtualMemAuto(eRWFlag, pnPixelSpace,
1554 0 : pnLineSpace, papszOptions);
1555 : }
1556 :
1557 6 : *pnPixelSpace = nPixelOffset;
1558 6 : *pnLineSpace = nLineOffset;
1559 6 : return pVMem;
1560 : }
1561 :
1562 : /************************************************************************/
1563 : /* ==================================================================== */
1564 : /* RawDataset */
1565 : /* ==================================================================== */
1566 : /************************************************************************/
1567 :
1568 : /************************************************************************/
1569 : /* RawDataset() */
1570 : /************************************************************************/
1571 :
1572 2040 : RawDataset::RawDataset()
1573 : {
1574 2040 : }
1575 :
1576 : /************************************************************************/
1577 : /* ~RawDataset() */
1578 : /************************************************************************/
1579 :
1580 : // It's pure virtual function but must be defined, even if empty.
1581 2040 : RawDataset::~RawDataset()
1582 : {
1583 2040 : }
1584 :
1585 : /************************************************************************/
1586 : /* IRasterIO() */
1587 : /* */
1588 : /* Multi-band raster io handler. */
1589 : /************************************************************************/
1590 :
1591 676 : CPLErr RawDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1592 : int nXSize, int nYSize, void *pData, int nBufXSize,
1593 : int nBufYSize, GDALDataType eBufType,
1594 : int nBandCount, BANDMAP_TYPE panBandMap,
1595 : GSpacing nPixelSpace, GSpacing nLineSpace,
1596 : GSpacing nBandSpace,
1597 : GDALRasterIOExtraArg *psExtraArg)
1598 :
1599 : {
1600 676 : const char *pszInterleave = nullptr;
1601 :
1602 676 : this->ClearCachedConfigOption();
1603 :
1604 : // The default GDALDataset::IRasterIO() implementation would go to
1605 : // BlockBasedRasterIO if the dataset is interleaved. However if the
1606 : // access pattern is compatible with DirectIO() we don't want to go
1607 : // BlockBasedRasterIO, but rather used our optimized path in
1608 : // RawRasterBand::IRasterIO().
1609 676 : if (nXSize == nBufXSize && nYSize == nBufYSize && nBandCount > 1 &&
1610 49 : (pszInterleave = GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE")) !=
1611 1352 : nullptr &&
1612 30 : EQUAL(pszInterleave, "PIXEL"))
1613 : {
1614 24 : RawRasterBand *poFirstBand = nullptr;
1615 24 : bool bCanDirectAccessToBIPDataset =
1616 24 : eRWFlag == GF_Read && nBandCount == nBands;
1617 24 : bool bCanUseDirectIO = true;
1618 95 : for (int iBandIndex = 0; iBandIndex < nBandCount; iBandIndex++)
1619 : {
1620 0 : RawRasterBand *poBand = dynamic_cast<RawRasterBand *>(
1621 72 : GetRasterBand(panBandMap[iBandIndex]));
1622 72 : if (poBand == nullptr)
1623 : {
1624 1 : bCanDirectAccessToBIPDataset = false;
1625 1 : bCanUseDirectIO = false;
1626 1 : break;
1627 : }
1628 71 : else if (!poBand->CanUseDirectIO(nXOff, nYOff, nXSize, nYSize,
1629 : eBufType, psExtraArg))
1630 : {
1631 0 : bCanUseDirectIO = false;
1632 0 : if (!bCanDirectAccessToBIPDataset)
1633 0 : break;
1634 : }
1635 71 : if (bCanDirectAccessToBIPDataset)
1636 : {
1637 18 : const auto eDT = poBand->GetRasterDataType();
1638 18 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
1639 17 : if (poBand->bNeedFileFlush || poBand->bLoadedScanlineDirty ||
1640 17 : poBand->HasDirtyBlocks() ||
1641 52 : panBandMap[iBandIndex] != iBandIndex + 1 ||
1642 17 : nPixelSpace != poBand->nPixelOffset)
1643 : {
1644 5 : bCanDirectAccessToBIPDataset = false;
1645 : }
1646 : else
1647 : {
1648 13 : if (poFirstBand == nullptr)
1649 : {
1650 5 : poFirstBand = poBand;
1651 5 : bCanDirectAccessToBIPDataset =
1652 10 : eDT == eBufType && nBandSpace == nDTSize &&
1653 5 : poFirstBand->nPixelOffset ==
1654 5 : cpl::fits_on<int>(nBands * nDTSize);
1655 : }
1656 : else
1657 : {
1658 8 : bCanDirectAccessToBIPDataset =
1659 8 : eDT == poFirstBand->GetRasterDataType() &&
1660 8 : poBand->fpRawL == poFirstBand->fpRawL &&
1661 8 : poBand->nImgOffset ==
1662 16 : poFirstBand->nImgOffset +
1663 8 : cpl::fits_on<int>(iBandIndex * nDTSize) &&
1664 8 : poBand->nPixelOffset == poFirstBand->nPixelOffset &&
1665 24 : poBand->nLineOffset == poFirstBand->nLineOffset &&
1666 8 : poBand->eByteOrder == poFirstBand->eByteOrder;
1667 : }
1668 : }
1669 : }
1670 : }
1671 24 : if (bCanDirectAccessToBIPDataset)
1672 : {
1673 4 : CPLDebugOnly("GDALRaw", "Direct access to BIP dataset");
1674 4 : const auto eDT = poFirstBand->GetRasterDataType();
1675 4 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
1676 : const bool bNeedsByteOrderChange =
1677 4 : poFirstBand->NeedsByteOrderChange();
1678 112 : for (int iY = 0; iY < nYSize; ++iY)
1679 : {
1680 108 : GByte *pabyOut = static_cast<GByte *>(pData) + iY * nLineSpace;
1681 108 : VSIFSeekL(poFirstBand->fpRawL,
1682 108 : poFirstBand->nImgOffset +
1683 108 : static_cast<vsi_l_offset>(nYOff + iY) *
1684 108 : poFirstBand->nLineOffset +
1685 108 : static_cast<vsi_l_offset>(nXOff) *
1686 108 : poFirstBand->nPixelOffset,
1687 : SEEK_SET);
1688 216 : if (VSIFReadL(pabyOut,
1689 108 : static_cast<size_t>(nXSize * nPixelSpace), 1,
1690 108 : poFirstBand->fpRawL) != 1)
1691 : {
1692 0 : return CE_Failure;
1693 : }
1694 108 : if (bNeedsByteOrderChange)
1695 : {
1696 54 : poFirstBand->DoByteSwap(
1697 54 : pabyOut, static_cast<size_t>(nXSize) * nBands, nDTSize,
1698 : true);
1699 : }
1700 : }
1701 4 : return CE_None;
1702 : }
1703 20 : else if (bCanUseDirectIO)
1704 : {
1705 19 : GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
1706 19 : void *pProgressDataGlobal = psExtraArg->pProgressData;
1707 :
1708 19 : CPLErr eErr = CE_None;
1709 78 : for (int iBandIndex = 0; iBandIndex < nBandCount && eErr == CE_None;
1710 : iBandIndex++)
1711 : {
1712 59 : GDALRasterBand *poBand = GetRasterBand(panBandMap[iBandIndex]);
1713 :
1714 59 : if (poBand == nullptr)
1715 : {
1716 0 : eErr = CE_Failure;
1717 0 : break;
1718 : }
1719 :
1720 59 : GByte *pabyBandData =
1721 59 : static_cast<GByte *>(pData) + iBandIndex * nBandSpace;
1722 :
1723 59 : psExtraArg->pfnProgress = GDALScaledProgress;
1724 118 : psExtraArg->pProgressData = GDALCreateScaledProgress(
1725 : 1.0 * iBandIndex / nBandCount,
1726 59 : 1.0 * (iBandIndex + 1) / nBandCount, pfnProgressGlobal,
1727 : pProgressDataGlobal);
1728 :
1729 59 : eErr = poBand->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1730 : static_cast<void *>(pabyBandData),
1731 : nBufXSize, nBufYSize, eBufType,
1732 : nPixelSpace, nLineSpace, psExtraArg);
1733 :
1734 59 : GDALDestroyScaledProgress(psExtraArg->pProgressData);
1735 : }
1736 :
1737 19 : psExtraArg->pfnProgress = pfnProgressGlobal;
1738 19 : psExtraArg->pProgressData = pProgressDataGlobal;
1739 :
1740 19 : return eErr;
1741 : }
1742 : }
1743 :
1744 653 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1745 : nBufXSize, nBufYSize, eBufType, nBandCount,
1746 : panBandMap, nPixelSpace, nLineSpace,
1747 653 : nBandSpace, psExtraArg);
1748 : }
1749 :
1750 : /************************************************************************/
1751 : /* RAWDatasetCheckMemoryUsage() */
1752 : /************************************************************************/
1753 :
1754 462 : bool RAWDatasetCheckMemoryUsage(int nXSize, int nYSize, int nBands, int nDTSize,
1755 : int nPixelOffset, int nLineOffset,
1756 : vsi_l_offset nHeaderSize,
1757 : vsi_l_offset nBandOffset, VSILFILE *fp)
1758 : {
1759 462 : const GIntBig nTotalBufferSize =
1760 462 : nPixelOffset == static_cast<GIntBig>(nDTSize) * nBands
1761 462 : ? // BIP ?
1762 350 : static_cast<GIntBig>(nPixelOffset) * nXSize
1763 112 : : static_cast<GIntBig>(std::abs(nPixelOffset)) * nXSize * nBands;
1764 :
1765 : // Currently each RawRasterBand allocates nPixelOffset * nRasterXSize bytes
1766 : // so for a pixel interleaved scheme, this will allocate lots of memory!
1767 : // Actually this is quadratic in the number of bands!
1768 : // Do a few sanity checks to avoid excessive memory allocation on
1769 : // small files.
1770 : // But ultimately we should fix RawRasterBand to have a shared buffer
1771 : // among bands.
1772 462 : const char *pszCheck = CPLGetConfigOption("RAW_CHECK_FILE_SIZE", nullptr);
1773 459 : if ((nBands > 10 || nTotalBufferSize > 20000 ||
1774 924 : (pszCheck && CPLTestBool(pszCheck))) &&
1775 3 : !(pszCheck && !CPLTestBool(pszCheck)))
1776 : {
1777 : vsi_l_offset nExpectedFileSize;
1778 : try
1779 : {
1780 : nExpectedFileSize =
1781 6 : (CPLSM(static_cast<uint64_t>(nHeaderSize)) +
1782 6 : CPLSM(static_cast<uint64_t>(nBandOffset)) *
1783 18 : CPLSM(static_cast<uint64_t>(nBands - 1)) +
1784 : (nLineOffset >= 0
1785 12 : ? CPLSM(static_cast<uint64_t>(nYSize - 1)) *
1786 12 : CPLSM(static_cast<uint64_t>(nLineOffset))
1787 6 : : CPLSM(static_cast<uint64_t>(0))) +
1788 : (nPixelOffset >= 0
1789 12 : ? CPLSM(static_cast<uint64_t>(nXSize - 1)) *
1790 12 : CPLSM(static_cast<uint64_t>(nPixelOffset))
1791 6 : : CPLSM(static_cast<uint64_t>(0))))
1792 6 : .v();
1793 : }
1794 0 : catch (...)
1795 : {
1796 0 : CPLError(CE_Failure, CPLE_AppDefined, "Image file is too small");
1797 0 : return false;
1798 : }
1799 6 : CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_END));
1800 6 : vsi_l_offset nFileSize = VSIFTellL(fp);
1801 : // Do not strictly compare against nExpectedFileSize, but use an
1802 : // arbitrary 50% margin, since some raw formats such as ENVI allow for
1803 : // sparse files (see https://github.com/OSGeo/gdal/issues/915)
1804 6 : if (nFileSize < nExpectedFileSize / 2)
1805 : {
1806 2 : CPLError(CE_Failure, CPLE_AppDefined, "Image file is too small");
1807 2 : return false;
1808 : }
1809 : }
1810 :
1811 : #if SIZEOF_VOIDP == 8
1812 460 : const char *pszDefault = "1024";
1813 : #else
1814 : const char *pszDefault = "512";
1815 : #endif
1816 460 : constexpr int MB_IN_BYTES = 1024 * 1024;
1817 : const GIntBig nMAX_BUFFER_MEM =
1818 460 : static_cast<GIntBig>(
1819 460 : atoi(CPLGetConfigOption("RAW_MEM_ALLOC_LIMIT_MB", pszDefault))) *
1820 460 : MB_IN_BYTES;
1821 460 : if (nTotalBufferSize > nMAX_BUFFER_MEM)
1822 : {
1823 0 : CPLError(
1824 : CE_Failure, CPLE_OutOfMemory,
1825 : CPL_FRMT_GIB
1826 : " MB of RAM would be needed to open the dataset. If you are "
1827 : "comfortable with this, you can set the RAW_MEM_ALLOC_LIMIT_MB "
1828 : "configuration option to that value or above",
1829 0 : DIV_ROUND_UP(nTotalBufferSize, MB_IN_BYTES));
1830 0 : return false;
1831 : }
1832 :
1833 460 : return true;
1834 : }
1835 :
1836 : /************************************************************************/
1837 : /* GetRawBinaryLayout() */
1838 : /************************************************************************/
1839 :
1840 9 : bool RawDataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
1841 : {
1842 9 : vsi_l_offset nImgOffset = 0;
1843 9 : GIntBig nBandOffset = 0;
1844 9 : int nPixelOffset = 0;
1845 9 : int nLineOffset = 0;
1846 9 : RawRasterBand::ByteOrder eByteOrder =
1847 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
1848 9 : GDALDataType eDT = GDT_Unknown;
1849 26 : for (int i = 1; i <= nBands; i++)
1850 : {
1851 17 : auto poBand = dynamic_cast<RawRasterBand *>(GetRasterBand(i));
1852 17 : if (poBand == nullptr)
1853 0 : return false;
1854 17 : if (i == 1)
1855 : {
1856 9 : nImgOffset = poBand->nImgOffset;
1857 9 : nPixelOffset = poBand->nPixelOffset;
1858 9 : nLineOffset = poBand->nLineOffset;
1859 9 : eByteOrder = poBand->eByteOrder;
1860 9 : if (eByteOrder == RawRasterBand::ByteOrder::ORDER_VAX)
1861 0 : return false;
1862 9 : eDT = poBand->GetRasterDataType();
1863 : }
1864 24 : else if (nPixelOffset != poBand->nPixelOffset ||
1865 8 : nLineOffset != poBand->nLineOffset ||
1866 24 : eByteOrder != poBand->eByteOrder ||
1867 8 : eDT != poBand->GetRasterDataType())
1868 : {
1869 0 : return false;
1870 : }
1871 8 : else if (i == 2)
1872 : {
1873 4 : nBandOffset = static_cast<GIntBig>(poBand->nImgOffset) -
1874 4 : static_cast<GIntBig>(nImgOffset);
1875 : }
1876 4 : else if (nBandOffset * (i - 1) !=
1877 4 : static_cast<GIntBig>(poBand->nImgOffset) -
1878 4 : static_cast<GIntBig>(nImgOffset))
1879 : {
1880 0 : return false;
1881 : }
1882 : }
1883 :
1884 9 : sLayout.eInterleaving = RawBinaryLayout::Interleaving::UNKNOWN;
1885 9 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
1886 9 : if (nBands > 1)
1887 : {
1888 4 : if (nPixelOffset == nBands * nDTSize &&
1889 2 : nLineOffset == nPixelOffset * nRasterXSize &&
1890 2 : nBandOffset == nDTSize)
1891 : {
1892 2 : sLayout.eInterleaving = RawBinaryLayout::Interleaving::BIP;
1893 : }
1894 2 : else if (nPixelOffset == nDTSize &&
1895 2 : nLineOffset == nDTSize * nBands * nRasterXSize &&
1896 1 : nBandOffset == static_cast<GIntBig>(nDTSize) * nRasterXSize)
1897 : {
1898 1 : sLayout.eInterleaving = RawBinaryLayout::Interleaving::BIL;
1899 : }
1900 1 : else if (nPixelOffset == nDTSize &&
1901 1 : nLineOffset == nDTSize * nRasterXSize &&
1902 : nBandOffset ==
1903 1 : static_cast<GIntBig>(nLineOffset) * nRasterYSize)
1904 : {
1905 1 : sLayout.eInterleaving = RawBinaryLayout::Interleaving::BSQ;
1906 : }
1907 : }
1908 :
1909 9 : sLayout.eDataType = eDT;
1910 9 : sLayout.bLittleEndianOrder =
1911 9 : eByteOrder == RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
1912 9 : sLayout.nImageOffset = nImgOffset;
1913 9 : sLayout.nPixelOffset = nPixelOffset;
1914 9 : sLayout.nLineOffset = nLineOffset;
1915 9 : sLayout.nBandOffset = nBandOffset;
1916 :
1917 9 : return true;
1918 : }
1919 :
1920 : /************************************************************************/
1921 : /* ClearCachedConfigOption() */
1922 : /************************************************************************/
1923 :
1924 676 : void RawDataset::ClearCachedConfigOption(void)
1925 : {
1926 676 : cachedCPLOneBigReadOption = 0;
1927 676 : }
|