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