Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Virtual GDAL Datasets
4 : * Purpose: Implementation of VRTRawRasterBand
5 : * Author: Frank Warmerdam <warmerdam@pobox.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "rawdataset.h"
16 : #include "vrtdataset.h"
17 :
18 : #include <cerrno>
19 : #include <cstdio>
20 : #include <cstdlib>
21 : #include <cstring>
22 :
23 : #include "cpl_conv.h"
24 : #include "cpl_error.h"
25 : #include "cpl_hash_set.h"
26 : #include "cpl_minixml.h"
27 : #include "cpl_string.h"
28 : #include "cpl_vsi.h"
29 : #include "gdal.h"
30 : #include "gdal_priv.h"
31 :
32 : /*! @cond Doxygen_Suppress */
33 :
34 : /************************************************************************/
35 : /* ==================================================================== */
36 : /* VRTRawRasterBand */
37 : /* ==================================================================== */
38 : /************************************************************************/
39 :
40 : /************************************************************************/
41 : /* VRTRawRasterBand() */
42 : /************************************************************************/
43 :
44 28 : VRTRawRasterBand::VRTRawRasterBand(GDALDataset *poDSIn, int nBandIn,
45 28 : GDALDataType eType)
46 : : m_poRawRaster(nullptr), m_pszSourceFilename(nullptr),
47 28 : m_bRelativeToVRT(FALSE)
48 : {
49 28 : Initialize(poDSIn->GetRasterXSize(), poDSIn->GetRasterYSize());
50 :
51 : // Declared in GDALRasterBand.
52 28 : poDS = poDSIn;
53 28 : nBand = nBandIn;
54 :
55 28 : if (eType != GDT_Unknown)
56 6 : eDataType = eType;
57 28 : }
58 :
59 : /************************************************************************/
60 : /* ~VRTRawRasterBand() */
61 : /************************************************************************/
62 :
63 56 : VRTRawRasterBand::~VRTRawRasterBand()
64 :
65 : {
66 28 : FlushCache(true);
67 28 : ClearRawLink();
68 56 : }
69 :
70 : /************************************************************************/
71 : /* IRasterIO() */
72 : /************************************************************************/
73 :
74 256 : CPLErr VRTRawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
75 : int nXSize, int nYSize, void *pData,
76 : int nBufXSize, int nBufYSize,
77 : GDALDataType eBufType, GSpacing nPixelSpace,
78 : GSpacing nLineSpace,
79 : GDALRasterIOExtraArg *psExtraArg)
80 : {
81 256 : if (m_poRawRaster == nullptr)
82 : {
83 0 : CPLError(CE_Failure, CPLE_AppDefined,
84 : "No raw raster band configured on VRTRawRasterBand.");
85 0 : return CE_Failure;
86 : }
87 :
88 256 : if (eRWFlag == GF_Write && eAccess == GA_ReadOnly)
89 : {
90 0 : CPLError(CE_Failure, CPLE_NoWriteAccess,
91 : "Attempt to write to read only dataset in"
92 : "VRTRawRasterBand::IRasterIO().");
93 :
94 0 : return CE_Failure;
95 : }
96 :
97 : /* -------------------------------------------------------------------- */
98 : /* Do we have overviews that would be appropriate to satisfy */
99 : /* this request? */
100 : /* -------------------------------------------------------------------- */
101 256 : if ((nBufXSize < nXSize || nBufYSize < nYSize) && GetOverviewCount() > 0)
102 : {
103 0 : if (OverviewRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
104 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
105 0 : nLineSpace, psExtraArg) == CE_None)
106 0 : return CE_None;
107 : }
108 :
109 256 : m_poRawRaster->SetAccess(eAccess);
110 :
111 256 : return m_poRawRaster->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
112 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
113 256 : nLineSpace, psExtraArg);
114 : }
115 :
116 : /************************************************************************/
117 : /* IReadBlock() */
118 : /************************************************************************/
119 :
120 70 : CPLErr VRTRawRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
121 : void *pImage)
122 :
123 : {
124 70 : if (m_poRawRaster == nullptr)
125 : {
126 0 : CPLError(CE_Failure, CPLE_AppDefined,
127 : "No raw raster band configured on VRTRawRasterBand.");
128 0 : return CE_Failure;
129 : }
130 :
131 70 : return m_poRawRaster->ReadBlock(nBlockXOff, nBlockYOff, pImage);
132 : }
133 :
134 : /************************************************************************/
135 : /* IWriteBlock() */
136 : /************************************************************************/
137 :
138 0 : CPLErr VRTRawRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
139 : void *pImage)
140 :
141 : {
142 0 : if (m_poRawRaster == nullptr)
143 : {
144 0 : CPLError(CE_Failure, CPLE_AppDefined,
145 : "No raw raster band configured on VRTRawRasterBand.");
146 0 : return CE_Failure;
147 : }
148 :
149 0 : m_poRawRaster->SetAccess(eAccess);
150 :
151 0 : return m_poRawRaster->WriteBlock(nBlockXOff, nBlockYOff, pImage);
152 : }
153 :
154 : /************************************************************************/
155 : /* SetRawLink() */
156 : /************************************************************************/
157 :
158 28 : CPLErr VRTRawRasterBand::SetRawLink(const char *pszFilename,
159 : const char *pszVRTPath,
160 : int bRelativeToVRTIn,
161 : vsi_l_offset nImageOffset, int nPixelOffset,
162 : int nLineOffset, const char *pszByteOrder)
163 :
164 : {
165 28 : ClearRawLink();
166 :
167 28 : static_cast<VRTDataset *>(poDS)->SetNeedsFlush();
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* Prepare filename. */
171 : /* -------------------------------------------------------------------- */
172 28 : if (pszFilename == nullptr)
173 : {
174 0 : CPLError(CE_Warning, CPLE_AppDefined,
175 : "Missing <SourceFilename> element in VRTRasterBand.");
176 0 : return CE_Failure;
177 : }
178 :
179 28 : char *pszExpandedFilename = nullptr;
180 28 : if (pszVRTPath != nullptr && bRelativeToVRTIn)
181 : {
182 16 : pszExpandedFilename =
183 16 : CPLStrdup(CPLProjectRelativeFilename(pszVRTPath, pszFilename));
184 : }
185 : else
186 : {
187 12 : pszExpandedFilename = CPLStrdup(pszFilename);
188 : }
189 :
190 : /* -------------------------------------------------------------------- */
191 : /* Try and open the file. We always use the large file API. */
192 : /* -------------------------------------------------------------------- */
193 28 : CPLPushErrorHandler(CPLQuietErrorHandler);
194 28 : FILE *fp = CPLOpenShared(pszExpandedFilename, "rb+", TRUE);
195 :
196 28 : if (fp == nullptr)
197 4 : fp = CPLOpenShared(pszExpandedFilename, "rb", TRUE);
198 :
199 32 : if (fp == nullptr &&
200 4 : static_cast<VRTDataset *>(poDS)->GetAccess() == GA_Update)
201 : {
202 3 : fp = CPLOpenShared(pszExpandedFilename, "wb+", TRUE);
203 : }
204 28 : CPLPopErrorHandler();
205 28 : CPLErrorReset();
206 :
207 28 : if (fp == nullptr)
208 : {
209 1 : CPLError(CE_Failure, CPLE_OpenFailed, "Unable to open %s.%s",
210 1 : pszExpandedFilename, VSIStrerror(errno));
211 :
212 1 : CPLFree(pszExpandedFilename);
213 1 : return CE_Failure;
214 : }
215 :
216 27 : CPLFree(pszExpandedFilename);
217 :
218 27 : if (!RAWDatasetCheckMemoryUsage(
219 : nRasterXSize, nRasterYSize, 1,
220 : GDALGetDataTypeSizeBytes(GetRasterDataType()), nPixelOffset,
221 : nLineOffset, nImageOffset, 0, reinterpret_cast<VSILFILE *>(fp)))
222 : {
223 2 : CPLCloseShared(fp);
224 2 : return CE_Failure;
225 : }
226 :
227 25 : m_pszSourceFilename = CPLStrdup(pszFilename);
228 25 : m_bRelativeToVRT = bRelativeToVRTIn;
229 :
230 : /* -------------------------------------------------------------------- */
231 : /* Work out if we are in native mode or not. */
232 : /* -------------------------------------------------------------------- */
233 25 : RawRasterBand::ByteOrder eByteOrder =
234 : #if CPL_IS_LSB
235 : RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
236 : #else
237 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
238 : #endif
239 :
240 25 : if (pszByteOrder != nullptr)
241 : {
242 23 : if (EQUAL(pszByteOrder, "LSB"))
243 14 : eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
244 9 : else if (EQUAL(pszByteOrder, "MSB"))
245 7 : eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
246 2 : else if (EQUAL(pszByteOrder, "VAX"))
247 1 : eByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
248 : else
249 : {
250 1 : CPLError(CE_Failure, CPLE_AppDefined,
251 : "Illegal ByteOrder value '%s', should be LSB, MSB or VAX.",
252 : pszByteOrder);
253 1 : CPLCloseShared(fp);
254 1 : return CE_Failure;
255 : }
256 : }
257 :
258 : /* -------------------------------------------------------------------- */
259 : /* Create a corresponding RawRasterBand. */
260 : /* -------------------------------------------------------------------- */
261 24 : m_poRawRaster =
262 48 : RawRasterBand::Create(reinterpret_cast<VSILFILE *>(fp), nImageOffset,
263 : nPixelOffset, nLineOffset, GetRasterDataType(),
264 : eByteOrder, GetXSize(), GetYSize(),
265 : RawRasterBand::OwnFP::NO)
266 24 : .release();
267 24 : if (!m_poRawRaster)
268 : {
269 0 : CPLCloseShared(fp);
270 0 : return CE_Failure;
271 : }
272 :
273 : /* -------------------------------------------------------------------- */
274 : /* Reset block size to match the raw raster. */
275 : /* -------------------------------------------------------------------- */
276 24 : m_poRawRaster->GetBlockSize(&nBlockXSize, &nBlockYSize);
277 :
278 24 : return CE_None;
279 : }
280 :
281 : /************************************************************************/
282 : /* ClearRawLink() */
283 : /************************************************************************/
284 :
285 56 : void VRTRawRasterBand::ClearRawLink()
286 :
287 : {
288 56 : if (m_poRawRaster != nullptr)
289 : {
290 24 : VSILFILE *fp = m_poRawRaster->GetFPL();
291 24 : delete m_poRawRaster;
292 24 : m_poRawRaster = nullptr;
293 : // We close the file after deleting the raster band
294 : // since data can be flushed in the destructor.
295 24 : if (fp != nullptr)
296 : {
297 24 : CPLCloseShared(reinterpret_cast<FILE *>(fp));
298 : }
299 : }
300 56 : CPLFree(m_pszSourceFilename);
301 56 : m_pszSourceFilename = nullptr;
302 56 : }
303 :
304 : /************************************************************************/
305 : /* GetVirtualMemAuto() */
306 : /************************************************************************/
307 :
308 0 : CPLVirtualMem *VRTRawRasterBand::GetVirtualMemAuto(GDALRWFlag eRWFlag,
309 : int *pnPixelSpace,
310 : GIntBig *pnLineSpace,
311 : char **papszOptions)
312 :
313 : {
314 : // check the pointer to RawRasterBand
315 0 : if (m_poRawRaster == nullptr)
316 : {
317 : // use the super class method
318 0 : return VRTRasterBand::GetVirtualMemAuto(eRWFlag, pnPixelSpace,
319 0 : pnLineSpace, papszOptions);
320 : }
321 : // if available, use the RawRasterBand method (use mmap if available)
322 0 : return m_poRawRaster->GetVirtualMemAuto(eRWFlag, pnPixelSpace, pnLineSpace,
323 0 : papszOptions);
324 : }
325 :
326 : /************************************************************************/
327 : /* XMLInit() */
328 : /************************************************************************/
329 :
330 22 : CPLErr VRTRawRasterBand::XMLInit(const CPLXMLNode *psTree,
331 : const char *pszVRTPath,
332 : VRTMapSharedResources &oMapSharedSources)
333 :
334 : {
335 : const CPLErr eErr =
336 22 : VRTRasterBand::XMLInit(psTree, pszVRTPath, oMapSharedSources);
337 22 : if (eErr != CE_None)
338 0 : return eErr;
339 :
340 : /* -------------------------------------------------------------------- */
341 : /* Validate a bit. */
342 : /* -------------------------------------------------------------------- */
343 22 : if (psTree == nullptr || psTree->eType != CXT_Element ||
344 66 : !EQUAL(psTree->pszValue, "VRTRasterBand") ||
345 22 : !EQUAL(CPLGetXMLValue(psTree, "subClass", ""), "VRTRawRasterBand"))
346 : {
347 0 : CPLError(CE_Failure, CPLE_AppDefined,
348 : "Invalid node passed to VRTRawRasterBand::XMLInit().");
349 0 : return CE_Failure;
350 : }
351 :
352 : /* -------------------------------------------------------------------- */
353 : /* Prepare filename. */
354 : /* -------------------------------------------------------------------- */
355 22 : const char *pszFilename = CPLGetXMLValue(psTree, "SourceFilename", nullptr);
356 :
357 22 : if (pszFilename == nullptr)
358 : {
359 0 : CPLError(CE_Warning, CPLE_AppDefined,
360 : "Missing <SourceFilename> element in VRTRasterBand.");
361 0 : return CE_Failure;
362 : }
363 :
364 22 : const bool l_bRelativeToVRT = CPLTestBool(
365 : CPLGetXMLValue(psTree, "SourceFilename.relativeToVRT", "1"));
366 :
367 : /* -------------------------------------------------------------------- */
368 : /* Collect layout information. */
369 : /* -------------------------------------------------------------------- */
370 22 : int nWordDataSize = GDALGetDataTypeSizeBytes(GetRasterDataType());
371 :
372 22 : const char *pszImageOffset = CPLGetXMLValue(psTree, "ImageOffset", "0");
373 44 : const vsi_l_offset nImageOffset = CPLScanUIntBig(
374 22 : pszImageOffset, static_cast<int>(strlen(pszImageOffset)));
375 :
376 22 : int nPixelOffset = nWordDataSize;
377 22 : const char *pszPixelOffset = CPLGetXMLValue(psTree, "PixelOffset", nullptr);
378 22 : if (pszPixelOffset != nullptr)
379 : {
380 20 : nPixelOffset = atoi(pszPixelOffset);
381 : }
382 22 : if (nPixelOffset <= 0)
383 : {
384 0 : CPLError(CE_Failure, CPLE_AppDefined,
385 : "Invalid value for <PixelOffset> element : %d", nPixelOffset);
386 0 : return CE_Failure;
387 : }
388 :
389 22 : int nLineOffset = 0;
390 22 : const char *pszLineOffset = CPLGetXMLValue(psTree, "LineOffset", nullptr);
391 22 : if (pszLineOffset == nullptr)
392 : {
393 2 : if (nPixelOffset > INT_MAX / GetXSize())
394 : {
395 0 : CPLError(CE_Failure, CPLE_AppDefined, "Int overflow");
396 0 : return CE_Failure;
397 : }
398 2 : nLineOffset = nPixelOffset * GetXSize();
399 : }
400 : else
401 20 : nLineOffset = atoi(pszLineOffset);
402 :
403 22 : const char *pszByteOrder = CPLGetXMLValue(psTree, "ByteOrder", nullptr);
404 :
405 : /* -------------------------------------------------------------------- */
406 : /* Open the file, and setup the raw layer access to the data. */
407 : /* -------------------------------------------------------------------- */
408 22 : return SetRawLink(pszFilename, pszVRTPath, l_bRelativeToVRT, nImageOffset,
409 22 : nPixelOffset, nLineOffset, pszByteOrder);
410 : }
411 :
412 : /************************************************************************/
413 : /* SerializeToXML() */
414 : /************************************************************************/
415 :
416 7 : CPLXMLNode *VRTRawRasterBand::SerializeToXML(const char *pszVRTPath,
417 : bool &bHasWarnedAboutRAMUsage,
418 : size_t &nAccRAMUsage)
419 :
420 : {
421 :
422 : /* -------------------------------------------------------------------- */
423 : /* We can't set the layout if there is no open rawband. */
424 : /* -------------------------------------------------------------------- */
425 7 : if (m_poRawRaster == nullptr)
426 : {
427 0 : CPLError(CE_Failure, CPLE_AppDefined,
428 : "VRTRawRasterBand::SerializeToXML() fails because "
429 : "m_poRawRaster is NULL.");
430 0 : return nullptr;
431 : }
432 :
433 7 : CPLXMLNode *psTree = VRTRasterBand::SerializeToXML(
434 : pszVRTPath, bHasWarnedAboutRAMUsage, nAccRAMUsage);
435 :
436 : /* -------------------------------------------------------------------- */
437 : /* Set subclass. */
438 : /* -------------------------------------------------------------------- */
439 7 : CPLCreateXMLNode(CPLCreateXMLNode(psTree, CXT_Attribute, "subClass"),
440 : CXT_Text, "VRTRawRasterBand");
441 :
442 : /* -------------------------------------------------------------------- */
443 : /* Setup the filename with relative flag. */
444 : /* -------------------------------------------------------------------- */
445 14 : CPLXMLNode *psNode = CPLCreateXMLElementAndValue(psTree, "SourceFilename",
446 7 : m_pszSourceFilename);
447 :
448 7 : CPLCreateXMLNode(CPLCreateXMLNode(psNode, CXT_Attribute, "relativeToVRT"),
449 7 : CXT_Text, m_bRelativeToVRT ? "1" : "0");
450 :
451 : /* -------------------------------------------------------------------- */
452 : /* Set other layout information. */
453 : /* -------------------------------------------------------------------- */
454 :
455 7 : CPLCreateXMLElementAndValue(
456 : psTree, "ImageOffset",
457 7 : CPLSPrintf(CPL_FRMT_GUIB, m_poRawRaster->GetImgOffset()));
458 :
459 7 : CPLCreateXMLElementAndValue(
460 : psTree, "PixelOffset",
461 7 : CPLSPrintf("%d", m_poRawRaster->GetPixelOffset()));
462 :
463 7 : CPLCreateXMLElementAndValue(
464 7 : psTree, "LineOffset", CPLSPrintf("%d", m_poRawRaster->GetLineOffset()));
465 :
466 7 : switch (m_poRawRaster->GetByteOrder())
467 : {
468 3 : case RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN:
469 3 : CPLCreateXMLElementAndValue(psTree, "ByteOrder", "LSB");
470 3 : break;
471 4 : case RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN:
472 4 : CPLCreateXMLElementAndValue(psTree, "ByteOrder", "MSB");
473 4 : break;
474 0 : case RawRasterBand::ByteOrder::ORDER_VAX:
475 0 : CPLCreateXMLElementAndValue(psTree, "ByteOrder", "VAX");
476 0 : break;
477 : }
478 :
479 7 : return psTree;
480 : }
481 :
482 : /************************************************************************/
483 : /* GetFileList() */
484 : /************************************************************************/
485 :
486 3 : void VRTRawRasterBand::GetFileList(char ***ppapszFileList, int *pnSize,
487 : int *pnMaxSize, CPLHashSet *hSetFiles)
488 : {
489 3 : if (m_pszSourceFilename == nullptr)
490 1 : return;
491 :
492 : /* -------------------------------------------------------------------- */
493 : /* Is it already in the list ? */
494 : /* -------------------------------------------------------------------- */
495 3 : CPLString osSourceFilename;
496 3 : if (m_bRelativeToVRT && strlen(poDS->GetDescription()) > 0)
497 : osSourceFilename =
498 2 : CPLFormFilename(CPLGetDirname(poDS->GetDescription()),
499 2 : m_pszSourceFilename, nullptr);
500 : else
501 1 : osSourceFilename = m_pszSourceFilename;
502 :
503 3 : if (CPLHashSetLookup(hSetFiles, osSourceFilename) != nullptr)
504 1 : return;
505 :
506 : /* -------------------------------------------------------------------- */
507 : /* Grow array if necessary */
508 : /* -------------------------------------------------------------------- */
509 2 : if (*pnSize + 1 >= *pnMaxSize)
510 : {
511 2 : *pnMaxSize = 2 + 2 * (*pnMaxSize);
512 2 : *ppapszFileList = static_cast<char **>(
513 2 : CPLRealloc(*ppapszFileList, sizeof(char *) * (*pnMaxSize)));
514 : }
515 :
516 : /* -------------------------------------------------------------------- */
517 : /* Add the string to the list */
518 : /* -------------------------------------------------------------------- */
519 2 : (*ppapszFileList)[*pnSize] = CPLStrdup(osSourceFilename);
520 2 : (*ppapszFileList)[(*pnSize + 1)] = nullptr;
521 2 : CPLHashSetInsert(hSetFiles, (*ppapszFileList)[*pnSize]);
522 :
523 2 : (*pnSize)++;
524 :
525 2 : VRTRasterBand::GetFileList(ppapszFileList, pnSize, pnMaxSize, hSetFiles);
526 : }
527 :
528 : /*! @endcond */
|