Coverage Report

Created: 2026-02-14 06:52

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