Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: gdalvirtualmem.cpp
4 : * Project: GDAL
5 : * Purpose: Dataset and rasterband exposed as a virtual memory mapping.
6 : * Author: Even Rouault, <even dot rouault at spatialys.com>
7 : *
8 : **********************************************************************
9 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "gdal.h"
16 : #include "gdal_priv.h"
17 :
18 : #include <cstddef>
19 : #include <cstring>
20 :
21 : #include <algorithm>
22 :
23 : #include "cpl_conv.h"
24 : #include "cpl_error.h"
25 : #include "cpl_virtualmem.h"
26 :
27 : // To be changed if we go to 64-bit RasterIO coordinates and spacing.
28 : using coord_type = int;
29 : using spacing_type = int;
30 :
31 : /************************************************************************/
32 : /* GDALVirtualMem */
33 : /************************************************************************/
34 :
35 : class GDALVirtualMem
36 : {
37 : GDALDatasetH hDS = nullptr;
38 : GDALRasterBandH hBand = nullptr;
39 : coord_type nXOff = 0;
40 : coord_type nYOff = 0;
41 : // int nXSize;
42 : // int nYSize;
43 : coord_type nBufXSize = 0;
44 : coord_type nBufYSize = 0;
45 : GDALDataType eBufType = GDT_Byte;
46 : int nBandCount = 0;
47 : int *panBandMap = nullptr;
48 : int nPixelSpace = 0;
49 : GIntBig nLineSpace = 0;
50 : GIntBig nBandSpace = 0;
51 :
52 : bool bIsCompact = false;
53 : bool bIsBandSequential = false;
54 :
55 48 : bool IsCompact() const
56 : {
57 48 : return bIsCompact;
58 : }
59 :
60 116 : bool IsBandSequential() const
61 : {
62 116 : return bIsBandSequential;
63 : }
64 :
65 : void GetXYBand(size_t nOffset, coord_type &x, coord_type &y,
66 : int &band) const;
67 : size_t GetOffset(const coord_type &x, const coord_type &y, int band) const;
68 : bool GotoNextPixel(coord_type &x, coord_type &y, int &band) const;
69 :
70 : void DoIOBandSequential(GDALRWFlag eRWFlag, size_t nOffset, void *pPage,
71 : size_t nBytes) const;
72 : void DoIOPixelInterleaved(GDALRWFlag eRWFlag, size_t nOffset, void *pPage,
73 : size_t nBytes) const;
74 :
75 : CPL_DISALLOW_COPY_ASSIGN(GDALVirtualMem)
76 :
77 : public:
78 : GDALVirtualMem(GDALDatasetH hDS, GDALRasterBandH hBand,
79 : const coord_type &nXOff, const coord_type &nYOff,
80 : const coord_type &nXSize, const coord_type &nYSize,
81 : const coord_type &nBufXSize, const coord_type &nBufYSize,
82 : GDALDataType eBufType, int nBandCount,
83 : const int *panBandMapIn, int nPixelSpace, GIntBig nLineSpace,
84 : GIntBig nBandSpace);
85 : ~GDALVirtualMem();
86 :
87 : static void FillCacheBandSequential(CPLVirtualMem *ctxt, size_t nOffset,
88 : void *pPageToFill, size_t nToFill,
89 : void *pUserData);
90 : static void SaveFromCacheBandSequential(CPLVirtualMem *ctxt, size_t nOffset,
91 : const void *pPageToBeEvicted,
92 : size_t nToEvicted, void *pUserData);
93 :
94 : static void FillCachePixelInterleaved(CPLVirtualMem *ctxt, size_t nOffset,
95 : void *pPageToFill, size_t nToFill,
96 : void *pUserData);
97 : static void SaveFromCachePixelInterleaved(CPLVirtualMem *ctxt,
98 : size_t nOffset,
99 : const void *pPageToBeEvicted,
100 : size_t nToEvicted,
101 : void *pUserData);
102 :
103 : static void Destroy(void *pUserData);
104 : };
105 :
106 : /************************************************************************/
107 : /* GDALVirtualMem() */
108 : /************************************************************************/
109 :
110 12 : GDALVirtualMem::GDALVirtualMem(
111 : GDALDatasetH hDSIn, GDALRasterBandH hBandIn, const coord_type &nXOffIn,
112 : const coord_type &nYOffIn, const coord_type & /* nXSize */,
113 : const coord_type & /* nYSize */, const coord_type &nBufXSizeIn,
114 : const coord_type &nBufYSizeIn, GDALDataType eBufTypeIn, int nBandCountIn,
115 : const int *panBandMapIn, int nPixelSpaceIn, GIntBig nLineSpaceIn,
116 12 : GIntBig nBandSpaceIn)
117 12 : : hDS(hDSIn), hBand(hBandIn), nXOff(nXOffIn), nYOff(nYOffIn),
118 : // TODO(schwehr): Why not used or removed?
119 : // nXSize(nXSize),
120 : // nYSize(nYSize),
121 12 : nBufXSize(nBufXSizeIn), nBufYSize(nBufYSizeIn), eBufType(eBufTypeIn),
122 : nBandCount(nBandCountIn), nPixelSpace(nPixelSpaceIn),
123 12 : nLineSpace(nLineSpaceIn), nBandSpace(nBandSpaceIn)
124 : {
125 12 : if (hDS != nullptr)
126 : {
127 3 : panBandMap = static_cast<int *>(CPLMalloc(nBandCount * sizeof(int)));
128 3 : if (panBandMapIn)
129 : {
130 3 : memcpy(panBandMap, panBandMapIn, nBandCount * sizeof(int));
131 : }
132 : else
133 : {
134 0 : for (int i = 0; i < nBandCount; i++)
135 0 : panBandMap[i] = i + 1;
136 : }
137 : }
138 : else
139 : {
140 9 : panBandMap = nullptr;
141 9 : nBandCount = 1;
142 : }
143 :
144 12 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
145 12 : if (nPixelSpace == nDataTypeSize &&
146 11 : nLineSpace == static_cast<GIntBig>(nBufXSize) * nPixelSpace &&
147 11 : nBandSpace == nBufYSize * nLineSpace)
148 11 : bIsCompact = true;
149 1 : else if (nBandSpace == nDataTypeSize &&
150 1 : nPixelSpace == nBandCount * nBandSpace &&
151 1 : nLineSpace == static_cast<GIntBig>(nBufXSize) * nPixelSpace)
152 1 : bIsCompact = true;
153 : else
154 0 : bIsCompact = false;
155 :
156 12 : bIsBandSequential = nBandSpace >= nBufYSize * nLineSpace;
157 12 : }
158 :
159 : /************************************************************************/
160 : /* ~GDALVirtualMem() */
161 : /************************************************************************/
162 :
163 24 : GDALVirtualMem::~GDALVirtualMem()
164 : {
165 12 : CPLFree(panBandMap);
166 12 : }
167 :
168 : /************************************************************************/
169 : /* GetXYBand() */
170 : /************************************************************************/
171 :
172 90 : void GDALVirtualMem::GetXYBand(size_t nOffset, coord_type &x, coord_type &y,
173 : int &band) const
174 : {
175 90 : if (IsBandSequential())
176 : {
177 77 : if (nBandCount == 1)
178 62 : band = 0;
179 : else
180 15 : band = static_cast<int>(nOffset / nBandSpace);
181 77 : y = static_cast<coord_type>((nOffset - band * nBandSpace) / nLineSpace);
182 77 : x = static_cast<coord_type>(
183 77 : (nOffset - band * nBandSpace - y * nLineSpace) / nPixelSpace);
184 : }
185 : else
186 : {
187 13 : y = static_cast<coord_type>(nOffset / nLineSpace);
188 13 : x = static_cast<coord_type>((nOffset - y * nLineSpace) / nPixelSpace);
189 13 : if (nBandCount == 1)
190 0 : band = 0;
191 : else
192 13 : band = static_cast<int>((nOffset - y * nLineSpace -
193 13 : static_cast<size_t>(x) * nPixelSpace) /
194 13 : nBandSpace);
195 : }
196 90 : }
197 :
198 : /************************************************************************/
199 : /* GotoNextPixel() */
200 : /************************************************************************/
201 :
202 26 : bool GDALVirtualMem::GotoNextPixel(coord_type &x, coord_type &y,
203 : int &band) const
204 : {
205 26 : if (IsBandSequential())
206 : {
207 19 : ++x;
208 19 : if (x == nBufXSize)
209 : {
210 19 : x = 0;
211 19 : ++y;
212 : }
213 19 : if (y == nBufYSize)
214 : {
215 0 : y = 0;
216 0 : band++;
217 0 : if (band == nBandCount)
218 0 : return false;
219 : }
220 : }
221 : else
222 : {
223 7 : band++;
224 7 : if (band == nBandCount)
225 : {
226 7 : band = 0;
227 7 : ++x;
228 : }
229 7 : if (x == nBufXSize)
230 : {
231 4 : x = 0;
232 4 : ++y;
233 4 : if (y == nBufYSize)
234 0 : return false;
235 : }
236 : }
237 26 : return true;
238 : }
239 :
240 : /************************************************************************/
241 : /* GetOffset() */
242 : /************************************************************************/
243 :
244 191 : size_t GDALVirtualMem::GetOffset(const coord_type &x, const coord_type &y,
245 : int band) const
246 : {
247 191 : return static_cast<size_t>(static_cast<size_t>(x) * nPixelSpace +
248 191 : y * nLineSpace + band * nBandSpace);
249 : }
250 :
251 : /************************************************************************/
252 : /* DoIOPixelInterleaved() */
253 : /************************************************************************/
254 :
255 9 : void GDALVirtualMem::DoIOPixelInterleaved(GDALRWFlag eRWFlag,
256 : const size_t nOffset, void *pPage,
257 : size_t nBytes) const
258 : {
259 9 : coord_type x = 0;
260 9 : coord_type y = 0;
261 9 : int band = 0;
262 :
263 9 : GetXYBand(nOffset, x, y, band);
264 : #ifdef DEBUG_VERBOSE
265 : fprintf(stderr, "eRWFlag=%d, nOffset=%d, x=%d, y=%d, band=%d\n", /*ok*/
266 : eRWFlag, static_cast<int>(nOffset), x, y, band);
267 : #endif
268 :
269 9 : if (eRWFlag == GF_Read && !IsCompact())
270 0 : memset(pPage, 0, nBytes);
271 :
272 9 : if (band >= nBandCount)
273 : {
274 0 : band = nBandCount - 1;
275 0 : if (!GotoNextPixel(x, y, band))
276 5 : return;
277 : }
278 9 : else if (x >= nBufXSize)
279 : {
280 0 : x = nBufXSize - 1;
281 0 : band = nBandCount - 1;
282 0 : if (!GotoNextPixel(x, y, band))
283 0 : return;
284 : }
285 :
286 9 : size_t nOffsetRecompute = GetOffset(x, y, band);
287 9 : CPLAssert(nOffsetRecompute >= nOffset);
288 9 : size_t nOffsetShift = nOffsetRecompute - nOffset;
289 9 : if (nOffsetShift >= nBytes)
290 0 : return;
291 :
292 : // If we don't start at the first band for that given pixel, load/store
293 : // the remaining bands
294 9 : if (band > 0)
295 : {
296 3 : size_t nEndOffsetEndOfPixel = GetOffset(x, y, nBandCount);
297 3 : int bandEnd = nBandCount;
298 : // Check that we have enough space to load/store until last band
299 : // Should be always OK unless the number of bands is really huge
300 3 : if (nEndOffsetEndOfPixel - nOffset > nBytes)
301 : {
302 : // Not enough space: find last possible band
303 : coord_type xEnd, yEnd;
304 0 : GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd);
305 0 : CPLAssert(x == xEnd);
306 0 : CPLAssert(y == yEnd);
307 : }
308 :
309 : // Finish reading/writing the remaining bands for that pixel
310 3 : CPL_IGNORE_RET_VAL(GDALDatasetRasterIO(
311 3 : hDS, eRWFlag, nXOff + x, nYOff + y, 1, 1,
312 3 : static_cast<char *>(pPage) + nOffsetShift, 1, 1, eBufType,
313 3 : bandEnd - band, panBandMap + band, nPixelSpace,
314 3 : static_cast<spacing_type>(nLineSpace),
315 3 : static_cast<spacing_type>(nBandSpace)));
316 :
317 3 : if (bandEnd < nBandCount)
318 0 : return;
319 :
320 3 : band = nBandCount - 1;
321 3 : if (!GotoNextPixel(x, y, band))
322 0 : return;
323 3 : nOffsetRecompute = GetOffset(x, y, 0);
324 3 : nOffsetShift = nOffsetRecompute - nOffset;
325 3 : if (nOffsetShift >= nBytes)
326 0 : return;
327 : }
328 :
329 : // Is there enough place to store/load up to the end of current line ?
330 9 : size_t nEndOffsetEndOfLine = GetOffset(nBufXSize - 1, y, nBandCount);
331 9 : if (nEndOffsetEndOfLine - nOffset > nBytes)
332 : {
333 : // No : read/write as many pixels on this line as possible
334 : coord_type xEnd, yEnd;
335 : int bandEnd;
336 4 : GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd);
337 4 : CPLAssert(y == yEnd);
338 :
339 4 : if (x < xEnd)
340 : {
341 4 : CPL_IGNORE_RET_VAL(GDALDatasetRasterIO(
342 4 : hDS, eRWFlag, nXOff + x, nYOff + y, xEnd - x, 1,
343 : static_cast<char *>(pPage) + nOffsetShift, xEnd - x, 1,
344 4 : eBufType, nBandCount, panBandMap, nPixelSpace,
345 4 : static_cast<spacing_type>(nLineSpace),
346 4 : static_cast<spacing_type>(nBandSpace)));
347 : }
348 :
349 : // Are there partial bands to read/write for the last pixel ?
350 4 : if (bandEnd > 0)
351 : {
352 3 : x = xEnd;
353 3 : nOffsetRecompute = GetOffset(x, y, 0);
354 3 : nOffsetShift = nOffsetRecompute - nOffset;
355 3 : if (nOffsetShift >= nBytes)
356 0 : return;
357 :
358 3 : if (bandEnd >= nBandCount)
359 0 : bandEnd = nBandCount;
360 :
361 3 : CPL_IGNORE_RET_VAL(GDALDatasetRasterIO(
362 3 : hDS, eRWFlag, nXOff + x, nYOff + y, 1, 1,
363 3 : static_cast<char *>(pPage) + nOffsetShift, 1, 1, eBufType,
364 3 : bandEnd, panBandMap, nPixelSpace,
365 3 : static_cast<spacing_type>(nLineSpace),
366 3 : static_cast<spacing_type>(nBandSpace)));
367 : }
368 :
369 4 : return;
370 : }
371 :
372 : // Yes, enough place to read/write until end of line
373 5 : if (x > 0 || nBytes - nOffsetShift < static_cast<size_t>(nLineSpace))
374 : {
375 4 : CPL_IGNORE_RET_VAL(GDALDatasetRasterIO(
376 4 : hDS, eRWFlag, nXOff + x, nYOff + y, nBufXSize - x, 1,
377 4 : static_cast<char *>(pPage) + nOffsetShift, nBufXSize - x, 1,
378 4 : eBufType, nBandCount, panBandMap, nPixelSpace,
379 4 : static_cast<spacing_type>(nLineSpace),
380 4 : static_cast<spacing_type>(nBandSpace)));
381 :
382 : // Go to beginning of next line
383 4 : x = nBufXSize - 1;
384 4 : band = nBandCount - 1;
385 4 : if (!GotoNextPixel(x, y, band))
386 0 : return;
387 4 : nOffsetRecompute = GetOffset(x, y, 0);
388 4 : nOffsetShift = nOffsetRecompute - nOffset;
389 4 : if (nOffsetShift >= nBytes)
390 0 : return;
391 : }
392 :
393 : // How many whole lines can we store/load ?
394 5 : coord_type nLineCount =
395 5 : static_cast<coord_type>((nBytes - nOffsetShift) / nLineSpace);
396 5 : if (y + nLineCount > nBufYSize)
397 0 : nLineCount = nBufYSize - y;
398 5 : if (nLineCount > 0)
399 : {
400 5 : CPL_IGNORE_RET_VAL(GDALDatasetRasterIO(
401 5 : hDS, eRWFlag, nXOff + 0, nYOff + y, nBufXSize, nLineCount,
402 5 : static_cast<GByte *>(pPage) + nOffsetShift, nBufXSize, nLineCount,
403 5 : eBufType, nBandCount, panBandMap, nPixelSpace,
404 5 : static_cast<spacing_type>(nLineSpace),
405 5 : static_cast<spacing_type>(nBandSpace)));
406 :
407 5 : y += nLineCount;
408 5 : if (y == nBufYSize)
409 1 : return;
410 4 : nOffsetRecompute = GetOffset(x, y, 0);
411 4 : nOffsetShift = nOffsetRecompute - nOffset;
412 : }
413 :
414 4 : if (nOffsetShift < nBytes)
415 : {
416 4 : DoIOPixelInterleaved(eRWFlag, nOffsetRecompute,
417 : static_cast<char *>(pPage) + nOffsetShift,
418 : nBytes - nOffsetShift);
419 : }
420 : }
421 :
422 : /************************************************************************/
423 : /* DoIOPixelInterleaved() */
424 : /************************************************************************/
425 :
426 58 : void GDALVirtualMem::DoIOBandSequential(GDALRWFlag eRWFlag,
427 : const size_t nOffset, void *pPage,
428 : size_t nBytes) const
429 : {
430 58 : coord_type x = 0;
431 58 : coord_type y = 0;
432 :
433 58 : int band = 0;
434 58 : GetXYBand(nOffset, x, y, band);
435 : #if DEBUG_VERBOSE
436 : fprintf(stderr, "eRWFlag=%d, nOffset=%d, x=%d, y=%d, band=%d\n", /*ok*/
437 : eRWFlag, static_cast<int>(nOffset), x, y, band);
438 : #endif
439 :
440 58 : if (eRWFlag == GF_Read && !IsCompact())
441 0 : memset(pPage, 0, nBytes);
442 :
443 58 : if (x >= nBufXSize)
444 : {
445 0 : x = nBufXSize - 1;
446 0 : if (!GotoNextPixel(x, y, band))
447 37 : return;
448 : }
449 58 : else if (y >= nBufYSize)
450 : {
451 0 : x = nBufXSize - 1;
452 0 : y = nBufYSize - 1;
453 0 : if (!GotoNextPixel(x, y, band))
454 0 : return;
455 : }
456 :
457 58 : size_t nOffsetRecompute = GetOffset(x, y, band);
458 58 : CPLAssert(nOffsetRecompute >= nOffset);
459 58 : size_t nOffsetShift = nOffsetRecompute - nOffset;
460 58 : if (nOffsetShift >= nBytes)
461 0 : return;
462 :
463 : // Is there enough place to store/load up to the end of current line?
464 58 : size_t nEndOffsetEndOfLine = GetOffset(nBufXSize, y, band);
465 58 : if (nEndOffsetEndOfLine - nOffset > nBytes)
466 : {
467 : // No : read/write as many pixels on this line as possible
468 : coord_type xEnd, yEnd;
469 : int bandEnd;
470 19 : GetXYBand(nOffset + nBytes, xEnd, yEnd, bandEnd);
471 19 : CPLAssert(y == yEnd);
472 19 : CPLAssert(band == bandEnd);
473 19 : CPL_IGNORE_RET_VAL(GDALRasterIO(
474 4 : hBand ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag,
475 19 : nXOff + x, nYOff + y, xEnd - x, 1,
476 19 : static_cast<char *>(pPage) + nOffsetShift, xEnd - x, 1, eBufType,
477 19 : nPixelSpace, static_cast<spacing_type>(nLineSpace)));
478 :
479 19 : return;
480 : }
481 :
482 : // Yes, enough place to read/write until end of line
483 39 : if (x > 0 || nBytes - nOffsetShift < static_cast<size_t>(nLineSpace))
484 : {
485 19 : CPL_IGNORE_RET_VAL(GDALRasterIO(
486 4 : hBand ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag,
487 19 : nXOff + x, nYOff + y, nBufXSize - x, 1,
488 19 : static_cast<char *>(pPage) + nOffsetShift, nBufXSize - x, 1,
489 19 : eBufType, nPixelSpace, static_cast<spacing_type>(nLineSpace)));
490 :
491 : // Go to beginning of next line
492 19 : x = nBufXSize - 1;
493 19 : if (!GotoNextPixel(x, y, band))
494 0 : return;
495 19 : nOffsetRecompute = GetOffset(x, y, band);
496 19 : nOffsetShift = nOffsetRecompute - nOffset;
497 19 : if (nOffsetShift >= nBytes)
498 0 : return;
499 : }
500 :
501 : // How many whole lines can we store/load ?
502 39 : coord_type nLineCount =
503 39 : static_cast<coord_type>((nBytes - nOffsetShift) / nLineSpace);
504 39 : if (y + nLineCount > nBufYSize)
505 9 : nLineCount = nBufYSize - y;
506 39 : if (nLineCount > 0)
507 : {
508 39 : CPL_IGNORE_RET_VAL(GDALRasterIO(
509 9 : hBand ? hBand : GDALGetRasterBand(hDS, panBandMap[band]), eRWFlag,
510 39 : nXOff + 0, nYOff + y, nBufXSize, nLineCount,
511 39 : static_cast<GByte *>(pPage) + nOffsetShift, nBufXSize, nLineCount,
512 39 : eBufType, nPixelSpace, static_cast<spacing_type>(nLineSpace)));
513 :
514 39 : y += nLineCount;
515 39 : if (y == nBufYSize)
516 : {
517 20 : y = 0;
518 20 : band++;
519 20 : if (band == nBandCount)
520 18 : return;
521 : }
522 21 : nOffsetRecompute = GetOffset(x, y, band);
523 21 : nOffsetShift = nOffsetRecompute - nOffset;
524 : }
525 :
526 21 : if (nOffsetShift < nBytes)
527 : {
528 21 : DoIOBandSequential(eRWFlag, nOffsetRecompute,
529 : static_cast<char *>(pPage) + nOffsetShift,
530 : nBytes - nOffsetShift);
531 : }
532 : }
533 :
534 : /************************************************************************/
535 : /* FillCacheBandSequential() */
536 : /************************************************************************/
537 :
538 24 : void GDALVirtualMem::FillCacheBandSequential(CPLVirtualMem *, size_t nOffset,
539 : void *pPageToFill, size_t nToFill,
540 : void *pUserData)
541 : {
542 24 : const GDALVirtualMem *psParams = static_cast<GDALVirtualMem *>(pUserData);
543 24 : psParams->DoIOBandSequential(GF_Read, nOffset, pPageToFill, nToFill);
544 24 : }
545 :
546 : /************************************************************************/
547 : /* SaveFromCacheBandSequential() */
548 : /************************************************************************/
549 :
550 13 : void GDALVirtualMem::SaveFromCacheBandSequential(CPLVirtualMem *,
551 : size_t nOffset,
552 : const void *pPageToBeEvicted,
553 : size_t nToEvicted,
554 : void *pUserData)
555 : {
556 13 : const GDALVirtualMem *psParams = static_cast<GDALVirtualMem *>(pUserData);
557 13 : psParams->DoIOBandSequential(
558 : GF_Write, nOffset, const_cast<void *>(pPageToBeEvicted), nToEvicted);
559 13 : }
560 :
561 : /************************************************************************/
562 : /* FillCachePixelInterleaved() */
563 : /************************************************************************/
564 :
565 5 : void GDALVirtualMem::FillCachePixelInterleaved(CPLVirtualMem *, size_t nOffset,
566 : void *pPageToFill,
567 : size_t nToFill, void *pUserData)
568 : {
569 5 : const GDALVirtualMem *psParams = static_cast<GDALVirtualMem *>(pUserData);
570 5 : psParams->DoIOPixelInterleaved(GF_Read, nOffset, pPageToFill, nToFill);
571 5 : }
572 :
573 : /************************************************************************/
574 : /* SaveFromCachePixelInterleaved() */
575 : /************************************************************************/
576 :
577 0 : void GDALVirtualMem::SaveFromCachePixelInterleaved(CPLVirtualMem *,
578 : size_t nOffset,
579 : const void *pPageToBeEvicted,
580 : size_t nToEvicted,
581 : void *pUserData)
582 : {
583 0 : const GDALVirtualMem *psParams = static_cast<GDALVirtualMem *>(pUserData);
584 0 : psParams->DoIOPixelInterleaved(
585 : GF_Write, nOffset, const_cast<void *>(pPageToBeEvicted), nToEvicted);
586 0 : }
587 :
588 : /************************************************************************/
589 : /* Destroy() */
590 : /************************************************************************/
591 :
592 12 : void GDALVirtualMem::Destroy(void *pUserData)
593 : {
594 12 : GDALVirtualMem *psParams = static_cast<GDALVirtualMem *>(pUserData);
595 12 : delete psParams;
596 12 : }
597 :
598 : /************************************************************************/
599 : /* GDALCheckBandParameters() */
600 : /************************************************************************/
601 :
602 6 : static bool GDALCheckBandParameters(GDALDatasetH hDS, int nBandCount,
603 : int *panBandMap)
604 : {
605 6 : if (nBandCount == 0)
606 : {
607 0 : CPLError(CE_Failure, CPLE_AppDefined, "nBandCount == 0");
608 0 : return false;
609 : }
610 :
611 6 : if (panBandMap != nullptr)
612 : {
613 22 : for (int i = 0; i < nBandCount; i++)
614 : {
615 16 : if (panBandMap[i] < 1 || panBandMap[i] > GDALGetRasterCount(hDS))
616 : {
617 0 : CPLError(CE_Failure, CPLE_AppDefined, "panBandMap[%d]=%d", i,
618 0 : panBandMap[i]);
619 0 : return false;
620 : }
621 : }
622 : }
623 0 : else if (nBandCount > GDALGetRasterCount(hDS))
624 : {
625 0 : CPLError(CE_Failure, CPLE_AppDefined,
626 : "nBandCount > GDALGetRasterCount(hDS)");
627 0 : return false;
628 : }
629 6 : return true;
630 : }
631 :
632 : /************************************************************************/
633 : /* GDALGetVirtualMem() */
634 : /************************************************************************/
635 :
636 : static CPLVirtualMem *
637 12 : GDALGetVirtualMem(GDALDatasetH hDS, GDALRasterBandH hBand, GDALRWFlag eRWFlag,
638 : coord_type nXOff, coord_type nYOff, coord_type nXSize,
639 : coord_type nYSize, coord_type nBufXSize, coord_type nBufYSize,
640 : GDALDataType eBufType, int nBandCount, int *panBandMap,
641 : int nPixelSpace, GIntBig nLineSpace, GIntBig nBandSpace,
642 : size_t nCacheSize, size_t nPageSizeHint,
643 : int bSingleThreadUsage, CSLConstList /*papszOptions*/)
644 : {
645 12 : CPLVirtualMem *view = nullptr;
646 12 : GDALVirtualMem *psParams = nullptr;
647 12 : GUIntBig nReqMem = 0;
648 :
649 12 : if (nXSize != nBufXSize || nYSize != nBufYSize)
650 : {
651 0 : CPLError(CE_Failure, CPLE_NotSupported,
652 : "nXSize != nBufXSize || nYSize != nBufYSize");
653 0 : return nullptr;
654 : }
655 :
656 : int nRasterXSize =
657 12 : hDS ? GDALGetRasterXSize(hDS) : GDALGetRasterBandXSize(hBand);
658 : int nRasterYSize =
659 12 : hDS ? GDALGetRasterYSize(hDS) : GDALGetRasterBandYSize(hBand);
660 :
661 12 : if (nXOff < 0 || nYOff < 0 || nXSize == 0 || nYSize == 0 || nBufXSize < 0 ||
662 12 : nBufYSize < 0 || nXOff + nXSize > nRasterXSize ||
663 12 : nYOff + nYSize > nRasterYSize)
664 : {
665 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid window request");
666 0 : return nullptr;
667 : }
668 :
669 12 : if (nPixelSpace < 0 || nLineSpace < 0 || nBandSpace < 0)
670 : {
671 0 : CPLError(CE_Failure, CPLE_NotSupported,
672 : "nPixelSpace < 0 || nLineSpace < 0 || nBandSpace < 0");
673 0 : return nullptr;
674 : }
675 :
676 12 : if (hDS != nullptr && !GDALCheckBandParameters(hDS, nBandCount, panBandMap))
677 0 : return nullptr;
678 :
679 12 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
680 12 : if (nPixelSpace == 0)
681 3 : nPixelSpace = nDataTypeSize;
682 12 : if (nLineSpace == 0)
683 4 : nLineSpace = static_cast<GIntBig>(nBufXSize) * nPixelSpace;
684 12 : if (nBandSpace == 0)
685 11 : nBandSpace = static_cast<GIntBig>(nBufYSize) * nLineSpace;
686 :
687 : // OFFSET = offset(x,y,band) = x * nPixelSpace + y * nLineSpace + band *
688 : // nBandSpace where 0 <= x < nBufXSize and 0 <= y < nBufYSize and 0 <= band
689 : // < nBandCount if nPixelSpace, nLineSpace and nBandSpace can have arbitrary
690 : // values, there is no way of finding a unique(x,y,band) solution. We need
691 : // to restrict the space of possibilities strongly.
692 : // if nBandSpace >= nBufYSize * nLineSpace and
693 : // nLineSpace >= nBufXSize * nPixelSpace, INTERLEAVE = BAND
694 : // band = OFFSET / nBandSpace
695 : // y = (OFFSET - band * nBandSpace) / nLineSpace
696 : // x = (OFFSET - band * nBandSpace - y * nLineSpace) / nPixelSpace
697 : // else if nPixelSpace >= nBandCount * nBandSpace and
698 : // nLineSpace >= nBufXSize * nPixelSpace, INTERLEAVE = PIXEL
699 : // y = OFFSET / nLineSpace
700 : // x = (OFFSET - y * nLineSpace) / nPixelSpace
701 : // band = (OFFSET - y * nLineSpace - x * nPixelSpace) / nBandSpace
702 :
703 12 : if (nDataTypeSize == 0 || /* to please Coverity. not needed */
704 12 : nLineSpace < static_cast<GIntBig>(nBufXSize) * nPixelSpace ||
705 2 : (nBandCount > 1 &&
706 2 : (nBandSpace == nPixelSpace ||
707 2 : (nBandSpace < nPixelSpace &&
708 1 : (nBandSpace < nDataTypeSize ||
709 1 : nPixelSpace < nBandCount * nBandSpace)) ||
710 2 : (nBandSpace > nPixelSpace && (nPixelSpace < nDataTypeSize ||
711 1 : nBandSpace < nBufYSize * nLineSpace)))))
712 : {
713 0 : CPLError(CE_Failure, CPLE_NotSupported,
714 : "Only pixel interleaving or band interleaving are supported");
715 0 : return nullptr;
716 : }
717 :
718 : /* Avoid odd spacings that would complicate I/O operations */
719 : /* Ensuring they are multiple of nDataTypeSize should be fine, because */
720 : /* the page size is a power of 2 that is also a multiple of nDataTypeSize */
721 12 : if ((nPixelSpace % nDataTypeSize) != 0 ||
722 12 : (nLineSpace % nDataTypeSize) != 0 || (nBandSpace % nDataTypeSize) != 0)
723 : {
724 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported spacing");
725 0 : return nullptr;
726 : }
727 :
728 12 : bool bIsBandSequential = nBandSpace >= nBufYSize * nLineSpace;
729 12 : if (bIsBandSequential)
730 11 : nReqMem = nBandCount * nBandSpace;
731 : else
732 1 : nReqMem = nBufYSize * nLineSpace;
733 : if (nReqMem != static_cast<GUIntBig>(static_cast<size_t>(nReqMem)))
734 : {
735 : CPLError(CE_Failure, CPLE_OutOfMemory,
736 : "Cannot reserve " CPL_FRMT_GUIB " bytes", nReqMem);
737 : return nullptr;
738 : }
739 :
740 12 : psParams = new GDALVirtualMem(
741 : hDS, hBand, nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
742 12 : eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace);
743 :
744 12 : view = CPLVirtualMemNew(
745 : static_cast<size_t>(nReqMem), nCacheSize, nPageSizeHint,
746 : bSingleThreadUsage,
747 : eRWFlag == GF_Read ? VIRTUALMEM_READONLY_ENFORCED
748 : : VIRTUALMEM_READWRITE,
749 : bIsBandSequential ? GDALVirtualMem::FillCacheBandSequential
750 : : GDALVirtualMem::FillCachePixelInterleaved,
751 : bIsBandSequential ? GDALVirtualMem::SaveFromCacheBandSequential
752 : : GDALVirtualMem::SaveFromCachePixelInterleaved,
753 : GDALVirtualMem::Destroy, psParams);
754 :
755 12 : if (view == nullptr)
756 : {
757 0 : delete psParams;
758 : }
759 :
760 12 : return view;
761 : }
762 :
763 : /************************************************************************/
764 : /* GDALDatasetGetVirtualMem() */
765 : /************************************************************************/
766 :
767 : /** Create a CPLVirtualMem object from a GDAL dataset object.
768 : *
769 : * Only supported on Linux for now.
770 : *
771 : * This method allows creating a virtual memory object for a region of one
772 : * or more GDALRasterBands from this dataset. The content of the virtual
773 : * memory object is automatically filled from dataset content when a virtual
774 : * memory page is first accessed, and it is released (or flushed in case of a
775 : * "dirty" page) when the cache size limit has been reached.
776 : *
777 : * The pointer to access the virtual memory object is obtained with
778 : * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called.
779 : * CPLVirtualMemFree() must be called before the dataset object is destroyed.
780 : *
781 : * If p is such a pointer and base_type the C type matching eBufType, for
782 : * default values of spacing parameters, the element of image coordinates (x, y)
783 : * (relative to xOff, yOff) for band b can be accessed with
784 : * ((base_type*)p)[x + y * nBufXSize + (b-1)*nBufXSize*nBufYSize].
785 : *
786 : * Note that the mechanism used to transparently fill memory pages when they are
787 : * accessed is the same (but in a controlled way) than what occurs when a memory
788 : * error occurs in a program. Debugging software will generally interrupt
789 : * program execution when that happens. If needed, CPLVirtualMemPin() can be
790 : * used to avoid that by ensuring memory pages are allocated before being
791 : * accessed.
792 : *
793 : * The size of the region that can be mapped as a virtual memory object depends
794 : * on hardware and operating system limitations.
795 : * On Linux AMD64 platforms, the maximum value is 128 TB.
796 : * On Linux x86 platforms, the maximum value is 2 GB.
797 : *
798 : * Data type translation is automatically done if the data type
799 : * (eBufType) of the buffer is different than
800 : * that of the GDALRasterBand.
801 : *
802 : * Image decimation / replication is currently not supported, i.e. if the
803 : * size of the region being accessed (nXSize x nYSize) is different from the
804 : * buffer size (nBufXSize x nBufYSize).
805 : *
806 : * The nPixelSpace, nLineSpace and nBandSpace parameters allow reading into or
807 : * writing from various organization of buffers. Arbitrary values for the
808 : * spacing parameters are not supported. Those values must be multiple of the
809 : * size of thebuffer data type, and must be either band sequential
810 : * organization (typically nPixelSpace = GDALGetDataTypeSizeBytes(eBufType),
811 : * nLineSpace = nPixelSpace * nBufXSize,
812 : * nBandSpace = nLineSpace * nBufYSize), or pixel-interleaved organization
813 : * (typically nPixelSpace = nBandSpace * nBandCount,
814 : * nLineSpace = nPixelSpace * nBufXSize,
815 : * nBandSpace = GDALGetDataTypeSizeBytes(eBufType))
816 : *
817 : * @param hDS Dataset object
818 : *
819 : * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to
820 : * write a region of data.
821 : *
822 : * @param nXOff The pixel offset to the top left corner of the region
823 : * of the band to be accessed. This would be zero to start from the left side.
824 : *
825 : * @param nYOff The line offset to the top left corner of the region
826 : * of the band to be accessed. This would be zero to start from the top.
827 : *
828 : * @param nXSize The width of the region of the band to be accessed in pixels.
829 : *
830 : * @param nYSize The height of the region of the band to be accessed in lines.
831 : *
832 : * @param nBufXSize the width of the buffer image into which the desired region
833 : * is to be read, or from which it is to be written.
834 : *
835 : * @param nBufYSize the height of the buffer image into which the desired
836 : * region is to be read, or from which it is to be written.
837 : *
838 : * @param eBufType the type of the pixel values in the data buffer. The
839 : * pixel values will automatically be translated to/from the GDALRasterBand
840 : * data type as needed.
841 : *
842 : * @param nBandCount the number of bands being read or written.
843 : *
844 : * @param panBandMap the list of nBandCount band numbers being read/written.
845 : * Note band numbers are 1 based. This may be NULL to select the first
846 : * nBandCount bands.
847 : *
848 : * @param nPixelSpace The byte offset from the start of one pixel value in
849 : * the buffer to the start of the next pixel value within a scanline. If
850 : * defaulted (0) the size of the datatype eBufType is used.
851 : *
852 : * @param nLineSpace The byte offset from the start of one scanline in
853 : * the buffer to the start of the next. If defaulted (0) the size of the
854 : * datatype eBufType * nBufXSize is used.
855 : *
856 : * @param nBandSpace the byte offset from the start of one bands data to the
857 : * start of the next. If defaulted (0) the value will be
858 : * nLineSpace * nBufYSize implying band sequential organization
859 : * of the data buffer.
860 : *
861 : * @param nCacheSize size in bytes of the maximum memory that will be really
862 : * allocated (must ideally fit into RAM)
863 : *
864 : * @param nPageSizeHint hint for the page size. Must be a multiple of the
865 : * system page size, returned by CPLGetPageSize().
866 : * Minimum value is generally 4096. Might be set to 0 to
867 : * let the function determine a default page size.
868 : *
869 : * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads
870 : * that will access the virtual memory mapping. This
871 : * can optimize performance a bit. If set to FALSE,
872 : * CPLVirtualMemDeclareThread() must be called.
873 : *
874 : * @param papszOptions NULL terminated list of options. Unused for now.
875 : *
876 : * @return a virtual memory object that must be freed by CPLVirtualMemFree(),
877 : * or NULL in case of failure.
878 : *
879 : */
880 :
881 3 : CPLVirtualMem *GDALDatasetGetVirtualMem(
882 : GDALDatasetH hDS, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
883 : int nYSize, int nBufXSize, int nBufYSize, GDALDataType eBufType,
884 : int nBandCount, int *panBandMap, int nPixelSpace, GIntBig nLineSpace,
885 : GIntBig nBandSpace, size_t nCacheSize, size_t nPageSizeHint,
886 : int bSingleThreadUsage, CSLConstList papszOptions)
887 : {
888 3 : return GDALGetVirtualMem(hDS, nullptr, eRWFlag, nXOff, nYOff, nXSize,
889 : nYSize, nBufXSize, nBufYSize, eBufType, nBandCount,
890 : panBandMap, nPixelSpace, nLineSpace, nBandSpace,
891 : nCacheSize, nPageSizeHint, bSingleThreadUsage,
892 3 : papszOptions);
893 : }
894 :
895 : /************************************************************************/
896 : /* GDALRasterBandGetVirtualMem() */
897 : /************************************************************************/
898 :
899 : /** Create a CPLVirtualMem object from a GDAL raster band object.
900 : *
901 : * Only supported on Linux for now.
902 : *
903 : * This method allows creating a virtual memory object for a region of a
904 : * GDALRasterBand. The content of the virtual
905 : * memory object is automatically filled from dataset content when a virtual
906 : * memory page is first accessed, and it is released (or flushed in case of a
907 : * "dirty" page) when the cache size limit has been reached.
908 : *
909 : * The pointer to access the virtual memory object is obtained with
910 : * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called.
911 : * CPLVirtualMemFree() must be called before the raster band object is
912 : * destroyed.
913 : *
914 : * If p is such a pointer and base_type the C type matching eBufType, for
915 : * values of spacing parameters, the element of image coordinates (x, y)
916 : * default (relative to xOff, yOff) can be accessed with
917 : * ((base_type*)p)[x + y * nBufXSize].
918 : *
919 : * Note that the mechanism used to transparently fill memory pages when they are
920 : * accessed is the same (but in a controlled way) than what occurs when a memory
921 : * error occurs in a program. Debugging software will generally interrupt
922 : * program execution when that happens. If needed, CPLVirtualMemPin() can be
923 : * used to avoid that by ensuring memory pages are allocated before being
924 : * accessed.
925 : *
926 : * The size of the region that can be mapped as a virtual memory object depends
927 : * on hardware and operating system limitations.
928 : * On Linux AMD64 platforms, the maximum value is 128 TB.
929 : * On Linux x86 platforms, the maximum value is 2 GB.
930 : *
931 : * Data type translation is automatically done if the data type
932 : * (eBufType) of the buffer is different than
933 : * that of the GDALRasterBand.
934 : *
935 : * Image decimation / replication is currently not supported, i.e. if the
936 : * size of the region being accessed (nXSize x nYSize) is different from the
937 : * buffer size (nBufXSize x nBufYSize).
938 : *
939 : * The nPixelSpace and nLineSpace parameters allow reading into or
940 : * writing from various organization of buffers. Arbitrary values for the
941 : * spacing parameters are not supported. Those values must be multiple of the
942 : * size of the buffer data type and must be such that nLineSpace >=
943 : * nPixelSpace * nBufXSize.
944 : *
945 : * @param hBand Rasterband object
946 : *
947 : * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to
948 : * write a region of data.
949 : *
950 : * @param nXOff The pixel offset to the top left corner of the region
951 : * of the band to be accessed. This would be zero to start from the left side.
952 : *
953 : * @param nYOff The line offset to the top left corner of the region
954 : * of the band to be accessed. This would be zero to start from the top.
955 : *
956 : * @param nXSize The width of the region of the band to be accessed in pixels.
957 : *
958 : * @param nYSize The height of the region of the band to be accessed in lines.
959 : *
960 : * @param nBufXSize the width of the buffer image into which the desired region
961 : * is to be read, or from which it is to be written.
962 : *
963 : * @param nBufYSize the height of the buffer image into which the desired
964 : * region is to be read, or from which it is to be written.
965 : *
966 : * @param eBufType the type of the pixel values in the data buffer. The
967 : * pixel values will automatically be translated to/from the GDALRasterBand
968 : * data type as needed.
969 : *
970 : * @param nPixelSpace The byte offset from the start of one pixel value in the
971 : * buffer to the start of the next pixel value within a scanline. If defaulted
972 : * (0) the size of the datatype eBufType is used.
973 : *
974 : * @param nLineSpace The byte offset from the start of one scanline in the
975 : * buffer to the start of the next. If defaulted (0) the size of the datatype
976 : * eBufType * nBufXSize is used.
977 : *
978 : * @param nCacheSize size in bytes of the maximum memory that will be really
979 : * allocated (must ideally fit into RAM)
980 : *
981 : * @param nPageSizeHint hint for the page size. Must be a multiple of the
982 : * system page size, returned by CPLGetPageSize().
983 : * Minimum value is generally 4096. Might be set to 0 to
984 : * let the function determine a default page size.
985 : *
986 : * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads
987 : * that will access the virtual memory mapping. This
988 : * can optimize performance a bit. If set to FALSE,
989 : * CPLVirtualMemDeclareThread() must be called.
990 : *
991 : * @param papszOptions NULL terminated list of options. Unused for now.
992 : *
993 : * @return a virtual memory object that must be freed by CPLVirtualMemFree(),
994 : * or NULL in case of failure.
995 : *
996 : */
997 :
998 9 : CPLVirtualMem *GDALRasterBandGetVirtualMem(
999 : GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
1000 : int nYSize, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1001 : int nPixelSpace, GIntBig nLineSpace, size_t nCacheSize,
1002 : size_t nPageSizeHint, int bSingleThreadUsage, CSLConstList papszOptions)
1003 : {
1004 9 : return GDALGetVirtualMem(nullptr, hBand, eRWFlag, nXOff, nYOff, nXSize,
1005 : nYSize, nBufXSize, nBufYSize, eBufType, 1, nullptr,
1006 : nPixelSpace, nLineSpace, 0, nCacheSize,
1007 9 : nPageSizeHint, bSingleThreadUsage, papszOptions);
1008 : }
1009 :
1010 : /************************************************************************/
1011 : /* GDALTiledVirtualMem */
1012 : /************************************************************************/
1013 :
1014 : class GDALTiledVirtualMem
1015 : {
1016 : GDALDatasetH hDS = nullptr;
1017 : GDALRasterBandH hBand = nullptr;
1018 : int nXOff = 0;
1019 : int nYOff = 0;
1020 : int nXSize = 0;
1021 : int nYSize = 0;
1022 : int nTileXSize = 0;
1023 : int nTileYSize = 0;
1024 : GDALDataType eBufType = GDT_Byte;
1025 : int nBandCount = 0;
1026 : int *panBandMap = nullptr;
1027 : GDALTileOrganization eTileOrganization = GTO_TIP;
1028 :
1029 : void DoIO(GDALRWFlag eRWFlag, size_t nOffset, void *pPage,
1030 : size_t nBytes) const;
1031 :
1032 : CPL_DISALLOW_COPY_ASSIGN(GDALTiledVirtualMem)
1033 :
1034 : public:
1035 : GDALTiledVirtualMem(GDALDatasetH hDS, GDALRasterBandH hBand, int nXOff,
1036 : int nYOff, int nXSize, int nYSize, int nTileXSize,
1037 : int nTileYSize, GDALDataType eBufType, int nBandCount,
1038 : const int *panBandMapIn,
1039 : GDALTileOrganization eTileOrganization);
1040 : ~GDALTiledVirtualMem();
1041 :
1042 : static void FillCache(CPLVirtualMem *ctxt, size_t nOffset,
1043 : void *pPageToFill, size_t nPageSize, void *pUserData);
1044 : static void SaveFromCache(CPLVirtualMem *ctxt, size_t nOffset,
1045 : const void *pPageToBeEvicted, size_t nToEvicted,
1046 : void *pUserData);
1047 :
1048 : static void Destroy(void *pUserData);
1049 : };
1050 :
1051 : /************************************************************************/
1052 : /* GDALTiledVirtualMem() */
1053 : /************************************************************************/
1054 :
1055 4 : GDALTiledVirtualMem::GDALTiledVirtualMem(
1056 : GDALDatasetH hDSIn, GDALRasterBandH hBandIn, int nXOffIn, int nYOffIn,
1057 : int nXSizeIn, int nYSizeIn, int nTileXSizeIn, int nTileYSizeIn,
1058 : GDALDataType eBufTypeIn, int nBandCountIn, const int *panBandMapIn,
1059 4 : GDALTileOrganization eTileOrganizationIn)
1060 : : hDS(hDSIn), hBand(hBandIn), nXOff(nXOffIn), nYOff(nYOffIn),
1061 : nXSize(nXSizeIn), nYSize(nYSizeIn), nTileXSize(nTileXSizeIn),
1062 : nTileYSize(nTileYSizeIn), eBufType(eBufTypeIn), nBandCount(nBandCountIn),
1063 4 : eTileOrganization(eTileOrganizationIn)
1064 : {
1065 4 : if (hDS != nullptr)
1066 : {
1067 3 : panBandMap = static_cast<int *>(CPLMalloc(nBandCount * sizeof(int)));
1068 3 : if (panBandMapIn)
1069 : {
1070 3 : memcpy(panBandMap, panBandMapIn, nBandCount * sizeof(int));
1071 : }
1072 : else
1073 : {
1074 0 : for (int i = 0; i < nBandCount; i++)
1075 0 : panBandMap[i] = i + 1;
1076 : }
1077 : }
1078 : else
1079 : {
1080 1 : panBandMap = nullptr;
1081 1 : nBandCount = 1;
1082 : }
1083 4 : }
1084 :
1085 : /************************************************************************/
1086 : /* ~GDALTiledVirtualMem() */
1087 : /************************************************************************/
1088 :
1089 8 : GDALTiledVirtualMem::~GDALTiledVirtualMem()
1090 : {
1091 4 : CPLFree(panBandMap);
1092 4 : }
1093 :
1094 : /************************************************************************/
1095 : /* DoIO() */
1096 : /************************************************************************/
1097 :
1098 48 : void GDALTiledVirtualMem::DoIO(GDALRWFlag eRWFlag, size_t nOffset, void *pPage,
1099 : size_t nBytes) const
1100 : {
1101 48 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1102 48 : const int nTilesPerRow = DIV_ROUND_UP(nXSize, nTileXSize);
1103 48 : const int nTilesPerCol = DIV_ROUND_UP(nYSize, nTileYSize);
1104 48 : size_t nPageSize =
1105 48 : static_cast<size_t>(nTileXSize) * nTileYSize * nDataTypeSize;
1106 48 : if (eTileOrganization != GTO_BSQ)
1107 16 : nPageSize *= nBandCount;
1108 48 : CPLAssert((nOffset % nPageSize) == 0);
1109 48 : CPLAssert(nBytes == nPageSize);
1110 48 : size_t nTile = 0;
1111 48 : int band = 0;
1112 48 : int nPixelSpace = 0;
1113 48 : int nLineSpace = 0;
1114 48 : int nBandSpace = 0;
1115 48 : if (eTileOrganization == GTO_TIP)
1116 : {
1117 8 : nTile = nOffset / nPageSize;
1118 8 : band = 0;
1119 8 : nPixelSpace = nDataTypeSize * nBandCount;
1120 8 : nLineSpace = nPixelSpace * nTileXSize;
1121 8 : nBandSpace = nDataTypeSize;
1122 : }
1123 40 : else if (eTileOrganization == GTO_BIT)
1124 : {
1125 8 : nTile = nOffset / nPageSize;
1126 8 : band = 0;
1127 8 : nPixelSpace = nDataTypeSize;
1128 8 : nLineSpace = nPixelSpace * nTileXSize;
1129 8 : nBandSpace = nLineSpace * nTileYSize;
1130 : }
1131 : else
1132 : {
1133 : // offset = nPageSize * (band * nTilesPerRow * nTilesPerCol + nTile)
1134 32 : band = static_cast<int>(nOffset / (static_cast<size_t>(nPageSize) *
1135 32 : nTilesPerRow * nTilesPerCol));
1136 32 : nTile = nOffset / nPageSize -
1137 32 : static_cast<size_t>(band) * nTilesPerRow * nTilesPerCol;
1138 32 : nPixelSpace = nDataTypeSize;
1139 32 : nLineSpace = nPixelSpace * nTileXSize;
1140 32 : nBandSpace = 0;
1141 32 : band++;
1142 : }
1143 48 : size_t nYTile = nTile / nTilesPerRow;
1144 48 : size_t nXTile = nTile - nYTile * nTilesPerRow;
1145 :
1146 : int nReqXSize =
1147 48 : std::min(nTileXSize, nXSize - static_cast<int>(nXTile * nTileXSize));
1148 : int nReqYSize =
1149 48 : std::min(nTileYSize, nYSize - static_cast<int>(nYTile * nTileYSize));
1150 48 : if (eRWFlag == GF_Read &&
1151 48 : (nReqXSize < nTileXSize || nReqYSize < nTileYSize))
1152 12 : memset(pPage, 0, nBytes);
1153 48 : if (hDS != nullptr)
1154 : {
1155 120 : CPL_IGNORE_RET_VAL(GDALDatasetRasterIO(
1156 40 : hDS, eRWFlag, static_cast<int>(nXOff + nXTile * nTileXSize),
1157 40 : static_cast<int>(nYOff + nYTile * nTileYSize), nReqXSize, nReqYSize,
1158 40 : pPage, nReqXSize, nReqYSize, eBufType,
1159 40 : eTileOrganization != GTO_BSQ ? nBandCount : 1,
1160 40 : eTileOrganization != GTO_BSQ ? panBandMap : &band, nPixelSpace,
1161 : nLineSpace, nBandSpace));
1162 : }
1163 : else
1164 : {
1165 8 : CPL_IGNORE_RET_VAL(GDALRasterIO(
1166 8 : hBand, eRWFlag, static_cast<int>(nXOff + nXTile * nTileXSize),
1167 8 : static_cast<int>(nYOff + nYTile * nTileYSize), nReqXSize, nReqYSize,
1168 8 : pPage, nReqXSize, nReqYSize, eBufType, nPixelSpace, nLineSpace));
1169 : }
1170 48 : }
1171 :
1172 : /************************************************************************/
1173 : /* FillCache() */
1174 : /************************************************************************/
1175 :
1176 48 : void GDALTiledVirtualMem::FillCache(CPLVirtualMem *, size_t nOffset,
1177 : void *pPageToFill, size_t nToFill,
1178 : void *pUserData)
1179 : {
1180 48 : const GDALTiledVirtualMem *psParams =
1181 : static_cast<GDALTiledVirtualMem *>(pUserData);
1182 48 : psParams->DoIO(GF_Read, nOffset, pPageToFill, nToFill);
1183 48 : }
1184 :
1185 : /************************************************************************/
1186 : /* SaveFromCache() */
1187 : /************************************************************************/
1188 :
1189 0 : void GDALTiledVirtualMem::SaveFromCache(CPLVirtualMem *, size_t nOffset,
1190 : const void *pPageToBeEvicted,
1191 : size_t nToEvicted, void *pUserData)
1192 : {
1193 0 : const GDALTiledVirtualMem *psParams =
1194 : static_cast<GDALTiledVirtualMem *>(pUserData);
1195 0 : psParams->DoIO(GF_Write, nOffset, const_cast<void *>(pPageToBeEvicted),
1196 : nToEvicted);
1197 0 : }
1198 :
1199 : /************************************************************************/
1200 : /* Destroy() */
1201 : /************************************************************************/
1202 :
1203 4 : void GDALTiledVirtualMem::Destroy(void *pUserData)
1204 : {
1205 4 : GDALTiledVirtualMem *psParams =
1206 : static_cast<GDALTiledVirtualMem *>(pUserData);
1207 4 : delete psParams;
1208 4 : }
1209 :
1210 : /************************************************************************/
1211 : /* GDALGetTiledVirtualMem() */
1212 : /************************************************************************/
1213 :
1214 4 : static CPLVirtualMem *GDALGetTiledVirtualMem(
1215 : GDALDatasetH hDS, GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff,
1216 : int nYOff, int nXSize, int nYSize, int nTileXSize, int nTileYSize,
1217 : GDALDataType eBufType, int nBandCount, int *panBandMap,
1218 : GDALTileOrganization eTileOrganization, size_t nCacheSize,
1219 : int bSingleThreadUsage, CSLConstList /* papszOptions */)
1220 : {
1221 : CPLVirtualMem *view;
1222 : GDALTiledVirtualMem *psParams;
1223 :
1224 4 : size_t nPageSize = CPLGetPageSize();
1225 4 : if (nPageSize == 0)
1226 : {
1227 0 : CPLError(CE_Failure, CPLE_NotSupported,
1228 : "GDALGetTiledVirtualMem() unsupported on this "
1229 : "operating system / configuration");
1230 0 : return nullptr;
1231 : }
1232 :
1233 : int nRasterXSize =
1234 4 : hDS ? GDALGetRasterXSize(hDS) : GDALGetRasterBandXSize(hBand);
1235 : int nRasterYSize =
1236 4 : hDS ? GDALGetRasterYSize(hDS) : GDALGetRasterBandYSize(hBand);
1237 :
1238 4 : if (nXOff < 0 || nYOff < 0 || nTileXSize <= 0 || nTileYSize <= 0 ||
1239 4 : nXOff + nXSize > nRasterXSize || nYOff + nYSize > nRasterYSize)
1240 : {
1241 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid window request");
1242 0 : return nullptr;
1243 : }
1244 :
1245 4 : if (hDS != nullptr && !GDALCheckBandParameters(hDS, nBandCount, panBandMap))
1246 0 : return nullptr;
1247 :
1248 4 : const int nDataTypeSize = GDALGetDataTypeSizeBytes(eBufType);
1249 4 : int nTilesPerRow = DIV_ROUND_UP(nXSize, nTileXSize);
1250 4 : int nTilesPerCol = DIV_ROUND_UP(nYSize, nTileYSize);
1251 4 : GUIntBig nReqMem = static_cast<GUIntBig>(nTilesPerRow) * nTilesPerCol *
1252 4 : nTileXSize * nTileYSize * nBandCount * nDataTypeSize;
1253 : #if SIZEOF_SIZE_T == 4
1254 : if (nReqMem != static_cast<GUIntBig>(static_cast<size_t>(nReqMem)))
1255 : {
1256 : CPLError(CE_Failure, CPLE_OutOfMemory,
1257 : "Cannot reserve " CPL_FRMT_GUIB " bytes", nReqMem);
1258 : return nullptr;
1259 : }
1260 : #endif
1261 :
1262 4 : size_t nPageSizeHint =
1263 4 : static_cast<size_t>(nTileXSize) * nTileYSize * nDataTypeSize;
1264 4 : if (eTileOrganization != GTO_BSQ)
1265 2 : nPageSizeHint *= nBandCount;
1266 4 : if ((nPageSizeHint % nPageSize) != 0)
1267 : {
1268 0 : CPLError(CE_Failure, CPLE_AppDefined,
1269 : "Tile dimensions incompatible with page size");
1270 0 : return nullptr;
1271 : }
1272 :
1273 4 : psParams = new GDALTiledVirtualMem(
1274 : hDS, hBand, nXOff, nYOff, nXSize, nYSize, nTileXSize, nTileYSize,
1275 4 : eBufType, nBandCount, panBandMap, eTileOrganization);
1276 :
1277 4 : view = CPLVirtualMemNew(static_cast<size_t>(nReqMem), nCacheSize,
1278 : nPageSizeHint, bSingleThreadUsage,
1279 : eRWFlag == GF_Read ? VIRTUALMEM_READONLY_ENFORCED
1280 : : VIRTUALMEM_READWRITE,
1281 : GDALTiledVirtualMem::FillCache,
1282 : GDALTiledVirtualMem::SaveFromCache,
1283 : GDALTiledVirtualMem::Destroy, psParams);
1284 :
1285 4 : if (view == nullptr)
1286 : {
1287 0 : delete psParams;
1288 : }
1289 4 : else if (CPLVirtualMemGetPageSize(view) != nPageSizeHint)
1290 : {
1291 0 : CPLError(CE_Failure, CPLE_AppDefined,
1292 : "Did not get expected page size : %d vs %d",
1293 0 : static_cast<int>(CPLVirtualMemGetPageSize(view)),
1294 : static_cast<int>(nPageSizeHint));
1295 0 : CPLVirtualMemFree(view);
1296 0 : return nullptr;
1297 : }
1298 :
1299 4 : return view;
1300 : }
1301 :
1302 : /************************************************************************/
1303 : /* GDALDatasetGetTiledVirtualMem() */
1304 : /************************************************************************/
1305 :
1306 : /** Create a CPLVirtualMem object from a GDAL dataset object, with tiling
1307 : * organization
1308 : *
1309 : * Only supported on Linux for now.
1310 : *
1311 : * This method allows creating a virtual memory object for a region of one
1312 : * or more GDALRasterBands from this dataset. The content of the virtual
1313 : * memory object is automatically filled from dataset content when a virtual
1314 : * memory page is first accessed, and it is released (or flushed in case of a
1315 : * "dirty" page) when the cache size limit has been reached.
1316 : *
1317 : * Contrary to GDALDatasetGetVirtualMem(), pixels will be organized by tiles
1318 : * instead of scanlines. Different ways of organizing pixel within/across tiles
1319 : * can be selected with the eTileOrganization parameter.
1320 : *
1321 : * If nXSize is not a multiple of nTileXSize or nYSize is not a multiple of
1322 : * nTileYSize, partial tiles will exists at the right and/or bottom of the
1323 : * region of interest. Those partial tiles will also have nTileXSize *
1324 : * nTileYSize dimension, with padding pixels.
1325 : *
1326 : * The pointer to access the virtual memory object is obtained with
1327 : * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called.
1328 : * CPLVirtualMemFree() must be called before the dataset object is destroyed.
1329 : *
1330 : * If p is such a pointer and base_type the C type matching eBufType, for
1331 : * default values of spacing parameters, the element of image coordinates (x, y)
1332 : * (relative to xOff, yOff) for band b can be accessed with:
1333 : * - for eTileOrganization = GTO_TIP,
1334 : * ((base_type*)p)[tile_number(x,y)*nBandCount*tile_size +
1335 : * offset_in_tile(x,y)*nBandCount + (b-1)].
1336 : * - for eTileOrganization = GTO_BIT,
1337 : * ((base_type*)p)[(tile_number(x,y)*nBandCount +
1338 : * (b-1)) * tile_size + offset_in_tile(x,y)].
1339 : * - for eTileOrganization = GTO_BSQ,
1340 : * ((base_type*)p)[(tile_number(x,y) +
1341 : * (b-1)*nTilesCount) * tile_size + offset_in_tile(x,y)].
1342 : *
1343 : * where nTilesPerRow = ceil(nXSize / nTileXSize)
1344 : * nTilesPerCol = ceil(nYSize / nTileYSize)
1345 : * nTilesCount = nTilesPerRow * nTilesPerCol
1346 : * tile_number(x,y) = (y / nTileYSize) * nTilesPerRow + (x / nTileXSize)
1347 : * offset_in_tile(x,y) = (y % nTileYSize) * nTileXSize + (x % nTileXSize)
1348 : * tile_size = nTileXSize * nTileYSize
1349 : *
1350 : * Note that for a single band request, all tile organizations are equivalent.
1351 : *
1352 : * Note that the mechanism used to transparently fill memory pages when they are
1353 : * accessed is the same (but in a controlled way) than what occurs when a memory
1354 : * error occurs in a program. Debugging software will generally interrupt
1355 : * program execution when that happens. If needed, CPLVirtualMemPin() can be
1356 : * used to avoid that by ensuring memory pages are allocated before being
1357 : * accessed.
1358 : *
1359 : * The size of the region that can be mapped as a virtual memory object depends
1360 : * on hardware and operating system limitations.
1361 : * On Linux AMD64 platforms, the maximum value is 128 TB.
1362 : * On Linux x86 platforms, the maximum value is 2 GB.
1363 : *
1364 : * Data type translation is automatically done if the data type
1365 : * (eBufType) of the buffer is different than
1366 : * that of the GDALRasterBand.
1367 : *
1368 : * @param hDS Dataset object
1369 : *
1370 : * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to
1371 : * write a region of data.
1372 : *
1373 : * @param nXOff The pixel offset to the top left corner of the region
1374 : * of the band to be accessed. This would be zero to start from the left side.
1375 : *
1376 : * @param nYOff The line offset to the top left corner of the region
1377 : * of the band to be accessed. This would be zero to start from the top.
1378 : *
1379 : * @param nXSize The width of the region of the band to be accessed in pixels.
1380 : *
1381 : * @param nYSize The height of the region of the band to be accessed in lines.
1382 : *
1383 : * @param nTileXSize the width of the tiles.
1384 : *
1385 : * @param nTileYSize the height of the tiles.
1386 : *
1387 : * @param eBufType the type of the pixel values in the data buffer. The
1388 : * pixel values will automatically be translated to/from the GDALRasterBand
1389 : * data type as needed.
1390 : *
1391 : * @param nBandCount the number of bands being read or written.
1392 : *
1393 : * @param panBandMap the list of nBandCount band numbers being read/written.
1394 : * Note band numbers are 1 based. This may be NULL to select the first
1395 : * nBandCount bands.
1396 : *
1397 : * @param eTileOrganization tile organization.
1398 : *
1399 : * @param nCacheSize size in bytes of the maximum memory that will be really
1400 : * allocated (must ideally fit into RAM)
1401 : *
1402 : * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads
1403 : * that will access the virtual memory mapping. This
1404 : * can optimize performance a bit. If set to FALSE,
1405 : * CPLVirtualMemDeclareThread() must be called.
1406 : *
1407 : * @param papszOptions NULL terminated list of options. Unused for now.
1408 : *
1409 : * @return a virtual memory object that must be freed by CPLVirtualMemFree(),
1410 : * or NULL in case of failure.
1411 : *
1412 : */
1413 :
1414 3 : CPLVirtualMem *GDALDatasetGetTiledVirtualMem(
1415 : GDALDatasetH hDS, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
1416 : int nYSize, int nTileXSize, int nTileYSize, GDALDataType eBufType,
1417 : int nBandCount, int *panBandMap, GDALTileOrganization eTileOrganization,
1418 : size_t nCacheSize, int bSingleThreadUsage, CSLConstList papszOptions)
1419 : {
1420 3 : return GDALGetTiledVirtualMem(hDS, nullptr, eRWFlag, nXOff, nYOff, nXSize,
1421 : nYSize, nTileXSize, nTileYSize, eBufType,
1422 : nBandCount, panBandMap, eTileOrganization,
1423 3 : nCacheSize, bSingleThreadUsage, papszOptions);
1424 : }
1425 :
1426 : /************************************************************************/
1427 : /* GDALRasterBandGetTiledVirtualMem() */
1428 : /************************************************************************/
1429 :
1430 : /** Create a CPLVirtualMem object from a GDAL rasterband object, with tiling
1431 : * organization
1432 : *
1433 : * Only supported on Linux for now.
1434 : *
1435 : * This method allows creating a virtual memory object for a region of one
1436 : * GDALRasterBand. The content of the virtual
1437 : * memory object is automatically filled from dataset content when a virtual
1438 : * memory page is first accessed, and it is released (or flushed in case of a
1439 : * "dirty" page) when the cache size limit has been reached.
1440 : *
1441 : * Contrary to GDALDatasetGetVirtualMem(), pixels will be organized by tiles
1442 : * instead of scanlines.
1443 : *
1444 : * If nXSize is not a multiple of nTileXSize or nYSize is not a multiple of
1445 : * nTileYSize, partial tiles will exists at the right and/or bottom of the
1446 : * region of interest. Those partial tiles will also have nTileXSize *
1447 : * nTileYSize dimension, with padding pixels.
1448 : *
1449 : * The pointer to access the virtual memory object is obtained with
1450 : * CPLVirtualMemGetAddr(). It remains valid until CPLVirtualMemFree() is called.
1451 : * CPLVirtualMemFree() must be called before the raster band object is
1452 : * destroyed.
1453 : *
1454 : * If p is such a pointer and base_type the C type matching eBufType, for
1455 : * default values of spacing parameters, the element of image coordinates (x, y)
1456 : * (relative to xOff, yOff) can be accessed with:
1457 : * ((base_type*)p)[tile_number(x,y)*tile_size + offset_in_tile(x,y)].
1458 : *
1459 : * where nTilesPerRow = ceil(nXSize / nTileXSize)
1460 : * nTilesCount = nTilesPerRow * nTilesPerCol
1461 : * tile_number(x,y) = (y / nTileYSize) * nTilesPerRow + (x / nTileXSize)
1462 : * offset_in_tile(x,y) = (y % nTileYSize) * nTileXSize + (x % nTileXSize)
1463 : * tile_size = nTileXSize * nTileYSize
1464 : *
1465 : * Note that the mechanism used to transparently fill memory pages when they are
1466 : * accessed is the same (but in a controlled way) than what occurs when a memory
1467 : * error occurs in a program. Debugging software will generally interrupt
1468 : * program execution when that happens. If needed, CPLVirtualMemPin() can be
1469 : * used to avoid that by ensuring memory pages are allocated before being
1470 : * accessed.
1471 : *
1472 : * The size of the region that can be mapped as a virtual memory object depends
1473 : * on hardware and operating system limitations.
1474 : * On Linux AMD64 platforms, the maximum value is 128 TB.
1475 : * On Linux x86 platforms, the maximum value is 2 GB.
1476 : *
1477 : * Data type translation is automatically done if the data type
1478 : * (eBufType) of the buffer is different than
1479 : * that of the GDALRasterBand.
1480 : *
1481 : * @param hBand Rasterband object
1482 : *
1483 : * @param eRWFlag Either GF_Read to read a region of data, or GF_Write to
1484 : * write a region of data.
1485 : *
1486 : * @param nXOff The pixel offset to the top left corner of the region
1487 : * of the band to be accessed. This would be zero to start from the left side.
1488 : *
1489 : * @param nYOff The line offset to the top left corner of the region
1490 : * of the band to be accessed. This would be zero to start from the top.
1491 : *
1492 : * @param nXSize The width of the region of the band to be accessed in pixels.
1493 : *
1494 : * @param nYSize The height of the region of the band to be accessed in lines.
1495 : *
1496 : * @param nTileXSize the width of the tiles.
1497 : *
1498 : * @param nTileYSize the height of the tiles.
1499 : *
1500 : * @param eBufType the type of the pixel values in the data buffer. The
1501 : * pixel values will automatically be translated to/from the GDALRasterBand
1502 : * data type as needed.
1503 : *
1504 : * @param nCacheSize size in bytes of the maximum memory that will be really
1505 : * allocated (must ideally fit into RAM)
1506 : *
1507 : * @param bSingleThreadUsage set to TRUE if there will be no concurrent threads
1508 : * that will access the virtual memory mapping. This
1509 : * can optimize performance a bit. If set to FALSE,
1510 : * CPLVirtualMemDeclareThread() must be called.
1511 : *
1512 : * @param papszOptions NULL terminated list of options. Unused for now.
1513 : *
1514 : * @return a virtual memory object that must be freed by CPLVirtualMemFree(),
1515 : * or NULL in case of failure.
1516 : *
1517 : */
1518 :
1519 1 : CPLVirtualMem *GDALRasterBandGetTiledVirtualMem(
1520 : GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
1521 : int nYSize, int nTileXSize, int nTileYSize, GDALDataType eBufType,
1522 : size_t nCacheSize, int bSingleThreadUsage, CSLConstList papszOptions)
1523 : {
1524 1 : return GDALGetTiledVirtualMem(nullptr, hBand, eRWFlag, nXOff, nYOff, nXSize,
1525 : nYSize, nTileXSize, nTileYSize, eBufType, 1,
1526 : nullptr, GTO_BSQ, nCacheSize,
1527 1 : bSingleThreadUsage, papszOptions);
1528 : }
|