1 /******************************************************************************
2  *
3  * Purpose:  Block directory API.
4  *
5  ******************************************************************************
6  * Copyright (c) 2011
7  * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included
17  * in all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25  * DEALINGS IN THE SOFTWARE.
26  ****************************************************************************/
27 
28 #include "blockdir/blocktilelayer.h"
29 #include "blockdir/blockdir.h"
30 #include "blockdir/blockfile.h"
31 #include "blockdir/asciitiledir.h"
32 #include "blockdir/binarytiledir.h"
33 #include "core/mutexholder.h"
34 #include "pcidsk_types.h"
35 #include "pcidsk_exception.h"
36 #include <cstdlib>
37 #include <cstring>
38 #include <cassert>
39 #include <algorithm>
40 #include <limits>
41 
42 #ifdef PCIMAJORVERSION
43 #include "raster/memcmp.hh"
44 #include "raster/memset.hh"
45 #endif
46 
47 namespace PCIDSK
48 {
49 
50 /************************************************************************/
51 /*                             BlockTileLayer()                         */
52 /************************************************************************/
53 
54 /**
55  * Constructor.
56  *
57  * @param poBlockDir The associated block directory.
58  * @param nLayer The index of the block layer.
59  * @param psBlockLayer The block layer info.
60  * @param psTileLayer The tile layer info.
61  */
BlockTileLayer(BlockDir * poBlockDir,uint32 nLayer,BlockLayerInfo * psBlockLayer,TileLayerInfo * psTileLayer)62 BlockTileLayer::BlockTileLayer(BlockDir * poBlockDir, uint32 nLayer,
63                                BlockLayerInfo * psBlockLayer,
64                                TileLayerInfo * psTileLayer)
65     : BlockLayer(poBlockDir, nLayer),
66       mpsBlockLayer(psBlockLayer),
67       mpsTileLayer(psTileLayer),
68       mbModified(false)
69 {
70     memset(mszDataType, 0, sizeof(mszDataType));
71     memset(mszCompress, 0, sizeof(mszCompress));
72 
73     mpoTileListMutex = DefaultCreateMutex();
74 }
75 
76 /************************************************************************/
77 /*                            ~BlockTileLayer()                         */
78 /************************************************************************/
79 
80 /**
81  * Destructor.
82  */
~BlockTileLayer(void)83 BlockTileLayer::~BlockTileLayer(void)
84 {
85     delete mpoTileListMutex;
86 }
87 
88 /************************************************************************/
89 /*                              GetTileInfo()                           */
90 /************************************************************************/
91 
92 /**
93  * Gets the tile at the specified index.
94  *
95  * @param nCol The column of the tile.
96  * @param nRow The row of the tile.
97  *
98  * @return The tile at the specified index.
99  */
100 BlockTileLayer::BlockTileInfo *
GetTileInfo(uint32 nCol,uint32 nRow)101 BlockTileLayer::GetTileInfo(uint32 nCol, uint32 nRow)
102 {
103     if (!IsValid())
104         return nullptr;
105 
106     uint32 nTilesPerRow = GetTilePerRow();
107 
108     uint32 iTile = nRow * nTilesPerRow + nCol;
109 
110     MutexHolder oLock(mpoTileListMutex);
111 
112     if (moTileList.empty())
113         ReadTileList();
114 
115     return &moTileList.at(iTile);
116 }
117 
118 /************************************************************************/
119 /*                                  Sync()                              */
120 /************************************************************************/
121 
122 /**
123  * Synchronizes the block tile layer to disk.
124  */
Sync(void)125 void BlockTileLayer::Sync(void)
126 {
127     if (!mbModified)
128         return;
129 
130     try
131     {
132         if (!GetFile()->GetUpdatable())
133             return;
134     }
135     catch (...)
136     {
137         return;
138     }
139 
140     MutexHolder oLock(mpoTileListMutex);
141 
142     if (!mbModified)
143         return;
144 
145     WriteTileList();
146 
147     mbModified = false;
148 }
149 
150 /************************************************************************/
151 /*                              IsCorrupted()                           */
152 /************************************************************************/
IsCorrupted(void) const153 bool BlockTileLayer::IsCorrupted(void) const
154 {
155     // Dead layers have a tile size of 0, but it should be considered valid.
156     if (GetLayerType() == BLTDead)
157         return false;
158 
159     // The tile layer is corrupted when the image size is 0.
160     if (GetXSize() == 0 || GetYSize() == 0)
161         return true;
162 
163     uint64 nTileSize =
164         static_cast<uint64>(GetTileXSize()) * GetTileYSize() * GetDataTypeSize();
165 
166     return nTileSize == 0 || nTileSize > std::numeric_limits<uint32>::max();
167 }
168 
169 /************************************************************************/
170 /*                              GetTileCount()                          */
171 /************************************************************************/
172 
173 /**
174  * Gets the number of tiles in the tile layer.
175  *
176  * @return The number of tiles in the tile layer.
177  */
GetTileCount(void) const178 uint32 BlockTileLayer::GetTileCount(void) const
179 {
180     return (uint32) (((static_cast<uint64>(GetXSize()) + GetTileXSize() - 1) / GetTileXSize()) *
181                      ((static_cast<uint64>(GetYSize()) + GetTileYSize() - 1) / GetTileYSize()));
182 }
183 
184 /************************************************************************/
185 /*                             GetTilePerRow()                          */
186 /************************************************************************/
187 
188 /**
189  * Gets the number of tiles per row in the tile layer.
190  *
191  * @return The number of tiles per row in the tile layer.
192  */
GetTilePerRow(void) const193 uint32 BlockTileLayer::GetTilePerRow(void) const
194 {
195     return (uint32) (static_cast<uint64>(GetXSize()) + GetTileXSize() - 1) / GetTileXSize();
196 }
197 
198 /************************************************************************/
199 /*                             GetTilePerCol()                          */
200 /************************************************************************/
201 
202 /**
203  * Gets the number of tiles per column in the tile layer.
204  *
205  * @return The number of tiles per column in the tile layer.
206  */
GetTilePerCol(void) const207 uint32 BlockTileLayer::GetTilePerCol(void) const
208 {
209     return (uint32) (static_cast<uint64>(GetYSize()) + GetTileYSize() - 1) / GetTileYSize();
210 }
211 
212 /************************************************************************/
213 /*                              GetTileSize()                           */
214 /************************************************************************/
215 
216 /**
217  * Gets the size in bytes of a tile.
218  *
219  * @return The size in bytes of a tile.
220  */
GetTileSize(void) const221 uint32 BlockTileLayer::GetTileSize(void) const
222 {
223     return GetTileXSize() * GetTileYSize() * GetDataTypeSize();
224 }
225 
226 /************************************************************************/
227 /*                            GetDataTypeSize()                         */
228 /************************************************************************/
229 
230 /**
231  * Gets the data type size in bytes.
232  *
233  * @return The data type size in bytes.
234  */
GetDataTypeSize(void) const235 uint32 BlockTileLayer::GetDataTypeSize(void) const
236 {
237     return DataTypeSize(GetDataTypeFromName(GetDataType()));
238 }
239 
240 /************************************************************************/
241 /*                              IsTileValid()                           */
242 /************************************************************************/
243 
244 /**
245  * Checks if the specified tile is valid.
246  *
247  * @param nCol The column of the tile.
248  * @param nRow The row of the tile.
249  *
250  * @return If the specified tile is valid.
251  */
IsTileValid(uint32 nCol,uint32 nRow)252 bool BlockTileLayer::IsTileValid(uint32 nCol, uint32 nRow)
253 {
254     BlockTileInfo * psTile = GetTileInfo(nCol, nRow);
255 
256     return (psTile && psTile->nOffset != INVALID_OFFSET && psTile->nSize != 0 &&
257             AreBlocksAllocated(psTile->nOffset, psTile->nSize));
258 }
259 
260 /************************************************************************/
261 /*                            GetTileDataSize()                         */
262 /************************************************************************/
263 
264 /**
265  * Gets the size in bytes of the specified tile.
266  *
267  * @param nCol The column of the tile.
268  * @param nRow The row of the tile.
269  *
270  * @return The size in bytes of the specified tile.
271  */
GetTileDataSize(uint32 nCol,uint32 nRow)272 uint32 BlockTileLayer::GetTileDataSize(uint32 nCol, uint32 nRow)
273 {
274     BlockTileInfo * psTile = GetTileInfo(nCol, nRow);
275 
276     return psTile ? psTile->nSize : 0;
277 }
278 
279 /************************************************************************/
280 /*                            WriteSparseTile()                         */
281 /************************************************************************/
282 
283 /**
284  * Writes the specified tile only if the data is sparse.
285  *
286  * @param pData The data of the tile.
287  * @param nCol The column of the tile.
288  * @param nRow The row of the tile.
289  *
290  * @return If the specified tile data is sparse.
291  */
WriteSparseTile(const void * pData,uint32 nCol,uint32 nRow)292 bool BlockTileLayer::WriteSparseTile(const void * pData,
293                                      uint32 nCol, uint32 nRow)
294 {
295     MutexHolder oLock(mpoTileListMutex);
296 
297     uint32 nValue = 0;
298 
299     bool bIsSparse = true;
300 
301     uint32 nTileSize = GetTileSize();
302 
303     // Check if we can use a sparse tile with a 4 byte value.
304     if (dynamic_cast<BinaryTileDir *>(mpoBlockDir) && nTileSize % 4 == 0)
305     {
306         uint32 * pnIter = (uint32 *) pData;
307 
308         nValue = *pnIter;
309 
310 #ifdef PCIMAJORVERSION
311         bIsSparse = raster::memcmp32(pnIter, nValue,
312                                      nTileSize / sizeof(uint32));
313 #else
314         uint32 * pnEnd = pnIter + nTileSize / sizeof(uint32);
315         for (; pnIter < pnEnd; ++pnIter)
316         {
317             if (*pnIter != nValue)
318             {
319                 bIsSparse = false;
320                 break;
321             }
322         }
323 #endif
324     }
325     // Check if we can use a sparse tile with a value of 0.
326     else
327     {
328         nValue = 0;
329 
330 #ifdef PCIMAJORVERSION
331         bIsSparse = raster::memcmp8((uchar *) pData, 0, nTileSize);
332 #else
333         uchar * pnIter = (uchar *) pData;
334         uchar * pnEnd = pnIter + nTileSize;
335         for (; pnIter < pnEnd; ++pnIter)
336         {
337             if (*pnIter != nValue)
338             {
339                 bIsSparse = false;
340                 break;
341             }
342         }
343 #endif
344     }
345 
346     // If the tile data is sparse store the sparse value in the nSize member
347     // of the BlockTileInfo structure.
348     if (bIsSparse)
349     {
350         BlockTileInfo * psTile = GetTileInfo(nCol, nRow);
351         if( psTile != nullptr ) // TODO: what if it is null
352         {
353             // Free the blocks used by the tile.
354             if (psTile->nOffset != INVALID_OFFSET)
355                 FreeBlocks(psTile->nOffset, psTile->nSize);
356 
357             psTile->nOffset = INVALID_OFFSET;
358 
359             psTile->nSize = nValue;
360 
361             mbModified = true;
362         }
363     }
364 
365     return bIsSparse;
366 }
367 
368 /************************************************************************/
369 /*                               WriteTile()                            */
370 /************************************************************************/
371 
372 /**
373  * Writes the specified tile.
374  *
375  * @param pData The data of the tile.
376  * @param nCol The column of the tile.
377  * @param nRow The row of the tile.
378  * @param nSize The size of the tile.
379  */
WriteTile(const void * pData,uint32 nCol,uint32 nRow,uint32 nSize)380 void BlockTileLayer::WriteTile(const void * pData,
381                                uint32 nCol, uint32 nRow, uint32 nSize)
382 {
383     MutexHolder oLock(mpoTileListMutex);
384 
385     if (!IsValid())
386         return;
387 
388     BlockTileInfo * psTile = GetTileInfo(nCol, nRow);
389 
390     if (!psTile)
391         return;
392 
393     if (nSize == 0)
394         nSize = GetTileSize();
395 
396     if (psTile->nOffset == INVALID_OFFSET)
397     {
398         psTile->nOffset = GetLayerSize();
399 
400         psTile->nSize = nSize;
401 
402         mbModified = true;
403     }
404 
405     if (psTile->nSize < nSize)
406     {
407         psTile->nOffset = GetLayerSize();
408 
409         psTile->nSize = nSize;
410 
411         mbModified = true;
412     }
413     else if (psTile->nSize > nSize)
414     {
415         psTile->nSize = nSize;
416 
417         mbModified = true;
418     }
419 
420     WriteToLayer(pData, psTile->nOffset, psTile->nSize);
421 }
422 
423 /************************************************************************/
424 /*                             ReadSparseTile()                         */
425 /************************************************************************/
426 
427 /**
428  * Reads the specified tile only if the data is sparse.
429  *
430  * @param pData The data of the tile.
431  * @param nCol The column of the tile.
432  * @param nRow The row of the tile.
433  *
434  * @return If the specified tile data is sparse.
435  */
ReadSparseTile(void * pData,uint32 nCol,uint32 nRow)436 bool BlockTileLayer::ReadSparseTile(void * pData, uint32 nCol, uint32 nRow)
437 {
438     if (!IsValid())
439         return false;
440 
441     BlockTileInfo * psTile = GetTileInfo(nCol, nRow);
442 
443     if (!psTile)
444         return false;
445 
446     if (psTile->nOffset != INVALID_OFFSET)
447         return false;
448 
449     uint32 nTileSize = GetTileSize();
450 
451     // Check if we can use a sparse tile with a 4 byte value.
452     if (dynamic_cast<BinaryTileDir *>(mpoBlockDir) && nTileSize % 4 == 0)
453     {
454 #ifdef PCIMAJORVERSION
455         raster::memset32((uint32 *) pData, psTile->nSize,
456                          nTileSize / sizeof(uint32));
457 #else
458         uint32 * pnIter = (uint32 *) pData;
459         uint32 * pnEnd = pnIter + nTileSize / sizeof(uint32);
460         for (; pnIter < pnEnd; ++pnIter)
461             *pnIter = psTile->nSize;
462 #endif
463     }
464     // Check if we can use a sparse tile with a value of 0.
465     else
466     {
467         memset(pData, 0, nTileSize);
468     }
469 
470     return true;
471 }
472 
473 /************************************************************************/
474 /*                                ReadTile()                            */
475 /************************************************************************/
476 
477 /**
478  * Reads the specified tile.
479  *
480  * @param pData The data of the tile.
481  * @param nCol The column of the tile.
482  * @param nRow The row of the tile.
483  * @param nSize The buffer size.
484  *
485  * @return The size of the tile.
486  */
ReadTile(void * pData,uint32 nCol,uint32 nRow,uint32 nSize)487 uint32 BlockTileLayer::ReadTile(void * pData, uint32 nCol, uint32 nRow, uint32 nSize)
488 {
489     if (!IsValid())
490         return 0;
491 
492     BlockTileInfo * psTile = GetTileInfo(nCol, nRow);
493 
494     if (!psTile)
495         return 0;
496 
497     if (psTile->nOffset == INVALID_OFFSET)
498         return 0;
499 
500     if (psTile->nSize == 0)
501         return 0;
502 
503     uint32 nReadSize = std::min(nSize, psTile->nSize);
504 
505 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
506     assert(psTile->nSize == nSize);
507 #endif
508 
509     if (!ReadFromLayer(pData, psTile->nOffset, nReadSize))
510         return 0;
511 
512     return nReadSize;
513 }
514 
515 /************************************************************************/
516 /*                         ReadPartialSparseTile()                      */
517 /************************************************************************/
518 
519 /**
520  * Reads the specified tile only if the data is sparse.
521  *
522  * @param pData The data of the tile.
523  * @param nCol The column of the tile.
524  * @param nRow The row of the tile.
525  * @param nOffset The offset of the data.
526  * @param nSize The size of the data.
527  *
528  * @return If the specified tile data is sparse.
529  */
ReadPartialSparseTile(void * pData,uint32 nCol,uint32 nRow,uint32 nOffset,uint32 nSize)530 bool BlockTileLayer::ReadPartialSparseTile(void * pData,
531                                            uint32 nCol, uint32 nRow,
532                                            uint32 nOffset, uint32 nSize)
533 {
534     if (!IsValid())
535         return false;
536 
537     BlockTileInfo * psTile = GetTileInfo(nCol, nRow);
538 
539     if (!psTile)
540         return false;
541 
542     if (psTile->nOffset != INVALID_OFFSET)
543         return false;
544 
545     uint32 nTileSize = GetTileSize();
546 
547     // Check if we can use a sparse tile with a 4 byte value.
548     if (dynamic_cast<BinaryTileDir *>(mpoBlockDir) && nTileSize % 4 == 0)
549     {
550         uint32 nValue = psTile->nSize;
551 
552         // We need to bitwise shift the value if the offset isn't aligned.
553         uint32 nByteOffset = nOffset % 4;
554 
555         if (nByteOffset != 0)
556         {
557             uint32 nBitOffset = nByteOffset * 8;
558 
559             nValue = (nValue << nBitOffset) | (nValue >> (32 - nBitOffset));
560         }
561 
562         uint32 nAlign = nSize / sizeof(uint32);
563 
564 #ifdef PCIMAJORVERSION
565         raster::memset32((uint32 *) pData, nValue, nAlign);
566 #else
567         uint32 * pnIter = (uint32 *) pData;
568         uint32 * pnEnd = pnIter + nAlign;
569         for (; pnIter < pnEnd; ++pnIter)
570             *pnIter = nValue;
571 #endif
572 
573         uint32 nRemaining = nSize % 4;
574 
575         if (nRemaining != 0)
576         {
577             uchar * pbyIter = (uchar *) pData + nAlign * 4;
578 
579             do
580             {
581                 nValue = (nValue << 8) | (nValue >> 24);
582 
583                 *pbyIter++ = (uchar) nValue;
584             }
585             while (--nRemaining);
586         }
587     }
588     // Check if we can use a sparse tile with a value of 0.
589     else
590     {
591         memset(pData, 0, nSize);
592     }
593 
594     return true;
595 }
596 
597 /************************************************************************/
598 /*                            ReadPartialTile()                         */
599 /************************************************************************/
600 
601 /**
602  * Reads the specified tile.
603  *
604  * @param pData The data of the tile.
605  * @param nCol The column of the tile.
606  * @param nRow The row of the tile.
607  * @param nOffset The offset of the data.
608  * @param nSize The size of the data.
609  *
610  * @return If the read was successful.
611  */
ReadPartialTile(void * pData,uint32 nCol,uint32 nRow,uint32 nOffset,uint32 nSize)612 bool BlockTileLayer::ReadPartialTile(void * pData, uint32 nCol, uint32 nRow,
613                                      uint32 nOffset, uint32 nSize)
614 {
615     if (!IsValid())
616         return false;
617 
618     BlockTileInfo * psTile = GetTileInfo(nCol, nRow);
619 
620     if (!psTile)
621         return false;
622 
623     if (psTile->nOffset == INVALID_OFFSET)
624         return false;
625 
626     if (psTile->nSize == 0 || psTile->nSize < nOffset + nSize)
627         return false;
628 
629     if (!ReadFromLayer(pData, psTile->nOffset + nOffset, nSize))
630         return false;
631 
632     return true;
633 }
634 
635 /************************************************************************/
636 /*                            SetTileLayerInfo()                        */
637 /************************************************************************/
638 
639 /**
640  * Sets the tile layer information.
641  *
642  * @param nXSize The width of the tile layer.
643  * @param nYSize The height of the tile layer.
644  * @param nTileXSize The width of a tile.
645  * @param nTileYSize The height of a tile.
646  * @param oDataType The data type of the tile layer.
647  * @param oCompress The compress type of the tile layer.
648  * @param bNoDataValid If the NoData value is valid.
649  * @param dfNoDataValue The NoData value of the tile layer.
650  */
SetTileLayerInfo(uint32 nXSize,uint32 nYSize,uint32 nTileXSize,uint32 nTileYSize,const std::string & oDataType,const std::string & oCompress,bool bNoDataValid,double dfNoDataValue)651 void BlockTileLayer::SetTileLayerInfo(uint32 nXSize, uint32 nYSize,
652                                       uint32 nTileXSize, uint32 nTileYSize,
653                                       const std::string & oDataType,
654                                       const std::string & oCompress,
655                                       bool bNoDataValid, double dfNoDataValue)
656 {
657     uint64 nTileSize =
658         static_cast<uint64>(nTileXSize) * nTileYSize *
659         DataTypeSize(GetDataTypeFromName(oDataType.c_str()));
660 
661     if (nTileSize == 0 || nTileSize > std::numeric_limits<uint32>::max())
662     {
663         return ThrowPCIDSKException("Invalid tile dimensions: %d x %d",
664                                     nTileXSize, nTileYSize);
665     }
666 
667     if (nXSize == 0 || nYSize == 0)
668     {
669         return ThrowPCIDSKException("Invalid tile layer dimensions: %d x %d",
670                                     nXSize, nYSize);
671     }
672 
673     mpsTileLayer->nXSize = nXSize;
674     mpsTileLayer->nYSize = nYSize;
675     mpsTileLayer->nTileXSize = nTileXSize;
676     mpsTileLayer->nTileYSize = nTileYSize;
677     mpsTileLayer->bNoDataValid = bNoDataValid;
678     mpsTileLayer->dfNoDataValue = dfNoDataValue;
679 
680     memset(mpsTileLayer->szDataType, ' ', 4);
681     memcpy(mpsTileLayer->szDataType, oDataType.data(), oDataType.size());
682 
683     memset(mpsTileLayer->szCompress, ' ', 8);
684     memcpy(mpsTileLayer->szCompress, oCompress.data(), oCompress.size());
685 
686     // Invalidate the cache variables.
687     *mszDataType = 0;
688     *mszCompress = 0;
689 
690     // Initialize the tile list.
691     uint32 nTileCount = GetTileCount();
692 
693     MutexHolder oLock(mpoTileListMutex);
694 
695     try
696     {
697         moTileList.resize(nTileCount);
698     }
699     catch (const std::exception & ex)
700     {
701         return ThrowPCIDSKException("Out of memory in BlockTileLayer::SetTileLayerInfo(): %s", ex.what());
702     }
703 
704     for (uint32 iTile = 0; iTile < nTileCount; iTile++)
705     {
706         BlockTileInfo * psTile = &moTileList[iTile];
707 
708         psTile->nOffset = INVALID_OFFSET;
709         psTile->nSize = 0;
710     }
711 
712     // Write the tile list to disk.
713     WriteTileList();
714 
715     mbModified = false;
716 
717     oLock.Release();
718 
719     // Make sure that the first tile starts on a block boundary.
720     uint64 nLayerSize = GetLayerSize();
721     uint32 nBlockSize = mpoBlockDir->GetBlockSize();
722 
723     if (nLayerSize % nBlockSize != 0)
724         Resize((nLayerSize / nBlockSize + 1) * nBlockSize);
725 }
726 
727 /************************************************************************/
728 /*                              GetDataType()                           */
729 /************************************************************************/
730 
731 /**
732  * Gets the data type of the tile layer.
733  *
734  * @return The data type of the tile layer.
735  */
GetDataType(void) const736 const char * BlockTileLayer::GetDataType(void) const
737 {
738     if (*mszDataType)
739         return mszDataType;
740 
741     MutexHolder oLock(mpoTileListMutex);
742 
743     if (*mszDataType)
744         return mszDataType;
745 
746     memcpy(mszDataType, mpsTileLayer->szDataType, 4);
747 
748     int nIter = 3;
749 
750     while (nIter > 0 && mszDataType[nIter] == ' ')
751         mszDataType[nIter--] = '\0';
752 
753     return mszDataType;
754 }
755 
756 /************************************************************************/
757 /*                            GetCompressType()                         */
758 /************************************************************************/
759 
760 /**
761  * Gets the compress type of the tile layer.
762  *
763  * @return The compress type of the tile layer.
764  */
GetCompressType(void) const765 const char * BlockTileLayer::GetCompressType(void) const
766 {
767     if (*mszCompress)
768         return mszCompress;
769 
770     MutexHolder oLock(mpoTileListMutex);
771 
772     if (*mszCompress)
773         return mszCompress;
774 
775     memcpy(mszCompress, mpsTileLayer->szCompress, 8);
776 
777     int nIter = 7;
778 
779     while (nIter > 0 && mszCompress[nIter] == ' ')
780         mszCompress[nIter--] = '\0';
781 
782     return mszCompress;
783 }
784 
785 } // namespace PCIDSK
786