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