Coverage Report

Created: 2025-11-15 08:43

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