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