Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: R Format Driver
4 : * Purpose: Read/write R stats package object format.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "rdataset.h"
32 :
33 : #include <cstddef>
34 : #include <cstdlib>
35 : #include <cstring>
36 : #if HAVE_FCNTL_H
37 : #include <fcntl.h>
38 : #endif
39 :
40 : #include <algorithm>
41 : #include <limits>
42 : #include <string>
43 : #include <utility>
44 :
45 : #include "cpl_conv.h"
46 : #include "cpl_error.h"
47 : #include "cpl_progress.h"
48 : #include "cpl_string.h"
49 : #include "cpl_vsi.h"
50 : #include "gdal.h"
51 : #include "gdal_frmts.h"
52 : #include "gdal_pam.h"
53 : #include "gdal_priv.h"
54 :
55 : // constexpr int R_NILSXP = 0;
56 : constexpr int R_LISTSXP = 2;
57 : constexpr int R_CHARSXP = 9;
58 : constexpr int R_INTSXP = 13;
59 : constexpr int R_REALSXP = 14;
60 : constexpr int R_STRSXP = 16;
61 :
62 : namespace
63 : {
64 :
65 : // TODO(schwehr): Move this to port/? for general use.
66 20 : bool SafeMult(GIntBig a, GIntBig b, GIntBig *result)
67 : {
68 20 : if (a == 0 || b == 0)
69 : {
70 0 : *result = 0;
71 0 : return true;
72 : }
73 :
74 20 : bool result_positive = (a >= 0 && b >= 0) || (a < 0 && b < 0);
75 20 : if (result_positive)
76 : {
77 : // Cannot convert min() to positive.
78 40 : if (a == std::numeric_limits<GIntBig>::min() ||
79 20 : b == std::numeric_limits<GIntBig>::min())
80 : {
81 0 : *result = 0;
82 0 : return false;
83 : }
84 20 : if (a < 0)
85 : {
86 0 : a = -a;
87 0 : b = -b;
88 : }
89 20 : if (a > std::numeric_limits<GIntBig>::max() / b)
90 : {
91 0 : *result = 0;
92 0 : return false;
93 : }
94 20 : *result = a * b;
95 20 : return true;
96 : }
97 :
98 0 : if (b < a)
99 0 : std::swap(a, b);
100 0 : if (a < (std::numeric_limits<GIntBig>::min() + 1) / b)
101 : {
102 0 : *result = 0;
103 0 : return false;
104 : }
105 :
106 0 : *result = a * b;
107 0 : return true;
108 : }
109 :
110 : } // namespace
111 :
112 : /************************************************************************/
113 : /* RRasterBand() */
114 : /************************************************************************/
115 :
116 7 : RRasterBand::RRasterBand(RDataset *poDSIn, int nBandIn,
117 7 : const double *padfMatrixValuesIn)
118 7 : : padfMatrixValues(padfMatrixValuesIn)
119 : {
120 7 : poDS = poDSIn;
121 7 : nBand = nBandIn;
122 :
123 7 : eDataType = GDT_Float64;
124 :
125 7 : nBlockXSize = poDSIn->nRasterXSize;
126 7 : nBlockYSize = 1;
127 7 : }
128 :
129 : /************************************************************************/
130 : /* IReadBlock() */
131 : /************************************************************************/
132 :
133 45 : CPLErr RRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
134 : void *pImage)
135 : {
136 45 : memcpy(pImage, padfMatrixValues + nBlockYOff * nBlockXSize,
137 45 : nBlockXSize * 8);
138 45 : return CE_None;
139 : }
140 :
141 : /************************************************************************/
142 : /* ==================================================================== */
143 : /* RDataset() */
144 : /* ==================================================================== */
145 : /************************************************************************/
146 :
147 : /************************************************************************/
148 : /* RDataset() */
149 : /************************************************************************/
150 :
151 10 : RDataset::RDataset()
152 10 : : fp(nullptr), bASCII(FALSE), nStartOfData(0), padfMatrixValues(nullptr)
153 : {
154 10 : }
155 :
156 : /************************************************************************/
157 : /* ~RDataset() */
158 : /************************************************************************/
159 :
160 20 : RDataset::~RDataset()
161 : {
162 10 : FlushCache(true);
163 10 : CPLFree(padfMatrixValues);
164 :
165 10 : if (fp)
166 10 : VSIFCloseL(fp);
167 20 : }
168 :
169 : /************************************************************************/
170 : /* ASCIIFGets() */
171 : /* */
172 : /* Fetch one line from an ASCII source into osLastStringRead. */
173 : /************************************************************************/
174 :
175 1385 : const char *RDataset::ASCIIFGets()
176 :
177 : {
178 1385 : char chNextChar = '\0';
179 :
180 1385 : osLastStringRead.resize(0);
181 :
182 3952 : do
183 : {
184 5337 : chNextChar = '\n';
185 5337 : VSIFReadL(&chNextChar, 1, 1, fp);
186 5337 : if (chNextChar != '\n')
187 3952 : osLastStringRead += chNextChar;
188 5337 : } while (chNextChar != '\n' && chNextChar != '\0');
189 :
190 2770 : return osLastStringRead;
191 : }
192 :
193 : /************************************************************************/
194 : /* ReadInteger() */
195 : /************************************************************************/
196 :
197 190 : int RDataset::ReadInteger()
198 :
199 : {
200 190 : if (bASCII)
201 : {
202 95 : return atoi(ASCIIFGets());
203 : }
204 :
205 95 : GInt32 nValue = 0;
206 :
207 95 : if (VSIFReadL(&nValue, 4, 1, fp) != 1)
208 0 : return -1;
209 95 : CPL_MSBPTR32(&nValue);
210 :
211 95 : return nValue;
212 : }
213 :
214 : /************************************************************************/
215 : /* ReadFloat() */
216 : /************************************************************************/
217 :
218 1280 : double RDataset::ReadFloat()
219 :
220 : {
221 1280 : if (bASCII)
222 : {
223 1280 : return CPLAtof(ASCIIFGets());
224 : }
225 :
226 0 : double dfValue = 0.0;
227 :
228 0 : if (VSIFReadL(&dfValue, 8, 1, fp) != 1)
229 0 : return -1;
230 0 : CPL_MSBPTR64(&dfValue);
231 :
232 0 : return dfValue;
233 : }
234 :
235 : /************************************************************************/
236 : /* ReadString() */
237 : /************************************************************************/
238 :
239 20 : const char *RDataset::ReadString()
240 :
241 : {
242 20 : if (ReadInteger() % 256 != R_CHARSXP)
243 : {
244 0 : osLastStringRead = "";
245 0 : return "";
246 : }
247 :
248 20 : const int nLenSigned = ReadInteger();
249 20 : if (nLenSigned < 0)
250 : {
251 0 : osLastStringRead = "";
252 0 : return "";
253 : }
254 20 : const size_t nLen = static_cast<size_t>(nLenSigned);
255 :
256 20 : char *pachWrkBuf = static_cast<char *>(VSIMalloc(nLen));
257 20 : if (pachWrkBuf == nullptr)
258 : {
259 0 : osLastStringRead = "";
260 0 : return "";
261 : }
262 20 : if (VSIFReadL(pachWrkBuf, 1, nLen, fp) != nLen)
263 : {
264 0 : osLastStringRead = "";
265 0 : CPLFree(pachWrkBuf);
266 0 : return "";
267 : }
268 :
269 20 : if (bASCII)
270 : {
271 : // Suck up newline and any extra junk.
272 10 : ASCIIFGets();
273 : }
274 :
275 20 : osLastStringRead.assign(pachWrkBuf, nLen);
276 20 : CPLFree(pachWrkBuf);
277 :
278 20 : return osLastStringRead;
279 : }
280 :
281 : /************************************************************************/
282 : /* ReadPair() */
283 : /************************************************************************/
284 :
285 30 : bool RDataset::ReadPair(CPLString &osObjName, int &nObjCode)
286 :
287 : {
288 30 : nObjCode = ReadInteger();
289 30 : if (nObjCode == 254)
290 10 : return true;
291 :
292 20 : if ((nObjCode % 256) != R_LISTSXP)
293 : {
294 0 : CPLError(CE_Failure, CPLE_OpenFailed,
295 : "Did not find expected object pair object.");
296 0 : return false;
297 : }
298 :
299 20 : int nPairCount = ReadInteger();
300 20 : if (nPairCount != 1)
301 : {
302 0 : CPLError(CE_Failure, CPLE_OpenFailed,
303 : "Did not find expected pair count of 1.");
304 0 : return false;
305 : }
306 :
307 : // Read the object name.
308 20 : const char *pszName = ReadString();
309 20 : if (pszName == nullptr || pszName[0] == '\0')
310 0 : return false;
311 :
312 20 : osObjName = pszName;
313 :
314 : // Confirm that we have a numeric matrix object.
315 20 : nObjCode = ReadInteger();
316 :
317 20 : return true;
318 : }
319 :
320 : /************************************************************************/
321 : /* Identify() */
322 : /************************************************************************/
323 :
324 50105 : int RDataset::Identify(GDALOpenInfo *poOpenInfo)
325 : {
326 50105 : if (poOpenInfo->nHeaderBytes < 50)
327 46002 : return FALSE;
328 :
329 : // If the extension is .rda and the file type is gzip
330 : // compressed we assume it is a gzipped R binary file.
331 4137 : if (memcmp(poOpenInfo->pabyHeader, "\037\213\b", 3) == 0 &&
332 34 : EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "rda"))
333 6 : return TRUE;
334 :
335 : // Is this an ASCII or XDR binary R file?
336 4097 : if (!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "RDA2\nA\n") &&
337 4087 : !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "RDX2\nX\n"))
338 4083 : return FALSE;
339 :
340 14 : return TRUE;
341 : }
342 :
343 : /************************************************************************/
344 : /* Open() */
345 : /************************************************************************/
346 :
347 10 : GDALDataset *RDataset::Open(GDALOpenInfo *poOpenInfo)
348 : {
349 : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
350 : if (poOpenInfo->pabyHeader == nullptr)
351 : return nullptr;
352 : #else
353 : // During fuzzing, do not use Identify to reject crazy content.
354 10 : if (!Identify(poOpenInfo))
355 0 : return nullptr;
356 : #endif
357 :
358 : // Confirm the requested access is supported.
359 10 : if (poOpenInfo->eAccess == GA_Update)
360 : {
361 0 : CPLError(CE_Failure, CPLE_NotSupported,
362 : "The R driver does not support update access to existing"
363 : " datasets.");
364 0 : return nullptr;
365 : }
366 :
367 : // Do we need to route the file through the decompression machinery?
368 10 : const bool bCompressed =
369 10 : memcmp(poOpenInfo->pabyHeader, "\037\213\b", 3) == 0;
370 : const CPLString osAdjustedFilename =
371 30 : std::string(bCompressed ? "/vsigzip/" : "") + poOpenInfo->pszFilename;
372 :
373 : // Establish this as a dataset and open the file using VSI*L.
374 20 : auto poDS = std::make_unique<RDataset>();
375 :
376 10 : poDS->fp = VSIFOpenL(osAdjustedFilename, "r");
377 10 : if (poDS->fp == nullptr)
378 : {
379 0 : return nullptr;
380 : }
381 :
382 10 : poDS->bASCII = STARTS_WITH_CI(
383 : reinterpret_cast<char *>(poOpenInfo->pabyHeader), "RDA2\nA\n");
384 :
385 : // Confirm this is a version 2 file.
386 10 : VSIFSeekL(poDS->fp, 7, SEEK_SET);
387 10 : if (poDS->ReadInteger() != R_LISTSXP)
388 : {
389 0 : CPLError(CE_Failure, CPLE_OpenFailed,
390 : "It appears %s is not a version 2 R object file after all!",
391 : poOpenInfo->pszFilename);
392 0 : return nullptr;
393 : }
394 :
395 : // Skip the version values.
396 10 : poDS->ReadInteger();
397 10 : poDS->ReadInteger();
398 :
399 : // Confirm we have a numeric vector object in a pairlist.
400 20 : CPLString osObjName;
401 10 : int nObjCode = 0;
402 :
403 10 : if (!poDS->ReadPair(osObjName, nObjCode))
404 : {
405 0 : return nullptr;
406 : }
407 :
408 10 : if (nObjCode % 256 != R_REALSXP)
409 : {
410 0 : CPLError(CE_Failure, CPLE_OpenFailed,
411 : "Failed to find expected numeric vector object.");
412 0 : return nullptr;
413 : }
414 :
415 10 : poDS->SetMetadataItem("R_OBJECT_NAME", osObjName);
416 :
417 : // Read the count.
418 10 : const int nValueCount = poDS->ReadInteger();
419 10 : if (nValueCount < 0)
420 : {
421 0 : CPLError(CE_Failure, CPLE_AppDefined, "nValueCount < 0: %d",
422 : nValueCount);
423 0 : return nullptr;
424 : }
425 :
426 10 : poDS->nStartOfData = VSIFTellL(poDS->fp);
427 :
428 : // TODO(schwehr): Factor in the size of doubles.
429 : VSIStatBufL stat;
430 : const int dStatSuccess =
431 10 : VSIStatExL(osAdjustedFilename, &stat, VSI_STAT_SIZE_FLAG);
432 20 : if (dStatSuccess != 0 || static_cast<vsi_l_offset>(nValueCount) >
433 10 : stat.st_size - poDS->nStartOfData)
434 : {
435 0 : CPLError(CE_Failure, CPLE_AppDefined,
436 : "Corrupt file. "
437 : "Object claims to be larger than available bytes. "
438 : "%d > " CPL_FRMT_GUIB,
439 0 : nValueCount, stat.st_size - poDS->nStartOfData);
440 0 : return nullptr;
441 : }
442 :
443 : // Read/Skip ahead to attributes.
444 10 : if (poDS->bASCII)
445 : {
446 10 : poDS->padfMatrixValues =
447 5 : static_cast<double *>(VSIMalloc2(nValueCount, sizeof(double)));
448 5 : if (poDS->padfMatrixValues == nullptr)
449 : {
450 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate %d doubles",
451 : nValueCount);
452 0 : return nullptr;
453 : }
454 1285 : for (int iValue = 0; iValue < nValueCount; iValue++)
455 1280 : poDS->padfMatrixValues[iValue] = poDS->ReadFloat();
456 : }
457 : else
458 : {
459 5 : VSIFSeekL(poDS->fp, 8 * nValueCount, SEEK_CUR);
460 : }
461 :
462 : // Read pairs till we run out, trying to find a few items that
463 : // have special meaning to us.
464 10 : poDS->nRasterXSize = 0;
465 10 : poDS->nRasterYSize = 0;
466 10 : int nBandCount = 0;
467 :
468 20 : while (poDS->ReadPair(osObjName, nObjCode) && nObjCode != 254)
469 : {
470 10 : if (osObjName == "dim" && nObjCode % 256 == R_INTSXP)
471 : {
472 10 : const int nCount = poDS->ReadInteger();
473 10 : if (nCount == 2)
474 : {
475 0 : poDS->nRasterXSize = poDS->ReadInteger();
476 0 : poDS->nRasterYSize = poDS->ReadInteger();
477 0 : nBandCount = 1;
478 : }
479 10 : else if (nCount == 3)
480 : {
481 10 : poDS->nRasterXSize = poDS->ReadInteger();
482 10 : poDS->nRasterYSize = poDS->ReadInteger();
483 10 : nBandCount = poDS->ReadInteger();
484 : }
485 : else
486 : {
487 0 : CPLError(CE_Failure, CPLE_AppDefined,
488 : "R 'dim' dimension wrong.");
489 0 : return nullptr;
490 : }
491 : }
492 0 : else if (nObjCode % 256 == R_REALSXP)
493 : {
494 0 : int nCount = poDS->ReadInteger();
495 0 : while (nCount > 0 && !VSIFEofL(poDS->fp))
496 : {
497 0 : nCount--;
498 0 : poDS->ReadFloat();
499 : }
500 : }
501 0 : else if (nObjCode % 256 == R_INTSXP)
502 : {
503 0 : int nCount = poDS->ReadInteger();
504 0 : while (nCount > 0 && !VSIFEofL(poDS->fp))
505 : {
506 0 : nCount--;
507 0 : poDS->ReadInteger();
508 : }
509 : }
510 0 : else if (nObjCode % 256 == R_STRSXP)
511 : {
512 0 : int nCount = poDS->ReadInteger();
513 0 : while (nCount > 0 && !VSIFEofL(poDS->fp))
514 : {
515 0 : nCount--;
516 0 : poDS->ReadString();
517 : }
518 : }
519 0 : else if (nObjCode % 256 == R_CHARSXP)
520 : {
521 0 : poDS->ReadString();
522 : }
523 : }
524 :
525 10 : if (poDS->nRasterXSize == 0)
526 : {
527 0 : CPLError(CE_Failure, CPLE_AppDefined,
528 : "Failed to find dim dimension information for R dataset.");
529 0 : return nullptr;
530 : }
531 :
532 20 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
533 10 : !GDALCheckBandCount(nBandCount, TRUE))
534 : {
535 0 : return nullptr;
536 : }
537 :
538 10 : GIntBig result = 0;
539 10 : bool ok = SafeMult(nBandCount, poDS->nRasterXSize, &result);
540 10 : ok &= SafeMult(result, poDS->nRasterYSize, &result);
541 :
542 10 : if (!ok || nValueCount < result)
543 : {
544 0 : CPLError(CE_Failure, CPLE_AppDefined, "Not enough pixel data.");
545 0 : return nullptr;
546 : }
547 :
548 : // Create the raster band object(s).
549 24 : for (int iBand = 0; iBand < nBandCount; iBand++)
550 : {
551 0 : std::unique_ptr<GDALRasterBand> poBand;
552 :
553 14 : if (poDS->bASCII)
554 7 : poBand = std::make_unique<RRasterBand>(
555 7 : poDS.get(), iBand + 1,
556 14 : poDS->padfMatrixValues + static_cast<size_t>(iBand) *
557 7 : poDS->nRasterXSize *
558 14 : poDS->nRasterYSize);
559 : else
560 : {
561 14 : poBand = RawRasterBand::Create(
562 7 : poDS.get(), iBand + 1, poDS->fp,
563 14 : poDS->nStartOfData +
564 7 : static_cast<vsi_l_offset>(poDS->nRasterXSize) *
565 7 : poDS->nRasterYSize * 8 * iBand,
566 7 : 8, poDS->nRasterXSize * 8, GDT_Float64,
567 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
568 7 : RawRasterBand::OwnFP::NO);
569 7 : if (!poBand)
570 0 : return nullptr;
571 : }
572 14 : poDS->SetBand(iBand + 1, std::move(poBand));
573 : }
574 :
575 : // Initialize any PAM information.
576 10 : poDS->SetDescription(poOpenInfo->pszFilename);
577 10 : poDS->TryLoadXML();
578 :
579 : // Check for overviews.
580 10 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
581 :
582 10 : return poDS.release();
583 : }
584 :
585 : /************************************************************************/
586 : /* GDALRegister_R() */
587 : /************************************************************************/
588 :
589 1512 : void GDALRegister_R()
590 :
591 : {
592 1512 : if (GDALGetDriverByName("R") != nullptr)
593 295 : return;
594 :
595 1217 : GDALDriver *poDriver = new GDALDriver();
596 :
597 1217 : poDriver->SetDescription("R");
598 1217 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
599 1217 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "R Object Data Store");
600 1217 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/r.html");
601 1217 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rda");
602 1217 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Float32");
603 1217 : poDriver->SetMetadataItem(
604 : GDAL_DMD_CREATIONOPTIONLIST,
605 : "<CreationOptionList>"
606 : " <Option name='ASCII' type='boolean' description='For ASCII output, "
607 : "default NO'/>"
608 : " <Option name='COMPRESS' type='boolean' description='Produced "
609 : "Compressed output, default YES'/>"
610 1217 : "</CreationOptionList>");
611 :
612 1217 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
613 :
614 1217 : poDriver->pfnOpen = RDataset::Open;
615 1217 : poDriver->pfnIdentify = RDataset::Identify;
616 1217 : poDriver->pfnCreateCopy = RCreateCopy;
617 :
618 1217 : GetGDALDriverManager()->RegisterDriver(poDriver);
619 : }
|