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