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