• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.36
/frmts/mrf/marfa_dataset.cpp
1
/*
2
 * Copyright (c) 2002-2012, California Institute of Technology.
3
 * All rights reserved.  Based on Government Sponsored Research under contracts
4
 * NAS7-1407 and/or NAS7-03001.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *   1. Redistributions of source code must retain the above copyright notice,
9
 * this list of conditions and the following disclaimer.
10
 *   2. Redistributions in binary form must reproduce the above copyright
11
 * notice, this list of conditions and the following disclaimer in the
12
 * documentation and/or other materials provided with the distribution.
13
 *   3. Neither the name of the California Institute of Technology (Caltech),
14
 * its operating division the Jet Propulsion Laboratory (JPL), the National
15
 * Aeronautics and Space Administration (NASA), nor the names of its
16
 * contributors may be used to endorse or promote products derived from this
17
 * software without specific prior written permission.
18
 *
19
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
 * ARE DISCLAIMED. IN NO EVENT SHALL THE CALIFORNIA INSTITUTE OF TECHNOLOGY BE
23
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 * POSSIBILITY OF SUCH DAMAGE.
30
 *
31
 * Portions copyright 2014-2021 Esri
32
 *
33
 * Licensed under the Apache License, Version 2.0 (the "License");
34
 * you may not use this file except in compliance with the License.
35
 * You may obtain a copy of the License at
36
 *
37
 * http://www.apache.org/licenses/LICENSE-2.0
38
 *
39
 * Unless required by applicable law or agreed to in writing, software
40
 * distributed under the License is distributed on an "AS IS" BASIS,
41
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42
 * See the License for the specific language governing permissions and
43
 * limitations under the License.
44
 */
45

46
/******************************************************************************
47
 *
48
 * Project:  Meta Raster File Format Driver Implementation, Dataset
49
 * Purpose:  Implementation of GDAL dataset
50
 *
51
 * Author:   Lucian Plesea, Lucian.Plesea jpl.nasa.gov, lplesea esri.com
52
 *
53
 ******************************************************************************
54
 *
55
 *   The MRF dataset and the band are closely tied together, they should be
56
 *   considered a single class, or a class (dataset) with extensions (bands).
57
 *
58
 *
59
 ****************************************************************************/
60

61
#include "marfa.h"
62
#include "mrfdrivercore.h"
63
#include "cpl_multiproc.h" /* for CPLSleep() */
64
#include "gdal_priv.h"
65
#include <assert.h>
66

67
#include <algorithm>
68
#include <limits>
69
#include <vector>
70
#if defined(ZSTD_SUPPORT)
71
#include <zstd.h>
72
#endif
73
using std::string;
74
using std::vector;
75

76
NAMESPACE_MRF_START
77

78
// Initialize as invalid
79
MRFDataset::MRFDataset()
371✔
80
    : zslice(0), idxSize(0), clonedSource(FALSE), nocopy(FALSE),
81
      bypass_cache(
82
          CPLTestBool(CPLGetConfigOption("MRF_BYPASSCACHING", "FALSE"))),
371✔
83
      mp_safe(FALSE), hasVersions(FALSE), verCount(0),
84
      bCrystalized(TRUE),  // Assume not in create mode
85
      spacing(0), no_errors(0), missing(0), poSrcDS(nullptr), level(-1),
86
      cds(nullptr), scale(0.0), pbuffer(nullptr), pbsize(0), tile(ILSize()),
87
      bdirty(0), bGeoTransformValid(TRUE), poColorTable(nullptr), Quality(0),
88
      pzscctx(nullptr), pzsdctx(nullptr), read_timer(), write_timer(0)
742✔
89
{
90
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
371✔
91
    ifp.FP = dfp.FP = nullptr;
371✔
92
    dfp.acc = GF_Read;
371✔
93
    ifp.acc = GF_Read;
371✔
94
}
371✔
95

96
bool MRFDataset::SetPBuffer(unsigned int sz)
181✔
97
{
98
    if (sz == 0)
181✔
99
    {
100
        CPLFree(pbuffer);
×
101
        pbuffer = nullptr;
×
102
    }
103
    void *pbufferNew = VSIRealloc(pbuffer, sz);
181✔
104
    if (pbufferNew == nullptr)
181✔
105
    {
106
        CPLError(CE_Failure, CPLE_OutOfMemory, "Cannot allocate %u bytes", sz);
×
107
        return false;
×
108
    }
109
    pbuffer = pbufferNew;
181✔
110
    pbsize = sz;
181✔
111
    return true;
181✔
112
}
113

114
static GDALColorEntry GetXMLColorEntry(CPLXMLNode *p)
256✔
115
{
116
    GDALColorEntry ce;
117
    ce.c1 = static_cast<short>(getXMLNum(p, "c1", 0));
256✔
118
    ce.c2 = static_cast<short>(getXMLNum(p, "c2", 0));
256✔
119
    ce.c3 = static_cast<short>(getXMLNum(p, "c3", 0));
256✔
120
    ce.c4 = static_cast<short>(getXMLNum(p, "c4", 255));
256✔
121
    return ce;
256✔
122
}
123

124
//
125
// Called by dataset destructor or at GDAL termination, to avoid
126
// closing datasets whose drivers have already been unloaded
127
//
128
int MRFDataset::CloseDependentDatasets()
371✔
129
{
130
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
371✔
131

132
    if (poSrcDS)
371✔
133
    {
134
        bHasDroppedRef = TRUE;
3✔
135
        GDALClose(GDALDataset::ToHandle(poSrcDS));
3✔
136
        poSrcDS = nullptr;
3✔
137
    }
138

139
    if (cds)
371✔
140
    {
141
        bHasDroppedRef = TRUE;
2✔
142
        GDALClose(GDALDataset::ToHandle(cds));
2✔
143
        cds = nullptr;
2✔
144
    }
145

146
    return bHasDroppedRef;
371✔
147
}
148

149
MRFDataset::~MRFDataset()
742✔
150
{  // Make sure everything gets written
151
    if (eAccess != GA_ReadOnly && !bCrystalized)
371✔
152
        if (!MRFDataset::Crystalize())
50✔
153
        {
154
            // Can't return error code from a destructor, just emit the error
155
            CPLError(CE_Failure, CPLE_FileIO, "Error creating files");
10✔
156
        }
157

158
    MRFDataset::FlushCache(true);
371✔
159
    MRFDataset::CloseDependentDatasets();
371✔
160

161
    if (ifp.FP)
371✔
162
        VSIFCloseL(ifp.FP);
263✔
163
    if (dfp.FP)
371✔
164
        VSIFCloseL(dfp.FP);
266✔
165

166
    delete poColorTable;
371✔
167

168
    // CPLFree ignores being called with NULL
169
    CPLFree(pbuffer);
371✔
170
    pbsize = 0;
371✔
171
#if defined(ZSTD_SUPPORT)
172
    ZSTD_freeCCtx(static_cast<ZSTD_CCtx *>(pzscctx));
371✔
173
    ZSTD_freeDCtx(static_cast<ZSTD_DCtx *>(pzsdctx));
371✔
174
#endif
175
    // total time spend doing compression and decompression
176
    if (0 != write_timer.count())
371✔
177
        CPLDebug("MRF_Timing", "Compression took %fms",
112✔
178
                 1e-6 * write_timer.count());
112✔
179

180
    if (0 != read_timer.count())
371✔
181
        CPLDebug("MRF_Timing", "Decompression took %fms",
142✔
182
                 1e-6 * read_timer.count());
142✔
183
}
742✔
184

185
/*
186
 *\brief Format specific RasterIO, may be bypassed by BlockBasedRasterIO by
187
 *setting GDAL_FORCE_CACHING to Yes, in which case the band ReadBlock and
188
 *WriteBLock are called directly
189
 */
190
CPLErr MRFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
140✔
191
                             int nXSize, int nYSize, void *pData, int nBufXSize,
192
                             int nBufYSize, GDALDataType eBufType,
193
                             int nBandCount, BANDMAP_TYPE panBandMap,
194
                             GSpacing nPixelSpace, GSpacing nLineSpace,
195
                             GSpacing nBandSpace,
196
                             GDALRasterIOExtraArg *psExtraArgs)
197
{
198
    CPLDebug("MRF_IO",
140✔
199
             "IRasterIO %s, %d, %d, %d, %d, bufsz %d,%d,%d strides P %d, L %d, "
200
             "B %d \n",
201
             eRWFlag == GF_Write ? "Write" : "Read", nXOff, nYOff, nXSize,
202
             nYSize, nBufXSize, nBufYSize, nBandCount,
203
             static_cast<int>(nPixelSpace), static_cast<int>(nLineSpace),
204
             static_cast<int>(nBandSpace));
205

206
    if (eRWFlag == GF_Write && !bCrystalized && !Crystalize())
140✔
207
    {
208
        CPLError(CE_Failure, CPLE_FileIO, "MRF: Error creating files");
×
209
        return CE_Failure;
×
210
    }
211

212
    //
213
    // Call the parent implementation, which splits it into bands and calls
214
    // their IRasterIO
215
    //
216
    return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
140✔
217
                                     pData, nBufXSize, nBufYSize, eBufType,
218
                                     nBandCount, panBandMap, nPixelSpace,
219
                                     nLineSpace, nBandSpace, psExtraArgs);
140✔
220
}
221

222
/**
223
 *\brief Build some overviews
224
 *
225
 *  if nOverviews is 0, erase the overviews (reduce to base image only)
226
 */
227

228
CPLErr MRFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
31✔
229
                                   const int *panOverviewList, int nBandsIn,
230
                                   const int *panBandList,
231
                                   GDALProgressFunc pfnProgress,
232
                                   void *pProgressData,
233
                                   CSLConstList papszOptions)
234

235
{
236
    if (pfnProgress == nullptr)
31✔
237
        pfnProgress = GDALDummyProgress;
×
238

239
    CPLErr eErr = CE_None;
31✔
240
    CPLDebug("MRF_OVERLAY", "IBuildOverviews %d, bands %d\n", nOverviews,
31✔
241
             nBandsIn);
242

243
    if (nBands != nBandsIn)
31✔
244
    {
245
        CPLError(CE_Failure, CPLE_NotSupported, "nBands = %d not supported",
×
246
                 nBandsIn);
247
        return CE_Failure;
×
248
    }
249

250
    //      If we don't have write access, then create external overviews
251
    if (GetAccess() != GA_Update)
31✔
252
    {
253
        CPLDebug("MRF", "File open read-only, creating overviews externally.");
1✔
254
        return GDALDataset::IBuildOverviews(
1✔
255
            pszResampling, nOverviews, panOverviewList, nBands, panBandList,
256
            pfnProgress, pProgressData, papszOptions);
1✔
257
    }
258

259
    if (nOverviews == 0)
30✔
260
    {
261
        // If there are none, nothing to do
262
        if (GetRasterBand(1)->GetOverviewCount() == 0)
×
263
            return CE_None;
×
264

265
        auto *b = static_cast<MRFRasterBand *>(GetRasterBand(1));
×
266
        // If the first band internal overviews don't exist, they are external
267
        if (b->overviews.empty())
×
268
            return GDALDataset::IBuildOverviews(
×
269
                pszResampling, nOverviews, panOverviewList, nBands, panBandList,
270
                pfnProgress, pProgressData, papszOptions);
×
271

272
        // We should clean overviews, but this is not allowed in an MRF
273
        CPLError(CE_Warning, CPLE_NotSupported,
×
274
                 "MRF: Internal overviews cannot be removed, "
275
                 "but they can be rebuilt");
276
        return CE_None;
×
277
    }
278

279
    std::vector<int> panOverviewListNew(nOverviews);
30✔
280
    for (int i = 0; i < nOverviews; i++)
61✔
281
        panOverviewListNew[i] = panOverviewList[i];
31✔
282
    try
283
    {  // Throw an error code, to make sure memory gets freed properly
284
        // Modify the metadata file if it doesn't already have the Rset model
285
        // set
286
        if (0.0 == scale)
30✔
287
        {
288
            CPLXMLNode *config = ReadConfig();
30✔
289
            try
290
            {
291
                const char *model =
292
                    CPLGetXMLValue(config, "Rsets.model", "uniform");
30✔
293
                if (!EQUAL(model, "uniform"))
30✔
294
                {
295
                    CPLError(CE_Failure, CPLE_AppDefined,
×
296
                             "MRF:IBuildOverviews, Overviews not implemented "
297
                             "for model %s",
298
                             model);
299
                    throw CE_Failure;
×
300
                }
301

302
                // The scale value is the same as first overview
303
                scale =
30✔
304
                    strtod(CPLGetXMLValue(
30✔
305
                               config, "Rsets.scale",
306
                               CPLOPrintf("%d", panOverviewList[0]).c_str()),
60✔
307
                           nullptr);
308
                if (scale == 0.0)
30✔
309
                {
310
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
311
                             "Invalid Rsets.scale value");
312
                    throw CE_Failure;
×
313
                }
314

315
                if (static_cast<int>(scale) != 2 &&
30✔
316
                    (EQUALN("Avg", pszResampling, 3) ||
×
317
                     EQUALN("Nnb", pszResampling, 3)))
×
318
                {
319
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
320
                             "MRF internal resampling requires a scale factor "
321
                             "of two");
322
                    throw CE_Failure;
×
323
                }
324

325
                // Initialize the empty overlays, all of them for a given scale
326
                // They could already exist, in which case they are not erased
327
                idxSize = AddOverviews(int(scale));
30✔
328

329
                // If we don't have overviews, don't try to generate them
330
                if (GetRasterBand(1)->GetOverviewCount() == 0)
30✔
331
                    throw CE_None;
×
332

333
                if (!CheckFileSize(current.idxfname, idxSize, GA_Update))
30✔
334
                {
335
                    CPLError(CE_Failure, CPLE_AppDefined,
×
336
                             "MRF: Can't extend index file");
337
                    throw CE_Failure;
×
338
                }
339

340
                //  Set the uniform node, in case it was not set before, and
341
                //  save the new configuration
342
                CPLSetXMLValue(config, "Rsets.#model", "uniform");
30✔
343
                CPLSetXMLValue(config, "Rsets.#scale", PrintDouble(scale));
30✔
344

345
                if (!WriteConfig(config))
30✔
346
                {
347
                    CPLError(CE_Failure, CPLE_AppDefined,
×
348
                             "MRF: Can't rewrite the metadata file");
349
                    throw CE_Failure;
×
350
                }
351
                CPLDestroyXMLNode(config);
30✔
352
                config = nullptr;
30✔
353
            }
354
            catch (const CPLErr &)
×
355
            {
356
                CPLDestroyXMLNode(config);
×
357
                throw;  // Rethrow
×
358
            }
359

360
            // To avoid issues with blacks overviews, generate all of them
361
            // if the user asked for a couple of overviews in the correct
362
            // sequence and starting with the lowest one
363
            if (!EQUAL(pszResampling, "NONE") &&
90✔
364
                nOverviews != GetRasterBand(1)->GetOverviewCount() &&
32✔
365
                CPLTestBool(
2✔
366
                    CPLGetConfigOption("MRF_ALL_OVERVIEW_LEVELS", "YES")))
367
            {
368
                bool bIncreasingPowers =
2✔
369
                    (panOverviewList[0] == static_cast<int>(scale));
2✔
370
                for (int i = 1; i < nOverviews; i++)
3✔
371
                    bIncreasingPowers =
1✔
372
                        bIncreasingPowers &&
2✔
373
                        (panOverviewList[i] ==
1✔
374
                         static_cast<int>(scale * panOverviewList[i - 1]));
1✔
375

376
                int ovrcount = GetRasterBand(1)->GetOverviewCount();
2✔
377
                if (bIncreasingPowers && nOverviews != ovrcount)
2✔
378
                {
379
                    CPLDebug("MRF",
2✔
380
                             "Generating %d levels instead of the %d requested",
381
                             ovrcount, nOverviews);
382
                    nOverviews = ovrcount;
2✔
383
                    panOverviewListNew.resize(ovrcount);
2✔
384
                    for (int i = 0; i < ovrcount; i++)
7✔
385
                        panOverviewListNew[i] = int(std::pow(scale, i + 1));
5✔
386
                }
387
            }
388
        }
389

390
        if (static_cast<int>(scale) != 2 && (EQUALN("Avg", pszResampling, 3) ||
30✔
391
                                             EQUALN("Nnb", pszResampling, 3)))
×
392
        {
393
            CPLError(CE_Failure, CPLE_IllegalArg,
×
394
                     "MRF internal resampling requires a scale factor of two");
395
            throw CE_Failure;
×
396
        }
397

398
        // First pass, count the pixels to be processed, mark invalid levels
399
        // Smallest positive value to avoid Coverity Scan complaining about
400
        // division by zero
401
        double dfTotalPixels = std::numeric_limits<double>::min();
30✔
402
        double dfPixels = double(nRasterXSize) * nRasterYSize;
30✔
403
        for (int i = 0; i < nOverviews; i++)
63✔
404
        {
405
            // Verify that scales are reasonable, val/scale has to be an integer
406
            if (!IsPower(panOverviewListNew[i], scale))
33✔
407
            {
408
                CPLError(CE_Warning, CPLE_AppDefined,
×
409
                         "MRF:IBuildOverviews, skipping overview factor %d,"
410
                         " it is not a power of %f",
411
                         panOverviewListNew[i], scale);
×
412
                panOverviewListNew[i] = -1;
×
413
                continue;
×
414
            };
415

416
            int srclevel = int(logbase(panOverviewListNew[i], scale) - 0.5);
33✔
417
            MRFRasterBand *b = static_cast<MRFRasterBand *>(GetRasterBand(1));
33✔
418

419
            // Warn for requests for invalid levels
420
            if (srclevel >= b->GetOverviewCount())
33✔
421
            {
422
                CPLError(CE_Warning, CPLE_AppDefined,
×
423
                         "MRF:IBuildOverviews, overview factor %d is not valid "
424
                         "for this dataset",
425
                         panOverviewListNew[i]);
×
426
                panOverviewListNew[i] = -1;
×
427
                continue;
×
428
            }
429
            dfTotalPixels += dfPixels * std::pow(scale, -srclevel);
33✔
430
        }
431

432
        dfPixels = 0;
30✔
433
        for (int i = 0; i < nOverviews; i++)
63✔
434
        {
435
            // Skip the invalid levels
436
            if (panOverviewListNew[i] < 0)
33✔
437
                continue;
×
438

439
            int srclevel = int(logbase(panOverviewListNew[i], scale) - 0.5);
33✔
440
            auto dfLevelPixels =
441
                std::pow(scale, -srclevel) * nRasterXSize * nRasterYSize;
33✔
442
            // Use "avg" flag to trigger the internal average sampling
443
            if (EQUALN("Avg", pszResampling, 3) ||
33✔
444
                EQUALN("Nnb", pszResampling, 3))
19✔
445
            {
446
                int sampling = EQUALN("Avg", pszResampling, 3) ? SAMPLING_Avg
26✔
447
                                                               : SAMPLING_Near;
448
                auto b = static_cast<MRFRasterBand *>(GetRasterBand(1));
26✔
449
                // Internal, using PatchOverview
450
                if (srclevel > 0)
26✔
451
                    b = static_cast<MRFRasterBand *>(
452
                        b->GetOverview(srclevel - 1));
3✔
453

454
                eErr =
455
                    PatchOverview(0, 0, b->nBlocksPerRow, b->nBlocksPerColumn,
26✔
456
                                  srclevel, 0, sampling);
457
                if (eErr == CE_Failure)
26✔
458
                    throw eErr;
26✔
459
            }
460
            else
461
            {
462
                // Use the GDAL method
463
                std::vector<GDALRasterBand *> apoSrcBandList(nBands);
14✔
464
                std::vector<std::vector<GDALRasterBand *>> aapoOverviewBandList(
465
                    nBands);
14✔
466
                for (int iBand = 0; iBand < nBands; iBand++)
14✔
467
                {
468
                    // This is the base level
469
                    apoSrcBandList[iBand] = GetRasterBand(panBandList[iBand]);
7✔
470
                    // Set up the destination
471
                    aapoOverviewBandList[iBand] = std::vector<GDALRasterBand *>{
14✔
472
                        apoSrcBandList[iBand]->GetOverview(srclevel)};
14✔
473
                    // Use the previous level as the source
474
                    if (srclevel > 0)
7✔
475
                        apoSrcBandList[iBand] =
×
476
                            apoSrcBandList[iBand]->GetOverview(srclevel - 1);
×
477
                }
478

479
                auto pScaledProgress = GDALCreateScaledProgress(
14✔
480
                    dfPixels / dfTotalPixels,
481
                    (dfPixels + dfLevelPixels) / dfTotalPixels, pfnProgress,
7✔
482
                    pProgressData);
483
                eErr = GDALRegenerateOverviewsMultiBand(
7✔
484
                    apoSrcBandList, aapoOverviewBandList, pszResampling,
485
                    GDALScaledProgress, pScaledProgress, papszOptions);
486
                GDALDestroyScaledProgress(pScaledProgress);
7✔
487
            }
488
            dfPixels += dfLevelPixels;
33✔
489
            pfnProgress(dfPixels / dfTotalPixels, "", pProgressData);
33✔
490
            if (eErr == CE_Failure)
33✔
491
                throw eErr;
×
492
        }
493

494
        pfnProgress(1.0, "", pProgressData);
30✔
495
    }
496
    catch (const CPLErr &e)
×
497
    {
498
        eErr = e;
×
499
    }
500

501
    return eErr;
30✔
502
}
503

504
/*
505
 *\brief blank separated list to vector of doubles
506
 */
507
static void list2vec(std::vector<double> &v, const char *pszList)
34✔
508
{
509
    if ((pszList == nullptr) || (pszList[0] == 0))
34✔
510
        return;
×
511
    char **papszTokens = CSLTokenizeString2(
34✔
512
        pszList, " \t\n\r", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
513
    v.clear();
34✔
514
    for (int i = 0; i < CSLCount(papszTokens); i++)
69✔
515
        v.push_back(CPLStrtod(papszTokens[i], nullptr));
35✔
516
    CSLDestroy(papszTokens);
34✔
517
}
518

519
void MRFDataset::SetNoDataValue(const char *pszVal)
20✔
520
{
521
    list2vec(vNoData, pszVal);
20✔
522
}
20✔
523

524
void MRFDataset::SetMinValue(const char *pszVal)
7✔
525
{
526
    list2vec(vMin, pszVal);
7✔
527
}
7✔
528

529
void MRFDataset::SetMaxValue(const char *pszVal)
7✔
530
{
531
    list2vec(vMax, pszVal);
7✔
532
}
7✔
533

534
/**
535
 *
536
 *\brief Read the XML config tree, from file
537
 *  Caller is responsible for freeing the memory
538
 *
539
 * @return NULL on failure, or the document tree on success.
540
 *
541
 */
542
CPLXMLNode *MRFDataset::ReadConfig() const
30✔
543
{
544
    if (fname[0] == '<')
30✔
545
        return CPLParseXMLString(fname);
×
546
    return CPLParseXMLFile(fname);
30✔
547
}
548

549
/**
550
 *\brief Write the XML config tree
551
 * Caller is responsible for correctness of data
552
 * and for freeing the memory
553
 *
554
 * @param config The document tree to write
555
 * @return TRUE on success, FALSE otherwise
556
 */
557
int MRFDataset::WriteConfig(CPLXMLNode *config)
202✔
558
{
559
    if (fname[0] == '<')
202✔
560
        return FALSE;
×
561
    return CPLSerializeXMLTreeToFile(config, fname);
202✔
562
}
563

564
static void
565
stringSplit(vector<string> &theStringVector,  // Altered/returned value
5✔
566
            const string &theString, size_t start = 0,
567
            const char theDelimiter = ' ')
568
{
569
    while (true)
570
    {
571
        size_t end = theString.find(theDelimiter, start);
5✔
572
        if (string::npos == end)
5✔
573
        {
574
            theStringVector.push_back(theString.substr(start));
5✔
575
            return;
5✔
576
        }
577
        theStringVector.push_back(theString.substr(start, end - start));
×
578
        start = end + 1;
×
579
    }
×
580
}
581

582
// Returns the number following the prefix if it exists in one of the vector
583
// strings Otherwise it returns the default
584
static int getnum(const vector<string> &theStringVector, const char prefix,
15✔
585
                  int def)
586
{
587
    for (unsigned int i = 0; i < theStringVector.size(); i++)
25✔
588
        if (theStringVector[i][0] == prefix)
15✔
589
            return atoi(theStringVector[i].c_str() + 1);
5✔
590
    return def;
10✔
591
}
592

593
/**
594
 *\brief Open a MRF file
595
 *
596
 */
597
GDALDataset *MRFDataset::Open(GDALOpenInfo *poOpenInfo)
204✔
598
{
599
    if (!MRFDriverIdentify(poOpenInfo))
204✔
600
        return nullptr;
×
601

602
    CPLXMLNode *config = nullptr;
204✔
603
    CPLErr ret = CE_None;
204✔
604
    const char *pszFileName = poOpenInfo->pszFilename;
204✔
605

606
    int level = -1;   // All levels
204✔
607
    int version = 0;  // Current
204✔
608
    int zslice = 0;
204✔
609
    string fn;        // Used to parse and adjust the file name
408✔
610
    string insidefn;  // inside tar file name
408✔
611

612
    // Different ways to open an MRF
613
    if (poOpenInfo->nHeaderBytes >= 10)
204✔
614
    {
615
        const char *pszHeader =
199✔
616
            reinterpret_cast<char *>(poOpenInfo->pabyHeader);
617
        fn.assign(pszHeader, poOpenInfo->nHeaderBytes);
199✔
618
        if (STARTS_WITH(pszHeader, "<MRF_META>"))  // Regular file name
199✔
619
            config = CPLParseXMLFile(pszFileName);
192✔
620
        else if (poOpenInfo->eAccess == GA_ReadOnly && fn.size() > 600 &&
7✔
621
                 (fn[262] == 0 || fn[262] == 32) &&
7✔
622
                 STARTS_WITH(fn.c_str() + 257, "ustar") &&
1✔
623
                 strlen(CPLGetPathSafe(fn.c_str()).c_str()) == 0 &&
15✔
624
                 STARTS_WITH(fn.c_str() + 512, "<MRF_META>"))
1✔
625
        {  // An MRF inside a tar
626
            insidefn = string("/vsitar/") + pszFileName + "/" + pszHeader;
1✔
627
            config = CPLParseXMLFile(insidefn.c_str());
1✔
628
        }
629
#if defined(LERC)
630
        else
631
            config = LERC_Band::GetMRFConfig(poOpenInfo);
6✔
632
#endif
633
    }
634
    else
635
    {
636
        if (EQUALN(pszFileName, "<MRF_META>", 10))  // Content as file name
5✔
637
            config = CPLParseXMLString(pszFileName);
×
638
        else
639
        {  // Try Ornate file name
640
            fn = pszFileName;
5✔
641
            size_t pos = fn.find(":MRF:");
5✔
642
            if (string::npos != pos)
5✔
643
            {  // Tokenize and pick known options
644
                vector<string> tokens;
5✔
645
                stringSplit(tokens, fn, pos + 5, ':');
5✔
646
                level = getnum(tokens, 'L', -1);
5✔
647
                version = getnum(tokens, 'V', 0);
5✔
648
                zslice = getnum(tokens, 'Z', 0);
5✔
649
                fn.resize(pos);  // Cut the ornamentations
5✔
650
                pszFileName = fn.c_str();
5✔
651
                config = CPLParseXMLFile(pszFileName);
5✔
652
            }
653
        }
654
    }
655

656
    if (!config)
204✔
657
        return nullptr;
×
658

659
    MRFDataset *ds = new MRFDataset();
204✔
660
    ds->fname = pszFileName;
204✔
661
    if (!insidefn.empty())
204✔
662
    {
663
        ds->publicname = pszFileName;
1✔
664
        ds->fname = insidefn;
1✔
665
    }
666
    ds->eAccess = poOpenInfo->eAccess;
204✔
667
    ds->level = level;
204✔
668
    ds->zslice = zslice;
204✔
669

670
    // OpenOptions can override file name arguments
671
    ds->ProcessOpenOptions(poOpenInfo->papszOpenOptions);
204✔
672

673
    if (level == -1)
204✔
674
        ret = ds->Initialize(config);
202✔
675
    else
676
    {
677
        // Open the whole dataset, then pick one level
678
        ds->cds = new MRFDataset();
2✔
679
        ds->cds->fname = ds->fname;
2✔
680
        ds->cds->eAccess = ds->eAccess;
2✔
681
        ds->zslice = zslice;
2✔
682
        ret = ds->cds->Initialize(config);
2✔
683
        if (ret == CE_None)
2✔
684
            ret = ds->LevelInit(level);
2✔
685
    }
686
    CPLDestroyXMLNode(config);
204✔
687

688
    if (ret != CE_None)
204✔
689
    {
690
        delete ds;
23✔
691
        return nullptr;
23✔
692
    }
693

694
    // Open a single version
695
    if (version != 0)
181✔
696
        ret = ds->SetVersion(version);
2✔
697

698
    if (ret != CE_None)
181✔
699
    {
700
        delete ds;
1✔
701
        return nullptr;
1✔
702
    }
703

704
    // Tell PAM what our real file name is, to help it find the aux.xml
705
    ds->SetPhysicalFilename(ds->fname);
180✔
706
    // Don't mess with metadata after this, otherwise PAM will re-write the
707
    // aux.xml
708
    ds->TryLoadXML();
180✔
709

710
    /* -------------------------------------------------------------------- */
711
    /*      Open external overviews.                                        */
712
    /* -------------------------------------------------------------------- */
713
    ds->oOvManager.Initialize(ds, ds->fname);
180✔
714

715
    return ds;
180✔
716
}
717

718
// Adjust the band images with the right offset, then adjust the sizes
719
CPLErr MRFDataset::SetVersion(int version)
2✔
720
{
721
    if (!hasVersions || version > verCount)
2✔
722
    {
723
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
724
                 "GDAL MRF: Version number error!");
725
        return CE_Failure;
1✔
726
    }
727
    // Size of one version index
728
    for (int bcount = 1; bcount <= nBands; bcount++)
2✔
729
    {
730
        MRFRasterBand *srcband =
731
            reinterpret_cast<MRFRasterBand *>(GetRasterBand(bcount));
1✔
732
        srcband->img.idxoffset += idxSize * verCount;
1✔
733
        for (int l = 0; l < srcband->GetOverviewCount(); l++)
1✔
734
        {
735
            MRFRasterBand *band =
736
                reinterpret_cast<MRFRasterBand *>(srcband->GetOverview(l));
×
737
            if (band != nullptr)
×
738
                band->img.idxoffset += idxSize * verCount;
×
739
        }
740
    }
741
    hasVersions = 0;
1✔
742
    return CE_None;
1✔
743
}
744

745
CPLErr MRFDataset::LevelInit(const int l)
2✔
746
{
747
    // Test that this level does exist
748
    if (l < 0 || l >= cds->GetRasterBand(1)->GetOverviewCount())
2✔
749
    {
750
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
751
                 "GDAL MRF: Overview not present!");
752
        return CE_Failure;
1✔
753
    }
754

755
    MRFRasterBand *srcband = reinterpret_cast<MRFRasterBand *>(
756
        cds->GetRasterBand(1)->GetOverview(l));
1✔
757

758
    // Copy the sizes from this level
759
    full = srcband->img;
1✔
760
    current = srcband->img;
1✔
761
    current.size.c = cds->current.size.c;
1✔
762
    scale = cds->scale;
1✔
763
    const auto poSRS = cds->GetSpatialRef();
1✔
764
    if (poSRS)
1✔
765
        m_oSRS = *poSRS;
1✔
766

767
    SetMetadataItem("INTERLEAVE", OrderName(current.order), "IMAGE_STRUCTURE");
1✔
768
    SetMetadataItem("COMPRESSION", CompName(current.comp), "IMAGE_STRUCTURE");
1✔
769

770
    bGeoTransformValid = (CE_None == cds->GetGeoTransform(m_gt));
1✔
771
    for (int i = 0; i < l + 1; i++)
4✔
772
    {
773
        m_gt[1] *= scale;
3✔
774
        m_gt[5] *= scale;
3✔
775
    }
776

777
    nRasterXSize = current.size.x;
1✔
778
    nRasterYSize = current.size.y;
1✔
779
    nBands = current.size.c;
1✔
780

781
    // Add the bands, copy constructor so they can be closed independently
782
    for (int i = 1; i <= nBands; i++)
2✔
783
        SetBand(i, new MRFLRasterBand(reinterpret_cast<MRFRasterBand *>(
1✔
784
                       cds->GetRasterBand(i)->GetOverview(l))));
1✔
785
    return CE_None;
1✔
786
}
787

788
// Is the string positive or not
789
inline bool on(const char *pszValue)
1,279✔
790
{
791
    if (!pszValue || pszValue[0] == 0)
1,279✔
792
        return false;
110✔
793
    return EQUAL(pszValue, "ON") || EQUAL(pszValue, "TRUE") ||
2,326✔
794
           EQUAL(pszValue, "YES");
2,326✔
795
}
796

797
/**
798
 *\brief Initialize the image structure and the dataset from the XML Raster node
799
 *
800
 * @param image the structure to be initialized
801
 * @param ds the parent dataset, some things get inherited
802
 * @param defimage defimage
803
 *
804
 * The structure should be initialized with the default values as much as
805
 *possible
806
 *
807
 */
808

809
static CPLErr Init_Raster(ILImage &image, MRFDataset *ds, CPLXMLNode *defimage)
366✔
810
{
811
    CPLXMLNode *node;  // temporary
812
    if (!defimage)
366✔
813
    {
814
        CPLError(CE_Failure, CPLE_AppDefined,
×
815
                 "GDAL MRF: Can't find raster info");
816
        return CE_Failure;
×
817
    }
818

819
    // Size is mandatory
820
    node = CPLGetXMLNode(defimage, "Size");
366✔
821

822
    if (node)
366✔
823
    {
824
        image.size = ILSize(static_cast<int>(getXMLNum(node, "x", -1)),
366✔
825
                            static_cast<int>(getXMLNum(node, "y", -1)),
366✔
826
                            static_cast<int>(getXMLNum(node, "z", 1)),
366✔
827
                            static_cast<int>(getXMLNum(node, "c", 1)), 0);
366✔
828
    }
829

830
    // Basic checks
831
    if (!node || image.size.x < 1 || image.size.y < 1 || image.size.z < 0 ||
366✔
832
        image.size.c < 0 || !GDALCheckBandCount(image.size.c, FALSE))
732✔
833
    {
834
        CPLError(CE_Failure, CPLE_AppDefined, "Raster size missing or invalid");
×
835
        return CE_Failure;
×
836
    }
837

838
    //  Pagesize, defaults to 512,512,1,c
839
    image.pagesize = ILSize(std::min(512, image.size.x),
366✔
840
                            std::min(512, image.size.y), 1, image.size.c);
366✔
841

842
    node = CPLGetXMLNode(defimage, "PageSize");
366✔
843
    if (node)
366✔
844
    {
845
        image.pagesize =
366✔
846
            ILSize(static_cast<int>(getXMLNum(node, "x", image.pagesize.x)),
366✔
847
                   static_cast<int>(getXMLNum(node, "y", image.pagesize.y)),
366✔
848
                   1,  // One slice at a time, forced
849
                   static_cast<int>(getXMLNum(node, "c", image.pagesize.c)));
366✔
850
        if (image.pagesize.x < 1 || image.pagesize.y < 1 ||
366✔
851
            image.pagesize.c <= 0)
366✔
852
        {
853
            CPLError(CE_Failure, CPLE_IllegalArg, "Invalid PageSize");
×
854
            return CE_Failure;
×
855
        }
856
    }
857

858
    // Page Encoding, defaults to PNG
859
    const char *pszCompression = CPLGetXMLValue(defimage, "Compression", "PNG");
366✔
860
    image.comp = CompToken(pszCompression);
366✔
861
    if (image.comp == IL_ERR_COMP)
366✔
862
    {
863
        CPLError(CE_Failure, CPLE_IllegalArg,
×
864
                 "GDAL MRF: Compression %s is unknown", pszCompression);
865
        return CE_Failure;
×
866
    }
867

868
    // Is there a palette?
869
    //
870
    // GDAL only supports RGB+A palette, the other modes don't work
871
    //
872

873
    if ((image.pagesize.c == 1) &&
676✔
874
        (nullptr != (node = CPLGetXMLNode(defimage, "Palette"))))
310✔
875
    {
876
        int entries = static_cast<int>(getXMLNum(node, "Size", 255));
1✔
877
        GDALPaletteInterp eInterp = GPI_RGB;
1✔
878
        if ((entries > 0) && (entries < 257))
1✔
879
        {
880
            GDALColorEntry ce_start = {0, 0, 0, 255}, ce_end = {0, 0, 0, 255};
1✔
881

882
            // Create it and initialize it to black opaque
883
            GDALColorTable *poColorTable = new GDALColorTable(eInterp);
1✔
884
            poColorTable->CreateColorRamp(0, &ce_start, entries - 1, &ce_end);
1✔
885
            // Read the values
886
            CPLXMLNode *p = CPLGetXMLNode(node, "Entry");
1✔
887
            if (p)
1✔
888
            {
889
                // Initialize the first entry
890
                ce_start = GetXMLColorEntry(p);
1✔
891
                int start_idx = static_cast<int>(getXMLNum(p, "idx", 0));
1✔
892
                if (start_idx < 0)
1✔
893
                {
894
                    CPLError(CE_Failure, CPLE_IllegalArg,
×
895
                             "GDAL MRF: Palette index %d not allowed",
896
                             start_idx);
897
                    delete poColorTable;
×
898
                    return CE_Failure;
×
899
                }
900
                poColorTable->SetColorEntry(start_idx, &ce_start);
1✔
901
                while (nullptr != (p = SearchXMLSiblings(p, "Entry")))
256✔
902
                {
903
                    // For every entry, create a ramp
904
                    ce_end = GetXMLColorEntry(p);
255✔
905
                    int end_idx =
906
                        static_cast<int>(getXMLNum(p, "idx", start_idx + 1));
255✔
907
                    if ((end_idx <= start_idx) || (start_idx >= entries))
255✔
908
                    {
909
                        CPLError(CE_Failure, CPLE_IllegalArg,
×
910
                                 "GDAL MRF: Index Error at index %d", end_idx);
911
                        delete poColorTable;
×
912
                        return CE_Failure;
×
913
                    }
914
                    poColorTable->CreateColorRamp(start_idx, &ce_start, end_idx,
255✔
915
                                                  &ce_end);
916
                    ce_start = ce_end;
255✔
917
                    start_idx = end_idx;
255✔
918
                }
919
            }
920

921
            ds->SetColorTable(poColorTable);
1✔
922
        }
923
        else
924
        {
925
            CPLError(CE_Failure, CPLE_IllegalArg,
×
926
                     "GDAL MRF: Palette definition error");
927
            return CE_Failure;
×
928
        }
929
    }
930

931
    // Order of increment
932
    if (image.pagesize.c != image.size.c && image.pagesize.c != 1)
366✔
933
    {
934
        // Fixes heap buffer overflow in
935
        // GDALMRFRasterBand::ReadInterleavedBlock() See
936
        // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=2884
937
        CPLError(CE_Failure, CPLE_NotSupported,
×
938
                 "GDAL MRF: image.pagesize.c = %d and image.size.c = %d",
939
                 image.pagesize.c, image.size.c);
940
        return CE_Failure;
×
941
    }
942

943
    image.order = OrderToken(
366✔
944
        CPLGetXMLValue(defimage, "Order",
945
                       (image.pagesize.c != image.size.c) ? "BAND" : "PIXEL"));
366✔
946
    if (image.order == IL_ERR_ORD)
366✔
947
    {
948
        CPLError(CE_Failure, CPLE_AppDefined, "GDAL MRF: Order %s is unknown",
×
949
                 CPLGetXMLValue(defimage, "Order", nullptr));
950
        return CE_Failure;
×
951
    }
952

953
    const char *photo_val = CPLGetXMLValue(defimage, "Photometric", nullptr);
366✔
954
    if (photo_val)
366✔
955
        ds->SetPhotometricInterpretation(photo_val);
5✔
956

957
    image.quality = atoi(CPLGetXMLValue(defimage, "Quality", "85"));
366✔
958
    if (image.quality < 0 || image.quality > 99)
366✔
959
    {
960
        CPLError(CE_Warning, CPLE_AppDefined,
×
961
                 "GDAL MRF: Quality setting error, using default of 85");
962
        image.quality = 85;
×
963
    }
964

965
    // Data Type, use GDAL Names
966
    image.dt = GDALGetDataTypeByName(
366✔
967
        CPLGetXMLValue(defimage, "DataType", GDALGetDataTypeName(image.dt)));
968
    if (image.dt == GDT_Unknown || GDALGetDataTypeSizeBytes(image.dt) == 0)
366✔
969
    {
970
        CPLError(CE_Failure, CPLE_AppDefined, "GDAL MRF: Unsupported type");
×
971
        return CE_Failure;
×
972
    }
973

974
    // Check the endianness if needed, assume host order
975
    if (is_Endianness_Dependent(image.dt, image.comp))
366✔
976
        image.nbo = on(CPLGetXMLValue(defimage, "NetByteOrder", "No"));
69✔
977

978
    CPLXMLNode *DataValues = CPLGetXMLNode(defimage, "DataValues");
366✔
979
    if (nullptr != DataValues)
366✔
980
    {
981
        const char *pszValue = CPLGetXMLValue(DataValues, "NoData", nullptr);
27✔
982
        if (pszValue)
27✔
983
            ds->SetNoDataValue(pszValue);
20✔
984
        pszValue = CPLGetXMLValue(DataValues, "min", nullptr);
27✔
985
        if (pszValue)
27✔
986
            ds->SetMinValue(pszValue);
7✔
987
        pszValue = CPLGetXMLValue(DataValues, "max", nullptr);
27✔
988
        if (pszValue)
27✔
989
            ds->SetMaxValue(pszValue);
7✔
990
    }
991

992
    // Calculate the page size in bytes
993
    const int nDTSize = GDALGetDataTypeSizeBytes(image.dt);
366✔
994
    if (nDTSize <= 0 || image.pagesize.z <= 0 ||
366✔
995
        image.pagesize.x > INT_MAX / image.pagesize.y ||
366✔
996
        image.pagesize.x * image.pagesize.y > INT_MAX / image.pagesize.z ||
366✔
997
        image.pagesize.x * image.pagesize.y * image.pagesize.z >
366✔
998
            INT_MAX / image.pagesize.c ||
366✔
999
        image.pagesize.x * image.pagesize.y * image.pagesize.z *
366✔
1000
                image.pagesize.c >
366✔
1001
            INT_MAX / nDTSize)
366✔
1002
    {
1003
        CPLError(CE_Failure, CPLE_AppDefined, "MRF page size too big");
×
1004
        return CE_Failure;
×
1005
    }
1006
    image.pageSizeBytes = nDTSize * image.pagesize.x * image.pagesize.y *
366✔
1007
                          image.pagesize.z * image.pagesize.c;
366✔
1008

1009
    // Calculate the page count, including the total for the level
1010
    image.pagecount = pcount(image.size, image.pagesize);
366✔
1011
    if (image.pagecount.l < 0)
366✔
1012
    {
1013
        return CE_Failure;
×
1014
    }
1015

1016
    // Data File Name and base offset
1017
    image.datfname =
1018
        getFname(defimage, "DataFile", ds->GetFname(), ILComp_Ext[image.comp]);
366✔
1019
    image.dataoffset = static_cast<int>(
366✔
1020
        getXMLNum(CPLGetXMLNode(defimage, "DataFile"), "offset", 0.0));
366✔
1021

1022
    // Index File Name and base offset
1023
    image.idxfname = getFname(defimage, "IndexFile", ds->GetFname(), ".idx");
366✔
1024
    image.idxoffset = static_cast<int>(
366✔
1025
        getXMLNum(CPLGetXMLNode(defimage, "IndexFile"), "offset", 0.0));
366✔
1026

1027
    return CE_None;
366✔
1028
}
1029

1030
char **MRFDataset::GetFileList()
162✔
1031
{
1032
    char **papszFileList = nullptr;
162✔
1033

1034
    string usename = fname;
162✔
1035
    if (!publicname.empty())
162✔
1036
        usename = publicname;
×
1037
    // Add the header file name if it is real
1038
    VSIStatBufL sStat;
1039
    if (VSIStatExL(usename.c_str(), &sStat, VSI_STAT_EXISTS_FLAG) == 0)
162✔
1040
        papszFileList = CSLAddString(papszFileList, usename.c_str());
162✔
1041

1042
    // These two should be real
1043
    // We don't really want to add these files, since they will be erased when
1044
    // an mrf is overwritten This collides with the concept that the data file
1045
    // never shrinks.  Same goes with the index, in case we just want to add
1046
    // things to it.
1047
    //    papszFileList = CSLAddString( papszFileList, full.datfname);
1048
    //    papszFileList = CSLAddString( papszFileList, full.idxfname);
1049
    //    if (!source.empty())
1050
    // papszFileList = CSLAddString( papszFileList, source);
1051

1052
    return papszFileList;
324✔
1053
}
1054

1055
// Try to create all the folders in the path in sequence, ignore errors
1056
static void mkdir_r(string const &fname)
4✔
1057
{
1058
    size_t loc = fname.find_first_of("\\/");
4✔
1059
    if (loc == string::npos)
4✔
1060
        return;
×
1061
    while (true)
1062
    {
1063
        ++loc;
5✔
1064
        loc = fname.find_first_of("\\/", loc);
5✔
1065
        if (loc == string::npos)
5✔
1066
            break;
4✔
1067
        VSIMkdir(fname.substr(0, loc).c_str(), 0);
1✔
1068
    }
1069
}
1070

1071
// Returns the dataset index file or null
1072
VSILFILE *MRFDataset::IdxFP()
7,268✔
1073
{
1074
    if (ifp.FP != nullptr)
7,268✔
1075
        return ifp.FP;
6,999✔
1076

1077
    // If missing is set, we already checked, there is no index
1078
    if (missing)
269✔
1079
        return nullptr;
×
1080

1081
    // If name starts with '(' it is not a real file name
1082
    if (current.idxfname[0] == '(')
269✔
1083
        return nullptr;
×
1084

1085
    const char *mode = "rb";
269✔
1086
    ifp.acc = GF_Read;
269✔
1087

1088
    if (eAccess == GA_Update || !source.empty())
269✔
1089
    {
1090
        mode = "r+b";
159✔
1091
        ifp.acc = GF_Write;
159✔
1092
    }
1093

1094
    ifp.FP = VSIFOpenL(current.idxfname, mode);
269✔
1095

1096
    // If file didn't open for reading and no_errors is set, just return null
1097
    // and make a note
1098
    if (ifp.FP == nullptr && eAccess == GA_ReadOnly && no_errors)
269✔
1099
    {
1100
        missing = 1;
×
1101
        return nullptr;
×
1102
    }
1103

1104
    // need to create the index file
1105
    if (ifp.FP == nullptr && !bCrystalized &&
369✔
1106
        (eAccess == GA_Update || !source.empty()))
100✔
1107
    {
1108
        mode = "w+b";
100✔
1109
        ifp.FP = VSIFOpenL(current.idxfname, mode);
100✔
1110
    }
1111

1112
    if (nullptr == ifp.FP && !source.empty())
269✔
1113
    {
1114
        // caching and cloning, try making the folder and attempt again
1115
        mkdir_r(current.idxfname);
4✔
1116
        ifp.FP = VSIFOpenL(current.idxfname, mode);
4✔
1117
    }
1118

1119
    GIntBig expected_size = idxSize;
269✔
1120
    if (clonedSource)
269✔
1121
        expected_size *= 2;
2✔
1122

1123
    if (nullptr != ifp.FP)
269✔
1124
    {
1125
        if (!bCrystalized &&
409✔
1126
            !CheckFileSize(current.idxfname, expected_size, GA_Update))
150✔
1127
        {
1128
            CPLError(CE_Failure, CPLE_FileIO,
×
1129
                     "MRF: Can't extend the cache index file %s",
1130
                     current.idxfname.c_str());
1131
            return nullptr;
×
1132
        }
1133

1134
        if (source.empty())
259✔
1135
            return ifp.FP;
255✔
1136

1137
        // Make sure the index is large enough before proceeding
1138
        // Timeout in .1 seconds, can't really guarantee the accuracy
1139
        // So this is about half second, should be sufficient
1140
        int timeout = 5;
4✔
1141
        do
×
1142
        {
1143
            if (CheckFileSize(current.idxfname, expected_size, GA_ReadOnly))
4✔
1144
                return ifp.FP;
4✔
1145
            CPLSleep(0.100); /* 100 ms */
×
1146
        } while (--timeout);
×
1147

1148
        // If we get here it is a time-out
1149
        CPLError(CE_Failure, CPLE_AppDefined,
×
1150
                 "GDAL MRF: Timeout on fetching cloned index file %s\n",
1151
                 current.idxfname.c_str());
1152
        return nullptr;
×
1153
    }
1154

1155
    // If single tile, and no index file, let the caller figure it out
1156
    if (IsSingleTile())
10✔
1157
        return nullptr;
6✔
1158

1159
    // Error if this is not a caching MRF
1160
    if (source.empty())
4✔
1161
    {
1162
        CPLError(CE_Failure, CPLE_AppDefined,
×
1163
                 "GDAL MRF: Can't open index file %s\n",
1164
                 current.idxfname.c_str());
1165
        return nullptr;
×
1166
    }
1167

1168
    // Caching/Cloning MRF and index could be read only
1169
    // Is this actually works, we should try again, maybe somebody else just
1170
    // created the file?
1171
    mode = "rb";
4✔
1172
    ifp.acc = GF_Read;
4✔
1173
    ifp.FP = VSIFOpenL(current.idxfname, mode);
4✔
1174
    if (nullptr != ifp.FP)
4✔
1175
        return ifp.FP;
×
1176

1177
    // Caching and index file absent, create it
1178
    // Due to a race, multiple processes might do this at the same time, but
1179
    // that is fine
1180
    ifp.FP = VSIFOpenL(current.idxfname, "wb");
4✔
1181
    if (nullptr == ifp.FP)
4✔
1182
    {
1183
        CPLError(CE_Failure, CPLE_AppDefined,
×
1184
                 "Can't create the MRF cache index file %s",
1185
                 current.idxfname.c_str());
1186
        return nullptr;
×
1187
    }
1188
    VSIFCloseL(ifp.FP);
4✔
1189
    ifp.FP = nullptr;
4✔
1190

1191
    // Make it large enough for caching and for cloning
1192
    if (!CheckFileSize(current.idxfname, expected_size, GA_Update))
4✔
1193
    {
1194
        CPLError(CE_Failure, CPLE_AppDefined,
×
1195
                 "Can't extend the cache index file %s",
1196
                 current.idxfname.c_str());
1197
        return nullptr;
×
1198
    }
1199

1200
    // Try opening it again in rw mode so we can read and write
1201
    mode = "r+b";
4✔
1202
    ifp.acc = GF_Write;
4✔
1203
    ifp.FP = VSIFOpenL(current.idxfname.c_str(), mode);
4✔
1204

1205
    if (nullptr == ifp.FP)
4✔
1206
    {
1207
        CPLError(CE_Failure, CPLE_AppDefined,
×
1208
                 "GDAL MRF: Can't reopen cache index file %s\n",
1209
                 full.idxfname.c_str());
1210
        return nullptr;
×
1211
    }
1212
    return ifp.FP;
4✔
1213
}
1214

1215
//
1216
// Returns the dataset data file or null
1217
// Data file is opened either in Read or Append mode, never in straight write
1218
//
1219
VSILFILE *MRFDataset::DataFP()
7,143✔
1220
{
1221
    if (dfp.FP != nullptr)
7,143✔
1222
        return dfp.FP;
6,877✔
1223
    const char *mode = "rb";
266✔
1224
    dfp.acc = GF_Read;
266✔
1225

1226
    // Open it for writing if updating or if caching
1227
    if (eAccess == GA_Update || !source.empty())
266✔
1228
    {
1229
        mode = "a+b";
158✔
1230
        dfp.acc = GF_Write;
158✔
1231
    }
1232

1233
    dfp.FP = VSIFOpenL(current.datfname, mode);
266✔
1234
    if (dfp.FP)
266✔
1235
        return dfp.FP;
266✔
1236

1237
    // It could be a caching MRF
1238
    if (source.empty())
×
1239
        goto io_error;
×
1240

1241
    // May be there but read only, remember that it was open that way
1242
    mode = "rb";
×
1243
    dfp.acc = GF_Read;
×
1244
    dfp.FP = VSIFOpenL(current.datfname, mode);
×
1245
    if (nullptr != dfp.FP)
×
1246
    {
1247
        CPLDebug("MRF_IO", "Opened %s RO mode %s\n", current.datfname.c_str(),
×
1248
                 mode);
1249
        return dfp.FP;
×
1250
    }
1251

1252
    if (source.empty())
×
1253
        goto io_error;
×
1254

1255
    // caching, maybe the folder didn't exist
1256
    mkdir_r(current.datfname);
×
1257
    mode = "a+b";
×
1258
    dfp.acc = GF_Write;
×
1259
    dfp.FP = VSIFOpenL(current.datfname, mode);
×
1260
    if (dfp.FP)
×
1261
        return dfp.FP;
×
1262

1263
io_error:
×
1264
    dfp.FP = nullptr;
×
1265
    CPLError(CE_Failure, CPLE_FileIO, "GDAL MRF: %s : %s", strerror(errno),
×
1266
             current.datfname.c_str());
1267
    return nullptr;
×
1268
}
1269

1270
// Builds an XML tree from the current MRF.  If written to a file it becomes an
1271
// MRF
1272
CPLXMLNode *MRFDataset::BuildConfig()
334✔
1273
{
1274
    CPLXMLNode *config = CPLCreateXMLNode(nullptr, CXT_Element, "MRF_META");
334✔
1275

1276
    if (!source.empty())
334✔
1277
    {
1278
        CPLXMLNode *psCachedSource =
1279
            CPLCreateXMLNode(config, CXT_Element, "CachedSource");
4✔
1280
        // Should wrap the string in CDATA, in case it is XML
1281
        CPLXMLNode *psSource =
1282
            CPLCreateXMLElementAndValue(psCachedSource, "Source", source);
4✔
1283
        if (clonedSource)
4✔
1284
            CPLSetXMLValue(psSource, "#clone", "true");
×
1285
    }
1286

1287
    // Use the full size
1288
    CPLXMLNode *raster = CPLCreateXMLNode(config, CXT_Element, "Raster");
334✔
1289

1290
    // Preserve the file names if not the default ones
1291
    if (full.datfname != getFname(GetFname(), ILComp_Ext[full.comp]))
334✔
1292
        CPLCreateXMLElementAndValue(raster, "DataFile", full.datfname.c_str());
×
1293
    if (full.idxfname != getFname(GetFname(), ".idx"))
334✔
1294
        CPLCreateXMLElementAndValue(raster, "IndexFile", full.idxfname.c_str());
×
1295
    if (spacing != 0)
334✔
1296
        XMLSetAttributeVal(raster, "Spacing", static_cast<double>(spacing),
×
1297
                           "%.0f");
1298

1299
    XMLSetAttributeVal(raster, "Size", full.size, "%.0f");
334✔
1300
    XMLSetAttributeVal(raster, "PageSize", full.pagesize, "%.0f");
334✔
1301

1302
#ifdef HAVE_PNG
1303
    if (full.comp != IL_PNG)
334✔
1304
#endif
1305
    {
1306
        CPLCreateXMLElementAndValue(raster, "Compression", CompName(full.comp));
194✔
1307
    }
1308

1309
    if (full.dt != GDT_Byte)
334✔
1310
        CPLCreateXMLElementAndValue(raster, "DataType",
202✔
1311
                                    GDALGetDataTypeName(full.dt));
1312

1313
    // special photometric interpretation
1314
    if (!photometric.empty())
334✔
1315
        CPLCreateXMLElementAndValue(raster, "Photometric", photometric);
4✔
1316

1317
    if (!vNoData.empty() || !vMin.empty() || !vMax.empty())
334✔
1318
    {
1319
        CPLXMLNode *values =
1320
            CPLCreateXMLNode(raster, CXT_Element, "DataValues");
43✔
1321
        XMLSetAttributeVal(values, "NoData", vNoData);
43✔
1322
        XMLSetAttributeVal(values, "min", vMin);
43✔
1323
        XMLSetAttributeVal(values, "max", vMax);
43✔
1324
    }
1325

1326
    // palette, if we have one
1327
    if (poColorTable != nullptr)
334✔
1328
    {
1329
        const char *pfrmt = "%.0f";
1✔
1330
        CPLXMLNode *pal = CPLCreateXMLNode(raster, CXT_Element, "Palette");
1✔
1331
        int sz = poColorTable->GetColorEntryCount();
1✔
1332
        if (sz != 256)
1✔
1333
            XMLSetAttributeVal(pal, "Size", poColorTable->GetColorEntryCount());
×
1334
        // RGB or RGBA for now
1335
        for (int i = 0; i < sz; i++)
257✔
1336
        {
1337
            CPLXMLNode *entry = CPLCreateXMLNode(pal, CXT_Element, "Entry");
256✔
1338
            const GDALColorEntry *ent = poColorTable->GetColorEntry(i);
256✔
1339
            // No need to set the index, it is always from 0 no size-1
1340
            XMLSetAttributeVal(entry, "c1", ent->c1, pfrmt);
256✔
1341
            XMLSetAttributeVal(entry, "c2", ent->c2, pfrmt);
256✔
1342
            XMLSetAttributeVal(entry, "c3", ent->c3, pfrmt);
256✔
1343
            if (ent->c4 != 255)
256✔
1344
                XMLSetAttributeVal(entry, "c4", ent->c4, pfrmt);
×
1345
        }
1346
    }
1347

1348
    if (is_Endianness_Dependent(full.dt, full.comp))  // Need to set the order
334✔
1349
        CPLCreateXMLElementAndValue(raster, "NetByteOrder",
62✔
1350
                                    (full.nbo || NET_ORDER) ? "TRUE" : "FALSE");
62✔
1351

1352
    if (full.quality > 0 && full.quality != 85)
334✔
1353
        CPLCreateXMLElementAndValue(raster, "Quality",
12✔
1354
                                    CPLOPrintf("%d", full.quality));
24✔
1355

1356
    // Done with the raster node
1357

1358
    if (scale != 0.0)
334✔
1359
    {
1360
        CPLCreateXMLNode(config, CXT_Element, "Rsets");
×
1361
        CPLSetXMLValue(config, "Rsets.#model", "uniform");
×
1362
        CPLSetXMLValue(config, "Rsets.#scale", PrintDouble(scale));
×
1363
    }
1364
    CPLXMLNode *gtags = CPLCreateXMLNode(config, CXT_Element, "GeoTags");
334✔
1365

1366
    // Do we have an affine transform different from identity?
1367
    GDALGeoTransform gt;
334✔
1368
    if ((MRFDataset::GetGeoTransform(gt) == CE_None) &&
668✔
1369
        (gt[0] != 0 || gt[1] != 1 || gt[2] != 0 || gt[3] != 0 || gt[4] != 0 ||
334✔
1370
         gt[5] != 1))
197✔
1371
    {
1372
        double minx = gt[0];
137✔
1373
        double maxx = gt[1] * full.size.x + minx;
137✔
1374
        double maxy = gt[3];
137✔
1375
        double miny = gt[5] * full.size.y + maxy;
137✔
1376
        CPLXMLNode *bbox = CPLCreateXMLNode(gtags, CXT_Element, "BoundingBox");
137✔
1377
        XMLSetAttributeVal(bbox, "minx", minx);
137✔
1378
        XMLSetAttributeVal(bbox, "miny", miny);
137✔
1379
        XMLSetAttributeVal(bbox, "maxx", maxx);
137✔
1380
        XMLSetAttributeVal(bbox, "maxy", maxy);
137✔
1381
    }
1382

1383
    const char *pszProj = GetProjectionRef();
334✔
1384
    if (pszProj && (!EQUAL(pszProj, "")))
334✔
1385
        CPLCreateXMLElementAndValue(gtags, "Projection", pszProj);
138✔
1386

1387
    if (optlist.Count() != 0)
334✔
1388
    {
1389
        CPLString options;
52✔
1390
        for (int i = 0; i < optlist.size(); i++)
54✔
1391
        {
1392
            options += optlist[i];
28✔
1393
            options += ' ';
28✔
1394
        }
1395
        options.pop_back();
26✔
1396
        CPLCreateXMLElementAndValue(config, "Options", options);
26✔
1397
    }
1398

1399
    return config;
334✔
1400
}
1401

1402
/**
1403
 * \brief Populates the dataset variables from the XML definition
1404
 *
1405
 *
1406
 */
1407
CPLErr MRFDataset::Initialize(CPLXMLNode *config)
366✔
1408
{
1409
    // We only need a basic initialization here, usually gets overwritten by the
1410
    // image params
1411
    full.dt = GDT_Byte;
366✔
1412
    full.hasNoData = false;
366✔
1413
    full.NoDataValue = 0;
366✔
1414
    Quality = 85;
366✔
1415

1416
    CPLErr ret = Init_Raster(full, this, CPLGetXMLNode(config, "Raster"));
366✔
1417
    if (CE_None != ret)
366✔
1418
        return ret;
×
1419

1420
    hasVersions = on(CPLGetXMLValue(config, "Raster.versioned", "no"));
366✔
1421
    mp_safe = on(CPLGetXMLValue(config, "Raster.mp_safe", "no"));
366✔
1422
    spacing = atoi(CPLGetXMLValue(config, "Raster.Spacing", "0"));
366✔
1423

1424
    // The zslice defined in the file wins over the oo or the file argument
1425
    if (CPLGetXMLNode(config, "Raster.zslice"))
366✔
1426
        zslice = atoi(CPLGetXMLValue(config, "Raster.zslice", "0"));
×
1427

1428
    Quality = full.quality;
366✔
1429

1430
    // Bounding box
1431
    CPLXMLNode *bbox = CPLGetXMLNode(config, "GeoTags.BoundingBox");
366✔
1432
    if (nullptr != bbox)
366✔
1433
    {
1434
        double x0, x1, y0, y1;
1435

1436
        x0 = atof(CPLGetXMLValue(bbox, "minx", "0"));
161✔
1437
        x1 = atof(CPLGetXMLValue(bbox, "maxx", "1"));
161✔
1438
        y1 = atof(CPLGetXMLValue(bbox, "maxy", "1"));
161✔
1439
        y0 = atof(CPLGetXMLValue(bbox, "miny", "0"));
161✔
1440

1441
        m_gt[0] = x0;
161✔
1442
        m_gt[1] = (x1 - x0) / full.size.x;
161✔
1443
        m_gt[2] = 0;
161✔
1444
        m_gt[3] = y1;
161✔
1445
        m_gt[4] = 0;
161✔
1446
        m_gt[5] = (y0 - y1) / full.size.y;
161✔
1447
        bGeoTransformValid = TRUE;
161✔
1448
    }
1449

1450
    const char *pszRawProjFromXML =
1451
        CPLGetXMLValue(config, "GeoTags.Projection", "");
366✔
1452
    if (strlen(pszRawProjFromXML) != 0)
366✔
1453
        m_oSRS.SetFromUserInput(
162✔
1454
            pszRawProjFromXML,
1455
            OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1456

1457
    // Copy the full size to current, data and index are not yet open
1458
    current = full;
366✔
1459
    if (current.size.z != 1)
366✔
1460
    {
1461
        SetMetadataItem("ZSIZE", CPLOPrintf("%d", current.size.z),
×
1462
                        "IMAGE_STRUCTURE");
×
1463
        SetMetadataItem("ZSLICE", CPLOPrintf("%d", zslice), "IMAGE_STRUCTURE");
×
1464
        // Capture the zslice in pagesize.l
1465
        current.pagesize.l = zslice;
×
1466
        // Adjust offset for base image
1467
        if (full.size.z <= 0)
×
1468
        {
1469
            CPLError(CE_Failure, CPLE_AppDefined,
×
1470
                     "GDAL MRF: Invalid Raster.z value");
1471
            return CE_Failure;
×
1472
        }
1473
        if (zslice >= full.size.z)
×
1474
        {
1475
            CPLError(CE_Failure, CPLE_AppDefined, "GDAL MRF: Invalid slice");
×
1476
            return CE_Failure;
×
1477
        }
1478

1479
        current.idxoffset +=
×
1480
            (current.pagecount.l / full.size.z) * zslice * sizeof(ILIdx);
×
1481
    }
1482

1483
    // Dataset metadata setup
1484
    SetMetadataItem("INTERLEAVE", OrderName(current.order), "IMAGE_STRUCTURE");
366✔
1485
    SetMetadataItem("COMPRESSION", CompName(current.comp), "IMAGE_STRUCTURE");
366✔
1486

1487
    if (is_Endianness_Dependent(current.dt, current.comp))
366✔
1488
        SetMetadataItem("NETBYTEORDER", current.nbo ? "TRUE" : "FALSE",
69✔
1489
                        "IMAGE_STRUCTURE");
69✔
1490

1491
    // Open the files for the current image, either RW or RO
1492
    nRasterXSize = current.size.x;
366✔
1493
    nRasterYSize = current.size.y;
366✔
1494
    nBands = current.size.c;
366✔
1495

1496
    if (!nBands || !nRasterXSize || !nRasterYSize)
366✔
1497
    {
1498
        CPLError(CE_Failure, CPLE_AppDefined, "GDAL MRF: Image size missing");
×
1499
        return CE_Failure;
×
1500
    }
1501

1502
    // Pick up the source data image, if there is one
1503
    source = CPLGetXMLValue(config, "CachedSource.Source", "");
366✔
1504
    // Is it a clone?
1505
    clonedSource =
366✔
1506
        on(CPLGetXMLValue(config, "CachedSource.Source.clone", "no"));
366✔
1507
    // Pick up the options, if any
1508
    optlist.Assign(CSLTokenizeString2(
1509
        CPLGetXMLValue(config, "Options", nullptr), " \t\n\r",
1510
        CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
366✔
1511

1512
    // Load all the options in the IMAGE_STRUCTURE metadata
1513
    for (int i = 0; i < optlist.Count(); i++)
397✔
1514
    {
1515
        CPLString s(optlist[i]);
62✔
1516
        size_t nSepPos = s.find_first_of(":=");
31✔
1517
        if (std::string::npos != nSepPos)
31✔
1518
        {
1519
            s.resize(nSepPos);
31✔
1520
            SetMetadataItem(s, optlist.FetchNameValue(s), "IMAGE_STRUCTURE");
31✔
1521
        }
1522
    }
1523

1524
    // We have the options, so we can call rasterband
1525
    for (int i = 1; i <= nBands; i++)
798✔
1526
    {
1527
        // The overviews are low resolution copies of the current one.
1528
        MRFRasterBand *band = newMRFRasterBand(this, current, i);
484✔
1529
        if (!band)
484✔
1530
            return CE_Failure;
52✔
1531

1532
        GDALColorInterp ci = GCI_Undefined;
432✔
1533

1534
        // Default color interpretation
1535
        switch (nBands)
432✔
1536
        {
1537
            case 1:
264✔
1538
            case 2:
1539
                ci = (i == 1) ? GCI_GrayIndex : GCI_AlphaBand;
264✔
1540
                break;
264✔
1541
            case 3:
153✔
1542
            case 4:
1543
                if (i < 3)
153✔
1544
                    ci = (i == 1) ? GCI_RedBand : GCI_GreenBand;
100✔
1545
                else
1546
                    ci = (i == 3) ? GCI_BlueBand : GCI_AlphaBand;
53✔
1547
        }
1548

1549
        if (GetColorTable())
432✔
1550
            ci = GCI_PaletteIndex;
1✔
1551

1552
        // Legacy, deprecated
1553
        if (optlist.FetchBoolean("MULTISPECTRAL", FALSE))
432✔
1554
            ci = GCI_Undefined;
×
1555

1556
        // New style
1557
        if (!photometric.empty())
432✔
1558
        {
1559
            if ("MULTISPECTRAL" == photometric)
15✔
1560
                ci = GCI_Undefined;
×
1561
        }
1562

1563
        band->SetColorInterpretation(ci);
432✔
1564
        SetBand(i, band);
432✔
1565
    }
1566

1567
    CPLXMLNode *rsets = CPLGetXMLNode(config, "Rsets");
314✔
1568
    if (nullptr != rsets && nullptr != rsets->psChild)
314✔
1569
    {
1570
        // We have rsets
1571

1572
        // Regular spaced overlays, until everything fits in a single tile
1573
        if (EQUAL("uniform", CPLGetXMLValue(rsets, "model", "uniform")))
32✔
1574
        {
1575
            scale = getXMLNum(rsets, "scale", 2.0);
32✔
1576
            if (scale <= 1)
32✔
1577
            {
1578
                CPLError(CE_Failure, CPLE_AppDefined,
×
1579
                         "MRF: zoom factor less than unit not allowed");
1580
                return CE_Failure;
×
1581
            }
1582
            // Looks like there are overlays
1583
            AddOverviews(int(scale));
32✔
1584
        }
1585
        else
1586
        {
1587
            CPLError(CE_Failure, CPLE_AppDefined, "Unknown Rset definition");
×
1588
            return CE_Failure;
×
1589
        }
1590
    }
1591

1592
    idxSize = IdxSize(full, int(scale));
314✔
1593
    if (idxSize == 0)
314✔
1594
        return CE_Failure;
×
1595

1596
    // If not set by the bands, get a pageSizeBytes buffer
1597
    if (GetPBufferSize() == 0 && !SetPBuffer(current.pageSizeBytes))
314✔
1598
        return CE_Failure;
×
1599

1600
    if (hasVersions)
314✔
1601
    {                  // It has versions, but how many?
1602
        verCount = 0;  // Assume it only has one
5✔
1603
        VSIStatBufL statb;
1604
        //  If the file exists, compute the last version number
1605
        if (0 == VSIStatL(full.idxfname, &statb))
5✔
1606
            verCount = int(statb.st_size / idxSize - 1);
5✔
1607
    }
1608

1609
    return CE_None;
314✔
1610
}
1611

1612
static inline bool has_path(const CPLString &name)
×
1613
{
1614
    return name.find_first_of("/\\") != string::npos;
×
1615
}
1616

1617
// Does name look like an absolute gdal file name?
1618
static inline bool is_absolute(const CPLString &name)
4✔
1619
{
1620
    return (name.find_first_of("/\\") == 0)  // Starts with root
4✔
1621
           || (name.size() > 1 && name[1] == ':' &&
4✔
1622
               isalpha(static_cast<unsigned char>(
×
1623
                   name[0])))    // Starts with drive letter
×
1624
           || (name[0] == '<');  // Maybe it is XML
8✔
1625
}
1626

1627
// Add the dirname of path to the beginning of name, if it is relative
1628
// returns true if name was modified
1629
static inline bool make_absolute(CPLString &name, const CPLString &path)
4✔
1630
{
1631
    if (!is_absolute(name) && (path.find_first_of("/\\") != string::npos))
4✔
1632
    {
1633
        name = path.substr(0, path.find_last_of("/\\") + 1) + name;
4✔
1634
        return true;
4✔
1635
    }
1636
    return false;
×
1637
}
1638

1639
/**
1640
 *\brief Get the source dataset, open it if necessary
1641
 */
1642
GDALDataset *MRFDataset::GetSrcDS()
5✔
1643
{
1644
    if (poSrcDS)
5✔
1645
        return poSrcDS;
1✔
1646
    if (source.empty())
4✔
1647
        return nullptr;
×
1648

1649
    // Stub out the error handler
1650
    CPLPushErrorHandler(CPLQuietErrorHandler);
4✔
1651
    // Try open the source dataset as is
1652
    poSrcDS =
4✔
1653
        GDALDataset::FromHandle(GDALOpenShared(source.c_str(), GA_ReadOnly));
4✔
1654
    CPLPopErrorHandler();
4✔
1655

1656
    // It the open fails, try again with the current dataset path prepended
1657
    if (!poSrcDS && make_absolute(source, fname))
4✔
1658
        poSrcDS = GDALDataset::FromHandle(
4✔
1659
            GDALOpenShared(source.c_str(), GA_ReadOnly));
1660

1661
    if (0 == source.find("<MRF_META>") && has_path(fname))
4✔
1662
    {
1663
        // MRF XML source, might need to patch the file names with the current
1664
        // one
1665
        MRFDataset *poMRFDS = dynamic_cast<MRFDataset *>(poSrcDS);
×
1666
        if (!poMRFDS)
×
1667
        {
1668
            delete poSrcDS;
×
1669
            poSrcDS = nullptr;
×
1670
            return nullptr;
×
1671
        }
1672
        make_absolute(poMRFDS->current.datfname, fname);
×
1673
        make_absolute(poMRFDS->current.idxfname, fname);
×
1674
    }
1675
    mp_safe = true;  // Turn on MP safety
4✔
1676
    return poSrcDS;
4✔
1677
}
1678

1679
/**
1680
 *\brief Add or verify that all overlays exits
1681
 *
1682
 * @return size of the index file
1683
 */
1684

1685
GIntBig MRFDataset::AddOverviews(int scaleIn)
62✔
1686
{
1687
    // Fit the overlays
1688
    ILImage img = current;
62✔
1689
    while (1 != img.pagecount.x * img.pagecount.y)
134✔
1690
    {
1691
        // Adjust raster data for next level
1692
        // Adjust the offsets for indices left at this level
1693
        img.idxoffset += sizeof(ILIdx) * img.pagecount.l / img.size.z *
72✔
1694
                         (img.size.z - zslice);
72✔
1695

1696
        // Next overview size
1697
        img.size.x = pcount(img.size.x, scaleIn);
72✔
1698
        img.size.y = pcount(img.size.y, scaleIn);
72✔
1699
        img.size.l++;  // Increment the level
72✔
1700
        img.pagecount = pcount(img.size, img.pagesize);
72✔
1701

1702
        // And adjust the offset again, within next level
1703
        img.idxoffset += sizeof(ILIdx) * img.pagecount.l / img.size.z * zslice;
72✔
1704
        int l = static_cast<int>(img.size.l);
72✔
1705
        // Create and register the overviews for each band
1706
        for (int i = 1; i <= nBands; i++)
144✔
1707
        {
1708
            MRFRasterBand *b =
1709
                reinterpret_cast<MRFRasterBand *>(GetRasterBand(i));
72✔
1710
            if (!(b->GetOverview(l - 1)))
72✔
1711
                b->AddOverview(newMRFRasterBand(this, img, i, l));
72✔
1712
        }
1713
    }
1714

1715
    // Last adjustment, should be a single set of c and leftover z tiles
1716
    return img.idxoffset +
62✔
1717
           sizeof(ILIdx) * img.pagecount.l / img.size.z * (img.size.z - zslice);
124✔
1718
}
1719

1720
//
1721
// set an entry if it doesn't already exist
1722
//
1723
static char **CSLAddIfMissing(char **papszList, const char *pszName,
135✔
1724
                              const char *pszValue)
1725
{
1726
    if (CSLFetchNameValue(papszList, pszName))
135✔
1727
        return papszList;
14✔
1728
    return CSLSetNameValue(papszList, pszName, pszValue);
121✔
1729
}
1730

1731
// CreateCopy implemented based on Create
1732
GDALDataset *MRFDataset::CreateCopy(const char *pszFilename,
134✔
1733
                                    GDALDataset *poSrcDS, int /*bStrict*/,
1734
                                    char **papszOptions,
1735
                                    GDALProgressFunc pfnProgress,
1736
                                    void *pProgressData)
1737
{
1738
    ILImage img;
268✔
1739

1740
    int x = poSrcDS->GetRasterXSize();
134✔
1741
    int y = poSrcDS->GetRasterYSize();
134✔
1742
    int nBands = poSrcDS->GetRasterCount();
134✔
1743
    if (nBands == 0)
134✔
1744
    {
1745
        CPLError(CE_Failure, CPLE_NotSupported, "nBands == 0 not supported");
1✔
1746
        return nullptr;
1✔
1747
    }
1748
    GDALRasterBand *poSrcBand1 = poSrcDS->GetRasterBand(1);
133✔
1749

1750
    GDALDataType dt = poSrcBand1->GetRasterDataType();
133✔
1751
    // Have our own options, to modify as we want
1752
    char **options = CSLDuplicate(papszOptions);
133✔
1753

1754
    const char *pszValue =
1755
        poSrcDS->GetMetadataItem("INTERLEAVE", "IMAGE_STRUCTURE");
133✔
1756
    options =
1757
        CSLAddIfMissing(options, "INTERLEAVE", pszValue ? pszValue : "PIXEL");
133✔
1758
    int xb, yb;
1759
    poSrcBand1->GetBlockSize(&xb, &yb);
133✔
1760

1761
    // Keep input block size if it exists and not explicitly set
1762
    if (CSLFetchNameValue(options, "BLOCKSIZE") == nullptr && xb != x &&
134✔
1763
        yb != y)
1✔
1764
    {
1765
        options = CSLAddIfMissing(options, "BLOCKXSIZE",
1✔
1766
                                  PrintDouble(xb, "%d").c_str());
2✔
1767
        options = CSLAddIfMissing(options, "BLOCKYSIZE",
1✔
1768
                                  PrintDouble(yb, "%d").c_str());
2✔
1769
    }
1770

1771
    MRFDataset *poDS = nullptr;
133✔
1772
    try
1773
    {
1774
        poDS = reinterpret_cast<MRFDataset *>(
1775
            Create(pszFilename, x, y, nBands, dt, options));
133✔
1776

1777
        if (poDS == nullptr || poDS->bCrystalized)
133✔
1778
            throw CPLOPrintf("MRF: Can't create %s", pszFilename);
11✔
1779

1780
        img = poDS->current;  // Deal with the current one here
122✔
1781

1782
        // Copy data values from source
1783
        for (int i = 0; i < poDS->nBands; i++)
284✔
1784
        {
1785
            int bHas;
1786
            double dfData;
1787
            GDALRasterBand *srcBand = poSrcDS->GetRasterBand(i + 1);
162✔
1788
            GDALRasterBand *mBand = poDS->GetRasterBand(i + 1);
162✔
1789
            dfData = srcBand->GetNoDataValue(&bHas);
162✔
1790
            if (bHas)
162✔
1791
            {
1792
                poDS->vNoData.push_back(dfData);
16✔
1793
                mBand->SetNoDataValue(dfData);
16✔
1794
            }
1795
            dfData = srcBand->GetMinimum(&bHas);
162✔
1796
            if (bHas)
162✔
1797
                poDS->vMin.push_back(dfData);
17✔
1798
            dfData = srcBand->GetMaximum(&bHas);
162✔
1799
            if (bHas)
162✔
1800
                poDS->vMax.push_back(dfData);
17✔
1801

1802
            // Copy the band metadata, PAM will handle it
1803
            char **meta = srcBand->GetMetadata("IMAGE_STRUCTURE");
162✔
1804
            if (CSLCount(meta))
162✔
1805
                mBand->SetMetadata(meta, "IMAGE_STRUCTURE");
7✔
1806

1807
            meta = srcBand->GetMetadata();
162✔
1808
            if (CSLCount(meta))
162✔
1809
                mBand->SetMetadata(meta);
18✔
1810
        }
1811

1812
        // Geotags
1813
        GDALGeoTransform gt;
122✔
1814
        if (CE_None == poSrcDS->GetGeoTransform(gt))
122✔
1815
            poDS->SetGeoTransform(gt);
119✔
1816

1817
        const auto poSRS = poSrcDS->GetSpatialRef();
122✔
1818
        if (poSRS)
122✔
1819
            poDS->m_oSRS = *poSRS;
118✔
1820

1821
        // Color palette if we only have one band
1822
        if (1 == nBands &&
225✔
1823
            GCI_PaletteIndex == poSrcBand1->GetColorInterpretation())
103✔
1824
            poDS->SetColorTable(poSrcBand1->GetColorTable()->Clone());
1✔
1825

1826
        // Finally write the XML in the right file name
1827
        if (!poDS->Crystalize())
122✔
1828
            throw CPLString("MRF: Error creating files");
10✔
1829
    }
1830
    catch (const CPLString &e)
21✔
1831
    {
1832
        if (nullptr != poDS)
21✔
1833
            delete poDS;
10✔
1834
        CPLError(CE_Failure, CPLE_ObjectNull, "%s", e.c_str());
21✔
1835
        poDS = nullptr;
21✔
1836
    }
1837

1838
    CSLDestroy(options);
133✔
1839
    if (nullptr == poDS)
133✔
1840
        return nullptr;
21✔
1841

1842
    char **papszFileList = poDS->GetFileList();
112✔
1843
    poDS->oOvManager.Initialize(poDS, poDS->GetPhysicalFilename(),
112✔
1844
                                papszFileList);
1845
    CSLDestroy(papszFileList);
112✔
1846

1847
    CPLErr err = CE_None;
112✔
1848
    // Have PAM copy all, but skip the mask
1849
    int nCloneFlags = GCIF_PAM_DEFAULT & ~GCIF_MASK;
112✔
1850

1851
    // If copy is disabled, we're done, we just created an empty MRF
1852
    if (!on(CSLFetchNameValue(papszOptions, "NOCOPY")))
112✔
1853
    {
1854
        // Use the GDAL copy call
1855
        // Need to flag the dataset as compressed (COMPRESSED=TRUE) to force
1856
        // block writes This might not be what we want, if the input and out
1857
        // order is truly separate
1858
        nCloneFlags |= GCIF_MASK;  // We do copy the data, so copy the mask too
110✔
1859
                                   // if necessary
1860
        char **papszCWROptions = nullptr;
110✔
1861
        papszCWROptions =
1862
            CSLAddNameValue(papszCWROptions, "COMPRESSED", "TRUE");
110✔
1863

1864
#ifdef HAVE_JPEG
1865
        // Use the Zen version of the CopyWholeRaster if input has a dataset
1866
        // mask and JPEGs are generated
1867
        if (GMF_PER_DATASET == poSrcDS->GetRasterBand(1)->GetMaskFlags() &&
112✔
1868
            (poDS->current.comp == IL_JPEG
2✔
1869
#ifdef HAVE_PNG
1870
             || poDS->current.comp == IL_JPNG
×
1871
#endif
1872
             ))
1873
        {
1874
            err = poDS->ZenCopy(poSrcDS, pfnProgress, pProgressData);
2✔
1875
            nCloneFlags ^= GCIF_MASK;  // Turn the external mask off
2✔
1876
        }
1877
        else
1878
#endif
1879
        {
1880
            err = GDALDatasetCopyWholeRaster(
108✔
1881
                (GDALDatasetH)poSrcDS, (GDALDatasetH)poDS, papszCWROptions,
1882
                pfnProgress, pProgressData);
1883
        }
1884

1885
        CSLDestroy(papszCWROptions);
110✔
1886
    }
1887

1888
    if (CE_None == err)
112✔
1889
        err = poDS->CloneInfo(poSrcDS, nCloneFlags);
112✔
1890

1891
    if (CE_Failure == err)
112✔
1892
    {
1893
        delete poDS;
×
1894
        return nullptr;
×
1895
    }
1896

1897
    return poDS;
112✔
1898
}
1899

1900
// Prepares the data so it is suitable for Zen JPEG encoding, based on input
1901
// mask If bFBO is set, only the values of the first band are set non-zero when
1902
// needed
1903
template <typename T>
1904
static void ZenFilter(T *buffer, GByte *mask, int nPixels, int nBands,
2✔
1905
                      bool bFBO)
1906
{
1907
    for (int i = 0; i < nPixels; i++)
524,290✔
1908
    {
1909
        if (mask[i] == 0)
524,288✔
1910
        {  // enforce zero values
1911
            for (int b = 0; b < nBands; b++)
516,096✔
1912
                buffer[nBands * i + b] = 0;
387,072✔
1913
        }
1914
        else
1915
        {  // enforce non-zero
1916
            if (bFBO)
395,264✔
1917
            {  // First band only
1918
                bool f = true;
197,632✔
1919
                for (int b = 0; b < nBands; b++)
790,528✔
1920
                {
1921
                    if (0 == buffer[nBands * i + b])
592,896✔
1922
                    {
1923
                        f = false;
×
1924
                        break;
×
1925
                    }
1926
                }
1927
                if (f)
197,632✔
1928
                    buffer[nBands * i] = 1;
197,632✔
1929
            }
1930
            else
1931
            {  // Every band
1932
                for (int b = 0; b < nBands; b++)
790,528✔
1933
                    if (0 == buffer[nBands * i + b])
592,896✔
1934
                        buffer[nBands * i + b] = 1;
×
1935
            }
1936
        }
1937
    }
1938
}
2✔
1939

1940
// Custom CopyWholeRaster for Zen JPEG, called when the input has a PER_DATASET
1941
// mask Works like GDALDatasetCopyWholeRaster, but it does filter the input data
1942
// based on the mask
1943
//
1944
CPLErr MRFDataset::ZenCopy(GDALDataset *poSrc, GDALProgressFunc pfnProgress,
2✔
1945
                           void *pProgressData)
1946
{
1947
    VALIDATE_POINTER1(poSrc, "MRF:ZenCopy", CE_Failure);
2✔
1948

1949
    if (!pfnProgress)
2✔
1950
        pfnProgress = GDALDummyProgress;
×
1951

1952
    /* -------------------------------------------------------------------- */
1953
    /*      Confirm the datasets match in size and band counts.             */
1954
    /* -------------------------------------------------------------------- */
1955
    const int nXSize = GetRasterXSize();
2✔
1956
    const int nYSize = GetRasterYSize();
2✔
1957
    const int nBandCount = GetRasterCount();
2✔
1958

1959
    if (poSrc->GetRasterXSize() != nXSize ||
2✔
1960
        poSrc->GetRasterYSize() != nYSize ||
4✔
1961
        poSrc->GetRasterCount() != nBandCount)
2✔
1962
    {
1963
        CPLError(CE_Failure, CPLE_AppDefined,
×
1964
                 "Input and output dataset sizes or band counts do not\n"
1965
                 "match in GDALDatasetCopyWholeRaster()");
1966
        return CE_Failure;
×
1967
    }
1968

1969
    /* -------------------------------------------------------------------- */
1970
    /*      Get our prototype band, and assume the others are similarly     */
1971
    /*      configured. Also get the per_dataset mask                       */
1972
    /* -------------------------------------------------------------------- */
1973
    GDALRasterBand *poSrcPrototypeBand = poSrc->GetRasterBand(1);
2✔
1974
    GDALRasterBand *poDstPrototypeBand = GetRasterBand(1);
2✔
1975
    GDALRasterBand *poSrcMask = poSrcPrototypeBand->GetMaskBand();
2✔
1976

1977
    const int nPageXSize = current.pagesize.x;
2✔
1978
    const int nPageYSize = current.pagesize.y;
2✔
1979
    const double nTotalBlocks =
2✔
1980
        static_cast<double>(DIV_ROUND_UP(nYSize, nPageYSize)) *
2✔
1981
        static_cast<double>(DIV_ROUND_UP(nXSize, nPageXSize));
2✔
1982
    const GDALDataType eDT = poDstPrototypeBand->GetRasterDataType();
2✔
1983

1984
    // All the bands are done per block
1985
    // this flag tells us to apply the Zen filter to the first band only
1986
    const bool bFirstBandOnly = (current.order == IL_Interleaved);
2✔
1987

1988
    if (!pfnProgress(0.0, nullptr, pProgressData))
2✔
1989
    {
1990
        CPLError(CE_Failure, CPLE_UserInterrupt,
×
1991
                 "User terminated CreateCopy()");
1992
        return CE_Failure;
×
1993
    }
1994

1995
    const int nPixelCount = nPageXSize * nPageYSize;
2✔
1996
    const int dts = GDALGetDataTypeSizeBytes(eDT);
2✔
1997
    void *buffer = VSI_MALLOC3_VERBOSE(nPixelCount, nBandCount, dts);
2✔
1998
    GByte *buffer_mask = nullptr;
2✔
1999
    if (buffer)
2✔
2000
        buffer_mask =
2001
            reinterpret_cast<GByte *>(VSI_MALLOC_VERBOSE(nPixelCount));
2✔
2002

2003
    if (!buffer || !buffer_mask)
2✔
2004
    {
2005
        // Just in case buffers did get allocated
2006
        CPLFree(buffer);
×
2007
        CPLFree(buffer_mask);
×
2008
        CPLError(CE_Failure, CPLE_OutOfMemory, "Can't allocate copy buffer");
×
2009
        return CE_Failure;
×
2010
    }
2011

2012
    int nBlocksDone = 0;
2✔
2013
    CPLErr eErr = CE_None;
2✔
2014
    // Advise the source that a complete read will be done
2015
    poSrc->AdviseRead(0, 0, nXSize, nYSize, nXSize, nYSize, eDT, nBandCount,
2✔
2016
                      nullptr, nullptr);
2✔
2017

2018
    // For every block, break on error
2019
    for (int row = 0; row < nYSize && eErr == CE_None; row += nPageYSize)
4✔
2020
    {
2021
        int nRows = std::min(nPageYSize, nYSize - row);
2✔
2022
        for (int col = 0; col < nXSize && eErr == CE_None; col += nPageXSize)
4✔
2023
        {
2024
            int nCols = std::min(nPageXSize, nXSize - col);
2✔
2025

2026
            // Report
2027
            if (eErr == CE_None && !pfnProgress(nBlocksDone++ / nTotalBlocks,
2✔
2028
                                                nullptr, pProgressData))
2029
            {
2030
                eErr = CE_Failure;
×
2031
                CPLError(CE_Failure, CPLE_UserInterrupt,
×
2032
                         "User terminated CreateCopy()");
2033
                break;
×
2034
            }
2035

2036
            // Get the data mask as byte
2037
            eErr = poSrcMask->RasterIO(GF_Read, col, row, nCols, nRows,
2✔
2038
                                       buffer_mask, nCols, nRows, GDT_Byte, 0,
2039
                                       0, nullptr);
2040

2041
            if (eErr != CE_None)
2✔
2042
                break;
×
2043

2044
            // If there is no data at all, skip this block
2045
            if (MatchCount(buffer_mask, nPixelCount, static_cast<GByte>(0)) ==
2✔
2046
                nPixelCount)
2047
                continue;
×
2048

2049
            // get the data in the buffer, interleaved
2050
            eErr = poSrc->RasterIO(
4✔
2051
                GF_Read, col, row, nCols, nRows, buffer, nCols, nRows, eDT,
2052
                nBandCount, nullptr, static_cast<GSpacing>(nBands) * dts,
2✔
2053
                static_cast<GSpacing>(nBands) * dts * nCols, dts, nullptr);
2✔
2054

2055
            if (eErr != CE_None)
2✔
2056
                break;
×
2057

2058
            // This is JPEG, only 8 and 12(16) bits unsigned integer types are
2059
            // valid
2060
            switch (eDT)
2✔
2061
            {
2062
                case GDT_Byte:
2✔
2063
                    ZenFilter(reinterpret_cast<GByte *>(buffer), buffer_mask,
2✔
2064
                              nPixelCount, nBandCount, bFirstBandOnly);
2065
                    break;
2✔
2066
                case GDT_UInt16:
×
2067
                    ZenFilter(reinterpret_cast<GUInt16 *>(buffer), buffer_mask,
×
2068
                              nPixelCount, nBandCount, bFirstBandOnly);
2069
                    break;
×
2070
                default:
×
2071
                    CPLError(CE_Failure, CPLE_AppDefined,
×
2072
                             "Unsupported data type for Zen filter");
2073
                    eErr = CE_Failure;
×
2074
                    break;
×
2075
            }
2076

2077
            // Write
2078
            if (eErr == CE_None)
2✔
2079
                eErr = RasterIO(
2✔
2080
                    GF_Write, col, row, nCols, nRows, buffer, nCols, nRows, eDT,
2081
                    nBandCount, nullptr, static_cast<GSpacing>(nBands) * dts,
2✔
2082
                    static_cast<GSpacing>(nBands) * dts * nCols, dts, nullptr);
2✔
2083

2084
        }  // Columns
2085
        if (eErr != CE_None)
2✔
2086
            break;
×
2087

2088
    }  // Rows
2089

2090
    // Cleanup
2091
    CPLFree(buffer);
2✔
2092
    CPLFree(buffer_mask);
2✔
2093

2094
    // Final report
2095
    if (eErr == CE_None && !pfnProgress(1.0, nullptr, pProgressData))
2✔
2096
    {
2097
        eErr = CE_Failure;
×
2098
        CPLError(CE_Failure, CPLE_UserInterrupt,
×
2099
                 "User terminated CreateCopy()");
2100
    }
2101

2102
    return eErr;
2✔
2103
}
2104

2105
// Apply open options to the current dataset
2106
// Called before the configuration is read
2107
void MRFDataset::ProcessOpenOptions(char **papszOptions)
204✔
2108
{
2109
    CPLStringList opt(papszOptions, FALSE);
408✔
2110
    no_errors = opt.FetchBoolean("NOERRORS", FALSE);
204✔
2111
    const char *val = opt.FetchNameValue("ZSLICE");
204✔
2112
    if (val)
204✔
2113
        zslice = atoi(val);
×
2114
}
204✔
2115

2116
// Apply create options to the current dataset, only valid during creation
2117
void MRFDataset::ProcessCreateOptions(char **papszOptions)
162✔
2118
{
2119
    assert(!bCrystalized);
162✔
2120
    CPLStringList opt(papszOptions, FALSE);
324✔
2121
    ILImage &img(full);
162✔
2122

2123
    const char *val = opt.FetchNameValue("COMPRESS");
162✔
2124
    if (val && IL_ERR_COMP == (img.comp = CompToken(val)))
162✔
2125
        throw CPLString("GDAL MRF: Error setting compression");
×
2126

2127
    val = opt.FetchNameValue("INTERLEAVE");
162✔
2128
    if (val && IL_ERR_ORD == (img.order = OrderToken(val)))
162✔
2129
        throw CPLString("GDAL MRF: Error setting interleave");
×
2130

2131
    val = opt.FetchNameValue("QUALITY");
162✔
2132
    if (val)
162✔
2133
        img.quality = atoi(val);
6✔
2134

2135
    val = opt.FetchNameValue("ZSIZE");
162✔
2136
    if (val)
162✔
2137
        img.size.z = atoi(val);
×
2138

2139
    val = opt.FetchNameValue("BLOCKXSIZE");
162✔
2140
    if (val)
162✔
2141
        img.pagesize.x = atoi(val);
1✔
2142

2143
    val = opt.FetchNameValue("BLOCKYSIZE");
162✔
2144
    if (val)
162✔
2145
        img.pagesize.y = atoi(val);
1✔
2146

2147
    val = opt.FetchNameValue("BLOCKSIZE");
162✔
2148
    if (val)
162✔
2149
        img.pagesize.x = img.pagesize.y = atoi(val);
30✔
2150

2151
    img.nbo = opt.FetchBoolean("NETBYTEORDER", FALSE) != FALSE;
162✔
2152

2153
    val = opt.FetchNameValue("CACHEDSOURCE");
162✔
2154
    if (val)
162✔
2155
    {
2156
        source = val;
2✔
2157
        nocopy = opt.FetchBoolean("NOCOPY", FALSE);
2✔
2158
    }
2159

2160
    val = opt.FetchNameValue("UNIFORM_SCALE");
162✔
2161
    if (val)
162✔
2162
        scale = atoi(val);
×
2163

2164
    val = opt.FetchNameValue("PHOTOMETRIC");
162✔
2165
    if (val)
162✔
2166
        photometric = val;
2✔
2167

2168
    val = opt.FetchNameValue("DATANAME");
162✔
2169
    if (val)
162✔
2170
        img.datfname = val;
×
2171

2172
    val = opt.FetchNameValue("INDEXNAME");
162✔
2173
    if (val)
162✔
2174
        img.idxfname = val;
×
2175

2176
    val = opt.FetchNameValue("SPACING");
162✔
2177
    if (val)
162✔
2178
        spacing = atoi(val);
×
2179

2180
    optlist.Assign(
2181
        CSLTokenizeString2(opt.FetchNameValue("OPTIONS"), " \t\n\r",
2182
                           CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
162✔
2183

2184
    // General Fixups
2185
    if (img.order == IL_Interleaved)
162✔
2186
        img.pagesize.c = img.size.c;
41✔
2187

2188
    // Compression dependent fixups
2189
}
162✔
2190

2191
/**
2192
 *\brief Create an MRF dataset, some settings can be changed later
2193
 * papszOptions might be anything that an MRF might take
2194
 * Still missing are the georeference ...
2195
 *
2196
 */
2197

2198
GDALDataset *MRFDataset::Create(const char *pszName, int nXSize, int nYSize,
166✔
2199
                                int nBandsIn, GDALDataType eType,
2200
                                char **papszOptions)
2201
{
2202
    if (nBandsIn == 0)
166✔
2203
    {
2204
        CPLError(CE_Failure, CPLE_NotSupported, "No bands defined");
1✔
2205
        return nullptr;
1✔
2206
    }
2207

2208
    MRFDataset *poDS = new MRFDataset();
165✔
2209
    CPLErr err = CE_None;
165✔
2210
    poDS->fname = pszName;
165✔
2211
    poDS->nBands = nBandsIn;
165✔
2212

2213
    // Don't know what to do with these in this call
2214
    // int level = -1;
2215
    // int version = 0;
2216

2217
    size_t pos = poDS->fname.find(":MRF:");
165✔
2218
    if (string::npos != pos)
165✔
2219
    {  // Tokenize and pick known options
2220
        vector<string> tokens;
×
2221
        stringSplit(tokens, poDS->fname, pos + 5, ':');
×
2222
        // level = getnum(tokens, 'L', -1);
2223
        // version = getnum(tokens, 'V', 0);
2224
        poDS->zslice = getnum(tokens, 'Z', 0);
×
2225
        poDS->fname.resize(pos);  // Cut the ornamentations
×
2226
    }
2227

2228
    // Try creating the mrf file early, to avoid failing on Crystalize later
2229
    if (!STARTS_WITH(poDS->fname.c_str(), "<MRF_META>"))
165✔
2230
    {
2231
        // Try opening it first, even though we still clobber it later
2232
        VSILFILE *mainfile = VSIFOpenL(poDS->fname.c_str(), "r+b");
165✔
2233
        if (!mainfile)
165✔
2234
        {  // Then try creating it
2235
            mainfile = VSIFOpenL(poDS->fname.c_str(), "w+b");
115✔
2236
            if (!mainfile)
115✔
2237
            {
2238
                CPLError(CE_Failure, CPLE_OpenFailed,
3✔
2239
                         "MRF: Can't open %s for writing", poDS->fname.c_str());
2240
                delete poDS;
3✔
2241
                return nullptr;
3✔
2242
            }
2243
        }
2244
        VSIFCloseL(mainfile);
162✔
2245
    }
2246

2247
    // Use the full, set some initial parameters
2248
    ILImage &img = poDS->full;
162✔
2249
    img.size = ILSize(nXSize, nYSize, 1, nBandsIn);
162✔
2250
#ifdef HAVE_PNG
2251
    img.comp = IL_PNG;
162✔
2252
#else
2253
    img.comp = IL_NONE;
2254
#endif
2255
    img.order = (nBandsIn < 5) ? IL_Interleaved : IL_Separate;
162✔
2256
    img.pagesize = ILSize(512, 512, 1, 1);
162✔
2257
    img.quality = 85;
162✔
2258
    img.dt = eType;
162✔
2259
    img.dataoffset = 0;
162✔
2260
    img.idxoffset = 0;
162✔
2261
    img.hasNoData = false;
162✔
2262
    img.nbo = false;
162✔
2263

2264
    // Set the guard that tells us it needs saving before IO can take place
2265
    poDS->bCrystalized = FALSE;
162✔
2266

2267
    // Process the options, anything that an MRF might take
2268

2269
    try
2270
    {
2271
        // Adjust the dataset and the full image
2272
        poDS->ProcessCreateOptions(papszOptions);
162✔
2273

2274
        // Set default file names
2275
        if (img.datfname.empty())
162✔
2276
            img.datfname = getFname(poDS->GetFname(), ILComp_Ext[img.comp]);
162✔
2277
        if (img.idxfname.empty())
162✔
2278
            img.idxfname = getFname(poDS->GetFname(), ".idx");
162✔
2279

2280
        poDS->eAccess = GA_Update;
162✔
2281
    }
2282

2283
    catch (const CPLString &e)
×
2284
    {
2285
        CPLError(CE_Failure, CPLE_OpenFailed, "%s", e.c_str());
×
2286
        delete poDS;
×
2287
        return nullptr;
×
2288
    }
2289

2290
    poDS->current = poDS->full;
162✔
2291
    poDS->SetDescription(poDS->GetFname());
162✔
2292

2293
    // Build a MRF XML and initialize from it, this creates the bands
2294
    CPLXMLNode *config = poDS->BuildConfig();
162✔
2295
    err = poDS->Initialize(config);
162✔
2296
    CPLDestroyXMLNode(config);
162✔
2297

2298
    if (CPLE_None != err)
162✔
2299
    {
2300
        delete poDS;
30✔
2301
        return nullptr;
30✔
2302
    }
2303

2304
    // If not set by the band, get a pageSizeBytes buffer
2305
    if (poDS->GetPBufferSize() == 0 &&
132✔
2306
        !poDS->SetPBuffer(poDS->current.pageSizeBytes))
×
2307
    {
2308
        delete poDS;
×
2309
        return nullptr;
×
2310
    }
2311

2312
    // Tell PAM what our real file name is, to help it find the aux.xml
2313
    poDS->SetPhysicalFilename(poDS->GetFname());
132✔
2314
    return poDS;
132✔
2315
}
2316

2317
int MRFDataset::Crystalize()
172✔
2318
{
2319
    if (bCrystalized || eAccess != GA_Update)
172✔
2320
    {
2321
        bCrystalized = TRUE;
×
2322
        return TRUE;
×
2323
    }
2324

2325
    // No need to write to disk if there is no filename.  This is a
2326
    // memory only dataset.
2327
    if (strlen(GetDescription()) == 0 ||
344✔
2328
        EQUALN(GetDescription(), "<MRF_META>", 10))
172✔
2329
    {
2330
        bCrystalized = TRUE;
×
2331
        return TRUE;
×
2332
    }
2333

2334
    CPLXMLNode *config = BuildConfig();
172✔
2335
    if (!WriteConfig(config))
172✔
2336
        return FALSE;
20✔
2337
    CPLDestroyXMLNode(config);
152✔
2338
    if (!nocopy && (!IdxFP() || !DataFP()))
152✔
2339
        return FALSE;
×
2340
    bCrystalized = TRUE;
152✔
2341
    return TRUE;
152✔
2342
}
2343

2344
// Copy the first index at the end of the file and bump the version count
2345
CPLErr MRFDataset::AddVersion()
1✔
2346
{
2347
    VSILFILE *l_ifp = IdxFP();
1✔
2348
    void *tbuff = CPLMalloc(static_cast<size_t>(idxSize));
1✔
2349
    VSIFSeekL(l_ifp, 0, SEEK_SET);
1✔
2350
    VSIFReadL(tbuff, 1, static_cast<size_t>(idxSize), l_ifp);
1✔
2351
    verCount++;  // The one we write
1✔
2352
    VSIFSeekL(l_ifp, idxSize * verCount,
1✔
2353
              SEEK_SET);  // At the end, this can mess things up royally
2354
    VSIFWriteL(tbuff, 1, static_cast<size_t>(idxSize), l_ifp);
1✔
2355
    CPLFree(tbuff);
1✔
2356
    return CE_None;
1✔
2357
}
2358

2359
//
2360
// Write a tile at the end of the data file
2361
// If buff and size are zero, it is equivalent to erasing the tile
2362
// If only size is zero, it is a special empty tile,
2363
// when used for caching, offset should be 1
2364
//
2365
// To make it multi-processor safe, open the file in append mode
2366
// and verify after write
2367
//
2368
CPLErr MRFDataset::WriteTile(void *buff, GUIntBig infooffset, GUIntBig size)
5,096✔
2369
{
2370
    CPLErr ret = CE_None;
5,096✔
2371
    ILIdx tinfo = {0, 0};
5,096✔
2372

2373
    VSILFILE *l_dfp = DataFP();
5,096✔
2374
    VSILFILE *l_ifp = IdxFP();
5,096✔
2375

2376
    // Verify buffer
2377
    std::vector<GByte> tbuff;
10,192✔
2378

2379
    if (l_ifp == nullptr || l_dfp == nullptr)
5,096✔
2380
        return CE_Failure;
×
2381

2382
    // Flag that versioned access requires a write even if empty
2383
    int new_tile = false;
5,096✔
2384
    // If it has versions, might need to start a new one
2385
    if (hasVersions)
5,096✔
2386
    {
2387
        int new_version = false;  // Assume no need to build new version
1✔
2388

2389
        // Read the current tile info
2390
        VSIFSeekL(l_ifp, infooffset, SEEK_SET);
1✔
2391
        VSIFReadL(&tinfo, 1, sizeof(ILIdx), l_ifp);
1✔
2392

2393
        if (verCount == 0)
1✔
2394
            new_version = true;  // No previous yet, might create a new version
1✔
2395
        else
2396
        {  // We need at least two versions before we can test for changes
2397
            ILIdx prevtinfo = {0, 0};
×
2398

2399
            // Read the previous one
2400
            VSIFSeekL(l_ifp, infooffset + verCount * idxSize, SEEK_SET);
×
2401
            VSIFReadL(&prevtinfo, 1, sizeof(ILIdx), l_ifp);
×
2402

2403
            // current and previous tiles are different, might create version
2404
            if (tinfo.size != prevtinfo.size ||
×
2405
                tinfo.offset != prevtinfo.offset)
×
2406
                new_version = true;
×
2407
        }
2408

2409
        // tinfo contains the current info or 0,0
2410
        if (tinfo.size == GIntBig(net64(size)))
1✔
2411
        {  // Might be identical
2412
            if (size != 0)
×
2413
            {
2414
                // Use the temporary buffer
2415
                tbuff.resize(static_cast<size_t>(size));
×
2416
                VSIFSeekL(l_dfp, infooffset, SEEK_SET);
×
2417
                VSIFReadL(tbuff.data(), 1, tbuff.size(), l_dfp);
×
2418
                // Need to write it if not the same
2419
                new_tile = !std::equal(tbuff.begin(), tbuff.end(),
×
2420
                                       static_cast<GByte *>(buff));
2421
                tbuff.clear();
×
2422
            }
2423
            else
2424
            {
2425
                // Writing a null tile on top of a null tile, does it count?
2426
                if (tinfo.offset != GIntBig(net64(GUIntBig(buff))))
×
2427
                    new_tile = true;
×
2428
            }
2429
        }
2430
        else
2431
        {
2432
            new_tile = true;  // Need to write it because it is different
1✔
2433
            if (verCount == 0 && tinfo.size == 0)
1✔
2434
                new_version = false;  // Don't create a version if current is
×
2435
                                      // empty and there is no previous
2436
        }
2437

2438
        if (!new_tile)
1✔
2439
            return CE_None;  // No reason to write
×
2440

2441
        // Do we need to start a new version before writing the tile?
2442
        if (new_version)
1✔
2443
            AddVersion();
1✔
2444
    }
2445

2446
    bool same = true;
5,096✔
2447
    if (size)
5,096✔
2448
        do
×
2449
        {
2450
            // start of critical MP section
2451
            VSIFSeekL(l_dfp, 0, SEEK_END);
5,085✔
2452
            GUIntBig offset = VSIFTellL(l_dfp) + spacing;
5,085✔
2453

2454
            // Spacing should be 0 in MP safe mode, this doesn't have much of
2455
            // effect Use the existing data, spacing content is not guaranteed
2456
            for (GUIntBig pending = spacing; pending != 0;
5,085✔
2457
                 pending -= std::min(pending, size))
×
2458
                VSIFWriteL(buff, 1,
×
2459
                           static_cast<size_t>(std::min(pending, size)),
×
2460
                           l_dfp);  // Usually only once
2461

2462
            if (static_cast<size_t>(size) !=
5,085✔
2463
                VSIFWriteL(buff, 1, static_cast<size_t>(size), l_dfp))
5,085✔
2464
                ret = CE_Failure;
×
2465
            // End of critical section
2466

2467
            tinfo.offset = net64(offset);
5,085✔
2468
            //
2469
            // For MP ops, check that we can read the same content, otherwise
2470
            // try again This makes the caching MRF MP safe on file systems that
2471
            // implement append mode fully, without using explicit locks
2472
            //
2473
            if (CE_None == ret && mp_safe)
5,085✔
2474
            {  // readback and check
2475
                if (tbuff.size() < size)
3✔
2476
                    tbuff.resize(static_cast<size_t>(size));
3✔
2477
                VSIFSeekL(l_dfp, offset, SEEK_SET);
3✔
2478
                VSIFReadL(tbuff.data(), 1, tbuff.size(), l_dfp);
3✔
2479
                same = std::equal(tbuff.begin(), tbuff.end(),
3✔
2480
                                  static_cast<GByte *>(buff));
2481
            }
2482
        } while (CE_None == ret && mp_safe && !same);
5,085✔
2483

2484
    if (CE_None != ret)
5,096✔
2485
    {
2486
        CPLError(CE_Failure, CPLE_AppDefined, "MRF: Tile write failed");
×
2487
        return ret;
×
2488
    }
2489

2490
    // Convert index to net format, offset is set already
2491
    tinfo.size = net64(size);
5,096✔
2492
    // Do nothing if the tile is empty and the file record is also empty
2493
    if (!new_tile && 0 == size && nullptr == buff)
5,096✔
2494
    {
2495
        VSIFSeekL(l_ifp, infooffset, SEEK_SET);
10✔
2496
        VSIFReadL(&tinfo, 1, sizeof(ILIdx), l_ifp);
10✔
2497
        if (0 == tinfo.offset && 0 == tinfo.size)
10✔
2498
            return ret;
10✔
2499
    }
2500

2501
    // Special case, any non-zero offset will do
2502
    if (nullptr != buff && 0 == size)
5,086✔
2503
        tinfo.offset = ~GUIntBig(0);
×
2504

2505
    VSIFSeekL(l_ifp, infooffset, SEEK_SET);
5,086✔
2506
    if (sizeof(tinfo) != VSIFWriteL(&tinfo, 1, sizeof(tinfo), l_ifp))
5,086✔
2507
    {
2508
        CPLError(CE_Failure, CPLE_AppDefined, "MRF: Index write failed");
×
2509
        ret = CE_Failure;
×
2510
    }
2511

2512
    return ret;
5,086✔
2513
}
2514

2515
CPLErr MRFDataset::SetGeoTransform(const GDALGeoTransform &gt)
128✔
2516
{
2517
    if (GetAccess() != GA_Update || bCrystalized)
128✔
2518
    {
2519
        CPLError(CE_Failure, CPLE_NotSupported,
×
2520
                 "SetGeoTransform only works during Create call");
2521
        return CE_Failure;
×
2522
    }
2523
    m_gt = gt;
128✔
2524
    bGeoTransformValid = TRUE;
128✔
2525
    return CE_None;
128✔
2526
}
2527

2528
bool MRFDataset::IsSingleTile()
16✔
2529
{
2530
    if (current.pagecount.l != 1 || !source.empty() || nullptr == DataFP())
16✔
2531
        return FALSE;
4✔
2532
    return 0 == reinterpret_cast<MRFRasterBand *>(GetRasterBand(1))
12✔
2533
                    ->GetOverviewCount();
12✔
2534
}
2535

2536
/*
2537
 *  Returns 0,1,0,0,0,1 even if it was not set
2538
 */
2539
CPLErr MRFDataset::GetGeoTransform(GDALGeoTransform &gt) const
455✔
2540
{
2541
    gt = m_gt;
455✔
2542
    MRFDataset *nonConstThis = const_cast<MRFDataset *>(this);
455✔
2543
    if (nonConstThis->GetMetadata("RPC") || nonConstThis->GetGCPCount())
455✔
2544
        bGeoTransformValid = FALSE;
×
2545
    if (!bGeoTransformValid)
455✔
2546
        return CE_Failure;
×
2547
    return CE_None;
455✔
2548
}
2549

2550
/**
2551
 *\brief Read a tile index
2552
 *
2553
 * It handles the non-existent index case, for no compression
2554
 * The bias is non-zero only when the cloned index is read
2555
 */
2556

2557
CPLErr MRFDataset::ReadTileIdx(ILIdx &tinfo, const ILSize &pos,
2,020✔
2558
                               const ILImage &img, const GIntBig bias)
2559
{
2560
    VSILFILE *l_ifp = IdxFP();
2,020✔
2561

2562
    // Initialize the tinfo structure, in case the files are missing
2563
    if (missing)
2,020✔
2564
        return CE_None;
×
2565

2566
    GIntBig offset = bias + IdxOffset(pos, img);
2,020✔
2567
    if (l_ifp == nullptr && img.comp == IL_NONE)
2,020✔
2568
    {
2569
        tinfo.size = current.pageSizeBytes;
×
2570
        tinfo.offset = offset * tinfo.size;
×
2571
        return CE_None;
×
2572
    }
2573

2574
    if (l_ifp == nullptr && IsSingleTile())
2,020✔
2575
    {
2576
        tinfo.offset = 0;
6✔
2577
        VSILFILE *l_dfp = DataFP();  // IsSingleTile() checks that fp is valid
6✔
2578
        VSIFSeekL(l_dfp, 0, SEEK_END);
6✔
2579
        tinfo.size = VSIFTellL(l_dfp);
6✔
2580

2581
        // It should be less than the pagebuffer
2582
        tinfo.size = std::min(tinfo.size, static_cast<GIntBig>(pbsize));
6✔
2583
        return CE_None;
6✔
2584
    }
2585

2586
    if (l_ifp == nullptr)
2,014✔
2587
    {
2588
        CPLError(CE_Failure, CPLE_FileIO, "Can't open index file");
×
2589
        return CE_Failure;
×
2590
    }
2591

2592
    VSIFSeekL(l_ifp, offset, SEEK_SET);
2,014✔
2593
    if (1 != VSIFReadL(&tinfo, sizeof(ILIdx), 1, l_ifp))
2,014✔
2594
        return CE_Failure;
×
2595
    // Convert them to native form
2596
    tinfo.offset = net64(tinfo.offset);
2,014✔
2597
    tinfo.size = net64(tinfo.size);
2,014✔
2598

2599
    if (0 == bias || 0 != tinfo.size || 0 != tinfo.offset)
2,014✔
2600
        return CE_None;
2,013✔
2601

2602
    // zero size and zero offset in sourced index means that this portion is
2603
    // un-initialized
2604

2605
    // Should be cloned and the offset within the cloned index
2606
    offset -= bias;
1✔
2607
    assert(offset < bias);
1✔
2608
    assert(clonedSource);
1✔
2609

2610
    // Read this block from the remote index, prepare it and store it in the
2611
    // right place The block size in bytes, should be a multiple of 16, to have
2612
    // full index entries
2613
    const int CPYSZ = 32768;
1✔
2614
    // Adjust offset to the start of the block
2615
    offset = (offset / CPYSZ) * CPYSZ;
1✔
2616
    GIntBig size = std::min(size_t(CPYSZ), size_t(bias - offset));
1✔
2617
    size /= sizeof(ILIdx);  // In records
1✔
2618
    vector<ILIdx> buf(static_cast<size_t>(size));
2✔
2619
    ILIdx *buffer = &buf[0];  // Buffer to copy the source to the clone index
1✔
2620

2621
    // Fetch the data from the cloned index
2622
    MRFDataset *pSrc = static_cast<MRFDataset *>(GetSrcDS());
1✔
2623
    if (nullptr == pSrc)
1✔
2624
    {
2625
        CPLError(CE_Failure, CPLE_FileIO, "Can't open cloned source index");
×
2626
        return CE_Failure;  // Source reported the error
×
2627
    }
2628

2629
    VSILFILE *srcidx = pSrc->IdxFP();
1✔
2630
    if (nullptr == srcidx)
1✔
2631
    {
2632
        CPLError(CE_Failure, CPLE_FileIO, "Can't open cloned source index");
×
2633
        return CE_Failure;  // Source reported the error
×
2634
    }
2635

2636
    VSIFSeekL(srcidx, offset, SEEK_SET);
1✔
2637
    size = VSIFReadL(buffer, sizeof(ILIdx), static_cast<size_t>(size), srcidx);
1✔
2638
    if (size != GIntBig(buf.size()))
1✔
2639
    {
2640
        CPLError(CE_Failure, CPLE_FileIO, "Can't read cloned source index");
×
2641
        return CE_Failure;  // Source reported the error
×
2642
    }
2643

2644
    // Mark the empty records as checked, by making the offset non-zero
2645
    for (vector<ILIdx>::iterator it = buf.begin(); it != buf.end(); ++it)
2✔
2646
    {
2647
        if (it->offset == 0 && it->size == 0)
1✔
2648
            it->offset = net64(1);
×
2649
    }
2650

2651
    // Write it in the right place in the local index file
2652
    VSIFSeekL(l_ifp, bias + offset, SEEK_SET);
1✔
2653
    size = VSIFWriteL(&buf[0], sizeof(ILIdx), static_cast<size_t>(size), l_ifp);
1✔
2654
    if (size != GIntBig(buf.size()))
1✔
2655
    {
2656
        CPLError(CE_Failure, CPLE_FileIO, "Can't write to cloning MRF index");
×
2657
        return CE_Failure;  // Source reported the error
×
2658
    }
2659

2660
    // Cloned index updated, restart this function, it will work now
2661
    return ReadTileIdx(tinfo, pos, img, bias);
1✔
2662
}
2663

2664
NAMESPACE_MRF_END
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc