Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Block directory API.
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2011
7 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include "blockdir/asciitiledir.h"
13 : #include "blockdir/asciitilelayer.h"
14 : #include "blockdir/blockfile.h"
15 : #include "core/pcidsk_utils.h"
16 : #include "core/pcidsk_scanint.h"
17 : #include "pcidsk_exception.h"
18 : #include "pcidsk_buffer.h"
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <cstdio>
22 : #include <algorithm>
23 : #include <limits>
24 : #include <set>
25 :
26 : using namespace PCIDSK;
27 :
28 : #define ASCII_TILEDIR_VERSION 1
29 :
30 : #define SYS_BLOCK_SIZE 8192
31 : #define SYS_BLOCK_INFO_SIZE 28
32 : #define SYS_BLOCK_LAYER_INFO_SIZE 24
33 :
34 : struct SysBlockInfo
35 : {
36 : uint16 nSegment;
37 : uint32 nStartBlock;
38 : uint32 nNextBlock;
39 : };
40 :
41 : typedef std::vector<SysBlockInfo> SysBlockInfoList;
42 :
43 : /************************************************************************/
44 : /* GetBlockList() */
45 : /************************************************************************/
46 10 : static BlockInfoList GetBlockList(const SysBlockInfoList & oBlockInfoList,
47 : uint32 iStartBlock)
48 : {
49 10 : uint32 iBlock = iStartBlock;
50 :
51 10 : BlockInfoList oBlockList;
52 10 : oBlockList.reserve(oBlockInfoList.size());
53 :
54 170 : while (iBlock < oBlockInfoList.size() &&
55 80 : oBlockList.size() <= oBlockInfoList.size())
56 : {
57 80 : const SysBlockInfo * psBlockInfo = &oBlockInfoList[iBlock];
58 :
59 : BlockInfo sBlock;
60 80 : sBlock.nSegment = psBlockInfo->nSegment;
61 80 : sBlock.nStartBlock = psBlockInfo->nStartBlock;
62 :
63 80 : oBlockList.push_back(sBlock);
64 :
65 80 : iBlock = psBlockInfo->nNextBlock;
66 : }
67 :
68 : // If the block list is larger than the block info list, it means that the
69 : // file is corrupted so look for a loop in the block list.
70 10 : if (oBlockList.size() > oBlockInfoList.size())
71 : {
72 0 : iBlock = iStartBlock;
73 :
74 0 : std::set<uint32> oBlockSet;
75 :
76 0 : oBlockList.clear();
77 :
78 0 : while (iBlock < oBlockInfoList.size())
79 : {
80 0 : const SysBlockInfo * psBlockInfo = &oBlockInfoList[iBlock];
81 :
82 : BlockInfo sBlock;
83 0 : sBlock.nSegment = psBlockInfo->nSegment;
84 0 : sBlock.nStartBlock = psBlockInfo->nStartBlock;
85 :
86 0 : oBlockList.push_back(sBlock);
87 :
88 0 : oBlockSet.insert(iBlock);
89 :
90 0 : iBlock = psBlockInfo->nNextBlock;
91 :
92 0 : if (oBlockSet.find(iBlock) != oBlockSet.end())
93 0 : break;
94 : }
95 : }
96 :
97 20 : return oBlockList;
98 : }
99 :
100 : /************************************************************************/
101 : /* GetOptimizedDirSize() */
102 : /************************************************************************/
103 2 : size_t AsciiTileDir::GetOptimizedDirSize(BlockFile * poFile)
104 : {
105 2 : std::string oFileOptions = poFile->GetFileOptions();
106 :
107 34 : for (char & chIter : oFileOptions)
108 32 : chIter = (char) toupper((uchar) chIter);
109 :
110 : // Compute the ratio.
111 2 : double dfRatio = 0.0;
112 :
113 : // The 35% is for the overviews.
114 2 : if (oFileOptions.find("TILED") != std::string::npos)
115 2 : dfRatio = 1.35;
116 : else
117 0 : dfRatio = 0.35;
118 :
119 : // The 5% is for the new blocks.
120 2 : dfRatio += 0.05;
121 :
122 2 : double dfFileSize = poFile->GetImageFileSize() * dfRatio;
123 :
124 2 : uint32 nBlockSize = SYS_BLOCK_SIZE;
125 :
126 2 : uint64 nBlockCount = (uint64) (dfFileSize / nBlockSize);
127 :
128 2 : uint64 nLayerCount = poFile->GetChannels();
129 :
130 : // The 12 is for the overviews.
131 2 : nLayerCount *= 12;
132 :
133 2 : uint64 nDirSize = 512 +
134 2 : (nBlockCount * SYS_BLOCK_INFO_SIZE +
135 2 : nLayerCount * SYS_BLOCK_LAYER_INFO_SIZE +
136 2 : nLayerCount * sizeof(TileLayerInfo));
137 :
138 : #if SIZEOF_VOIDP < 8
139 : if (nDirSize > std::numeric_limits<size_t>::max())
140 : return ThrowPCIDSKException(0, "Unable to create extremely large file on 32-bit system.");
141 : #endif
142 :
143 4 : return static_cast<size_t>(nDirSize);
144 : }
145 :
146 : /************************************************************************/
147 : /* AsciiTileDir() */
148 : /************************************************************************/
149 :
150 : /**
151 : * Constructor.
152 : *
153 : * @param poFile The associated file object.
154 : * @param nSegment The segment of the block directory.
155 : */
156 11 : AsciiTileDir::AsciiTileDir(BlockFile * poFile, uint16 nSegment)
157 11 : : BlockTileDir(poFile, nSegment)
158 : {
159 : // Read the block directory header from disk.
160 : uint8 abyHeader[512];
161 :
162 11 : mpoFile->ReadFromSegment(mnSegment, abyHeader, 0, 512);
163 :
164 : // Get the version of the block directory.
165 11 : mnVersion = ScanInt3(abyHeader + 7);
166 :
167 : // Read the block directory info from the header.
168 11 : msBlockDir.nLayerCount = ScanInt8(abyHeader + 10);
169 11 : msBlockDir.nBlockCount = ScanInt8(abyHeader + 18);
170 11 : msBlockDir.nFirstFreeBlock = ScanInt8(abyHeader + 26);
171 :
172 : // The third last byte is for the endianness.
173 11 : mchEndianness = abyHeader[512 - 3];
174 11 : mbNeedsSwap = (mchEndianness == 'B' ?
175 11 : !BigEndianSystem() : BigEndianSystem());
176 :
177 : // The last 2 bytes of the header are for the validity info.
178 11 : memcpy(&mnValidInfo, abyHeader + 512 - 2, 2);
179 :
180 11 : SwapValue(&mnValidInfo);
181 :
182 : // Check that we support the tile directory version.
183 11 : if (mnVersion > ASCII_TILEDIR_VERSION)
184 : {
185 0 : ThrowPCIDSKException("The tile directory version %d is not supported.", mnVersion);
186 0 : return;
187 : }
188 :
189 : // The size of the block layers.
190 11 : uint64 nReadSize = (static_cast<uint64>(msBlockDir.nBlockCount) * SYS_BLOCK_INFO_SIZE +
191 11 : static_cast<uint64>(msBlockDir.nLayerCount) * SYS_BLOCK_LAYER_INFO_SIZE);
192 :
193 11 : if (mpoFile->IsCorruptedSegment(mnSegment, 512, nReadSize))
194 : {
195 0 : ThrowPCIDSKException("The tile directory is corrupted.");
196 0 : return;
197 : }
198 :
199 : #if SIZEOF_VOIDP < 8
200 : if (nReadSize > std::numeric_limits<size_t>::max())
201 : {
202 : ThrowPCIDSKException("Unable to open extremely large file on 32-bit system.");
203 : return;
204 : }
205 : #endif
206 :
207 : // Initialize the block layers.
208 : try
209 : {
210 11 : moLayerInfoList.resize(msBlockDir.nLayerCount);
211 11 : moTileLayerInfoList.resize(msBlockDir.nLayerCount);
212 :
213 11 : moLayerList.resize(msBlockDir.nLayerCount);
214 : }
215 0 : catch (const std::exception & ex)
216 : {
217 0 : ThrowPCIDSKException("Out of memory in AsciiTileDir(): %s", ex.what());
218 0 : return;
219 : }
220 :
221 24 : for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
222 : {
223 13 : moLayerInfoList[iLayer] = new BlockLayerInfo;
224 13 : moTileLayerInfoList[iLayer] = new TileLayerInfo;
225 :
226 13 : moLayerList[iLayer] = new AsciiTileLayer(this, iLayer,
227 13 : moLayerInfoList[iLayer],
228 13 : moTileLayerInfoList[iLayer]);
229 : }
230 :
231 : // Read the block directory from disk.
232 11 : if (memcmp(abyHeader + 128, "SUBVERSION 1", 12) != 0)
233 : {
234 5 : ReadFullDir();
235 :
236 10 : for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
237 5 : GetTileLayer(iLayer)->ReadHeader();
238 : }
239 : else
240 : {
241 6 : ReadPartialDir();
242 : }
243 :
244 : // Check if any of the tile layers are corrupted.
245 24 : for (BlockLayer * poLayer : moLayerList)
246 : {
247 13 : BlockTileLayer * poTileLayer = dynamic_cast<BlockTileLayer *>(poLayer);
248 :
249 13 : if (poTileLayer == nullptr || poTileLayer->IsCorrupted())
250 : {
251 0 : ThrowPCIDSKException("The tile directory is corrupted.");
252 0 : return;
253 : }
254 : }
255 : }
256 :
257 : /************************************************************************/
258 : /* AsciiTileDir() */
259 : /************************************************************************/
260 :
261 : /**
262 : * Constructor.
263 : *
264 : * @param poFile The associated file object.
265 : * @param nSegment The segment of the block directory.
266 : * @param nBlockSize The size of the blocks.
267 : */
268 2 : AsciiTileDir::AsciiTileDir(BlockFile * poFile, uint16 nSegment,
269 2 : CPL_UNUSED uint32 nBlockSize)
270 2 : : BlockTileDir(poFile, nSegment, ASCII_TILEDIR_VERSION)
271 : {
272 : // Initialize the directory info.
273 2 : msBlockDir.nLayerCount = 0;
274 2 : msBlockDir.nBlockCount = 0;
275 2 : msBlockDir.nFirstFreeBlock = 0;
276 :
277 : // Create an empty free block layer.
278 2 : msFreeBlockLayer.nLayerType = BLTFree;
279 2 : msFreeBlockLayer.nStartBlock = INVALID_BLOCK;
280 2 : msFreeBlockLayer.nBlockCount = 0;
281 2 : msFreeBlockLayer.nLayerSize = 0;
282 :
283 2 : mpoFreeBlockLayer = new AsciiTileLayer(this, INVALID_LAYER,
284 2 : &msFreeBlockLayer, nullptr);
285 2 : }
286 :
287 : /************************************************************************/
288 : /* GetTileLayer() */
289 : /************************************************************************/
290 :
291 : /**
292 : * Gets the block layer at the specified index.
293 : *
294 : * @param iLayer The index of the block layer.
295 : *
296 : * @return The block layer at the specified index.
297 : */
298 12 : AsciiTileLayer * AsciiTileDir::GetTileLayer(uint32 iLayer)
299 : {
300 12 : return (AsciiTileLayer *) BlockDir::GetLayer(iLayer);
301 : }
302 :
303 : /************************************************************************/
304 : /* GetBlockSize() */
305 : /************************************************************************/
306 :
307 : /**
308 : * Gets the block size of the block directory.
309 : *
310 : * @return The block size of the block directory.
311 : */
312 99 : uint32 AsciiTileDir::GetBlockSize(void) const
313 : {
314 99 : return SYS_BLOCK_SIZE;
315 : }
316 :
317 : /************************************************************************/
318 : /* ReadFullDir() */
319 : /************************************************************************/
320 5 : void AsciiTileDir::ReadFullDir(void)
321 : {
322 : // The size of the block layers.
323 5 : uint64 nReadSize = (static_cast<uint64>(msBlockDir.nBlockCount) * SYS_BLOCK_INFO_SIZE +
324 5 : static_cast<uint64>(msBlockDir.nLayerCount) * SYS_BLOCK_LAYER_INFO_SIZE);
325 :
326 5 : if (mpoFile->IsCorruptedSegment(mnSegment, 512, nReadSize))
327 0 : return ThrowPCIDSKException("The tile directory is corrupted.");
328 :
329 : #if SIZEOF_VOIDP < 8
330 : if (nReadSize > std::numeric_limits<size_t>::max())
331 : return ThrowPCIDSKException("Unable to open extremely large file on 32-bit system.");
332 : #endif
333 :
334 : // Read the block layers from disk.
335 5 : uint8 * pabyBlockDir = (uint8 *) malloc(static_cast<size_t>(nReadSize));
336 :
337 5 : if (pabyBlockDir == nullptr)
338 0 : return ThrowPCIDSKException("Out of memory in AsciiTileDir::ReadFullDir().");
339 :
340 10 : PCIDSKBuffer oBlockDirAutoPtr;
341 5 : oBlockDirAutoPtr.buffer = (char *) pabyBlockDir;
342 :
343 5 : uint8 * pabyBlockDirIter = pabyBlockDir;
344 :
345 5 : mpoFile->ReadFromSegment(mnSegment, pabyBlockDir, 512, nReadSize);
346 :
347 : // Read the block list.
348 5 : SysBlockInfoList oBlockInfoList(msBlockDir.nBlockCount);
349 :
350 85 : for (uint32 iBlock = 0; iBlock < msBlockDir.nBlockCount; iBlock++)
351 : {
352 80 : SysBlockInfo * psBlock = &oBlockInfoList[iBlock];
353 :
354 80 : psBlock->nSegment = ScanInt4(pabyBlockDirIter);
355 80 : pabyBlockDirIter += 4;
356 :
357 80 : psBlock->nStartBlock = ScanInt8(pabyBlockDirIter);
358 80 : pabyBlockDirIter += 8;
359 :
360 : //psBlock->nLayer = ScanInt8(pabyBlockDirIter);
361 80 : pabyBlockDirIter += 8;
362 :
363 80 : psBlock->nNextBlock = ScanInt8(pabyBlockDirIter);
364 80 : pabyBlockDirIter += 8;
365 : }
366 :
367 : // Read the block layers.
368 10 : for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
369 : {
370 5 : BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
371 :
372 5 : psLayer->nLayerType = ScanInt4(pabyBlockDirIter);
373 5 : pabyBlockDirIter += 4;
374 :
375 5 : psLayer->nStartBlock = ScanInt8(pabyBlockDirIter);
376 5 : pabyBlockDirIter += 8;
377 :
378 5 : psLayer->nLayerSize = ScanInt12(pabyBlockDirIter);
379 5 : pabyBlockDirIter += 12;
380 : }
381 :
382 : // Create all the block layers.
383 10 : for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
384 : {
385 5 : BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
386 :
387 5 : AsciiTileLayer * poLayer = GetTileLayer((uint32) iLayer);
388 :
389 : poLayer->moBlockList =
390 5 : GetBlockList(oBlockInfoList, psLayer->nStartBlock);
391 :
392 : // We need to validate the block count field.
393 5 : psLayer->nBlockCount = (uint32) poLayer->moBlockList.size();
394 : }
395 :
396 : // Create the free block layer.
397 5 : msFreeBlockLayer.nLayerType = BLTFree;
398 5 : msFreeBlockLayer.nStartBlock = msBlockDir.nFirstFreeBlock;
399 5 : msFreeBlockLayer.nBlockCount = 0;
400 5 : msFreeBlockLayer.nLayerSize = 0;
401 :
402 5 : mpoFreeBlockLayer = new AsciiTileLayer(this, INVALID_LAYER,
403 5 : &msFreeBlockLayer, nullptr);
404 :
405 5 : ((AsciiTileLayer *) mpoFreeBlockLayer)->moBlockList =
406 5 : GetBlockList(oBlockInfoList, msFreeBlockLayer.nStartBlock);
407 :
408 : // We need to validate the block count field.
409 5 : msFreeBlockLayer.nBlockCount = (uint32)
410 5 : ((AsciiTileLayer *) mpoFreeBlockLayer)->moBlockList.size();
411 : }
412 :
413 : /************************************************************************/
414 : /* ReadPartialDir() */
415 : /************************************************************************/
416 6 : void AsciiTileDir::ReadPartialDir(void)
417 : {
418 : // The offset of the block layers.
419 6 : uint64 nOffset = static_cast<uint64>(msBlockDir.nBlockCount) * SYS_BLOCK_INFO_SIZE;
420 :
421 : // The size of the block layers.
422 6 : uint64 nReadSize = (static_cast<uint64>(msBlockDir.nLayerCount) * SYS_BLOCK_LAYER_INFO_SIZE +
423 : static_cast<uint64>(msBlockDir.nLayerCount) * sizeof(TileLayerInfo));
424 :
425 6 : if (mpoFile->IsCorruptedSegment(mnSegment, 512 + nOffset, nReadSize))
426 0 : return ThrowPCIDSKException("The tile directory is corrupted.");
427 :
428 : #if SIZEOF_VOIDP < 8
429 : if (nReadSize > std::numeric_limits<size_t>::max())
430 : return ThrowPCIDSKException("Unable to open extremely large file on 32-bit system.");
431 : #endif
432 :
433 : // Read the block layers from disk.
434 6 : uint8 * pabyBlockDir = (uint8 *) malloc(static_cast<size_t>(nReadSize));
435 :
436 6 : if (pabyBlockDir == nullptr)
437 0 : return ThrowPCIDSKException("Out of memory in AsciiTileDir::ReadPartialDir().");
438 :
439 6 : PCIDSKBuffer oBlockDirAutoPtr;
440 6 : oBlockDirAutoPtr.buffer = (char *) pabyBlockDir;
441 :
442 6 : uint8 * pabyBlockDirIter = pabyBlockDir;
443 :
444 6 : mpoFile->ReadFromSegment(mnSegment, pabyBlockDir, 512 + nOffset, nReadSize);
445 :
446 : // Read the block layers.
447 6 : BlockLayerInfo * psPreviousLayer = nullptr;
448 :
449 14 : for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
450 : {
451 8 : BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
452 :
453 8 : psLayer->nLayerType = ScanInt4(pabyBlockDirIter);
454 8 : pabyBlockDirIter += 4;
455 :
456 8 : psLayer->nStartBlock = ScanInt8(pabyBlockDirIter);
457 8 : pabyBlockDirIter += 8;
458 :
459 8 : psLayer->nLayerSize = ScanInt12(pabyBlockDirIter);
460 8 : pabyBlockDirIter += 12;
461 :
462 8 : if (psLayer->nStartBlock != INVALID_BLOCK)
463 : {
464 8 : if (psPreviousLayer)
465 : {
466 2 : if (psLayer->nStartBlock < psPreviousLayer->nStartBlock)
467 0 : return ThrowPCIDSKException("The tile directory is corrupted.");
468 :
469 2 : psPreviousLayer->nBlockCount =
470 2 : psLayer->nStartBlock - psPreviousLayer->nStartBlock;
471 : }
472 :
473 8 : psPreviousLayer = psLayer;
474 : }
475 : else
476 : {
477 0 : psLayer->nBlockCount = 0;
478 : }
479 : }
480 :
481 : // Read the tile layers.
482 14 : for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
483 : {
484 8 : size_t nSize = sizeof(TileLayerInfo);
485 8 : SwapTileLayer((TileLayerInfo *) pabyBlockDirIter);
486 8 : memcpy(moTileLayerInfoList[iLayer], pabyBlockDirIter, nSize);
487 8 : pabyBlockDirIter += nSize;
488 : }
489 :
490 : // Read the free block layer.
491 6 : msFreeBlockLayer.nLayerType = BLTFree;
492 6 : msFreeBlockLayer.nStartBlock = msBlockDir.nFirstFreeBlock;
493 6 : msFreeBlockLayer.nBlockCount = 0;
494 6 : msFreeBlockLayer.nLayerSize = 0;
495 :
496 6 : if (msFreeBlockLayer.nStartBlock != INVALID_BLOCK)
497 : {
498 6 : if (psPreviousLayer)
499 : {
500 6 : if (msFreeBlockLayer.nStartBlock < psPreviousLayer->nStartBlock)
501 0 : return ThrowPCIDSKException("The tile directory is corrupted.");
502 :
503 6 : psPreviousLayer->nBlockCount =
504 6 : msFreeBlockLayer.nStartBlock - psPreviousLayer->nStartBlock;
505 : }
506 :
507 6 : if (msBlockDir.nBlockCount < msFreeBlockLayer.nStartBlock)
508 0 : return ThrowPCIDSKException("The tile directory is corrupted.");
509 :
510 6 : msFreeBlockLayer.nBlockCount =
511 6 : msBlockDir.nBlockCount - msFreeBlockLayer.nStartBlock;
512 : }
513 : else
514 : {
515 0 : if (psPreviousLayer)
516 : {
517 0 : if (msBlockDir.nBlockCount < psPreviousLayer->nStartBlock)
518 0 : return ThrowPCIDSKException("The tile directory is corrupted.");
519 :
520 0 : psPreviousLayer->nBlockCount =
521 0 : msBlockDir.nBlockCount - psPreviousLayer->nStartBlock;
522 : }
523 :
524 0 : msFreeBlockLayer.nBlockCount = 0;
525 : }
526 : }
527 :
528 : /************************************************************************/
529 : /* GetDirSize() */
530 : /************************************************************************/
531 :
532 : /**
533 : * Gets the size in bytes of the block tile directory.
534 : *
535 : * @return The size in bytes of the block tile directory.
536 : */
537 2 : size_t AsciiTileDir::GetDirSize(void) const
538 : {
539 2 : uint64 nDirSize = 0;
540 :
541 : // Add the size of the header.
542 2 : nDirSize += 512;
543 :
544 : // Add the size of the blocks.
545 4 : for (size_t iLayer = 0; iLayer < moLayerInfoList.size(); iLayer++)
546 : {
547 2 : const BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
548 :
549 2 : nDirSize += static_cast<uint64>(psLayer->nBlockCount) * SYS_BLOCK_INFO_SIZE;
550 : }
551 :
552 : // Add the size of the free blocks.
553 2 : nDirSize += static_cast<uint64>(msFreeBlockLayer.nBlockCount) * SYS_BLOCK_INFO_SIZE;
554 :
555 : // Add the size of the block layers.
556 2 : nDirSize += static_cast<uint64>(moLayerInfoList.size()) * SYS_BLOCK_LAYER_INFO_SIZE;
557 :
558 : // Add the size of the tile layers.
559 2 : nDirSize += static_cast<uint64>(moTileLayerInfoList.size()) * sizeof(TileLayerInfo);
560 :
561 : #if SIZEOF_VOIDP < 8
562 : if (nDirSize > std::numeric_limits<size_t>::max())
563 : return ThrowPCIDSKException(0, "Unable to open extremely large file on 32-bit system or the tile directory is corrupted.");
564 : #endif
565 :
566 2 : return static_cast<size_t>(nDirSize);
567 : }
568 :
569 : /************************************************************************/
570 : /* GetLayerBlockCount() */
571 : /************************************************************************/
572 8 : uint32 AsciiTileDir::GetLayerBlockCount(void) const
573 : {
574 8 : uint32 nLayerBlockCount = 0;
575 :
576 16 : for (size_t iLayer = 0; iLayer < moLayerInfoList.size(); iLayer++)
577 : {
578 8 : const BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
579 :
580 8 : nLayerBlockCount += psLayer->nBlockCount;
581 : }
582 :
583 8 : return nLayerBlockCount;
584 : }
585 :
586 : /************************************************************************/
587 : /* GetFreeBlockCount() */
588 : /************************************************************************/
589 8 : uint32 AsciiTileDir::GetFreeBlockCount(void) const
590 : {
591 8 : return msFreeBlockLayer.nBlockCount;
592 : }
593 :
594 : /************************************************************************/
595 : /* UpdateBlockDirInfo() */
596 : /************************************************************************/
597 2 : void AsciiTileDir::UpdateBlockDirInfo(void)
598 : {
599 2 : uint32 nLayerBlockCount = GetLayerBlockCount();
600 2 : uint32 nFreeBlockCount = GetFreeBlockCount();
601 :
602 : // Update the block directory info.
603 2 : msBlockDir.nLayerCount = (uint32) moLayerInfoList.size();
604 2 : msBlockDir.nBlockCount = nLayerBlockCount + nFreeBlockCount;
605 2 : msBlockDir.nFirstFreeBlock = nLayerBlockCount;
606 2 : }
607 :
608 : /************************************************************************/
609 : /* InitBlockList() */
610 : /************************************************************************/
611 4 : void AsciiTileDir::InitBlockList(AsciiTileLayer * poLayer)
612 : {
613 4 : if (!poLayer)
614 0 : return;
615 4 : if( poLayer->mpsBlockLayer->nBlockCount == 0)
616 : {
617 0 : poLayer->moBlockList = BlockInfoList();
618 0 : return;
619 : }
620 :
621 4 : BlockLayerInfo * psLayer = poLayer->mpsBlockLayer;
622 :
623 : // The offset of the blocks.
624 4 : uint64 nOffset = static_cast<uint64>(psLayer->nStartBlock) * SYS_BLOCK_INFO_SIZE;
625 :
626 : // The size of the blocks.
627 4 : uint64 nReadSize = static_cast<uint64>(psLayer->nBlockCount) * SYS_BLOCK_INFO_SIZE;
628 :
629 4 : if (mpoFile->IsCorruptedSegment(mnSegment, 512 + nOffset, nReadSize))
630 0 : return ThrowPCIDSKException("The tile directory is corrupted.");
631 :
632 : #if SIZEOF_VOIDP < 8
633 : if (nReadSize > std::numeric_limits<size_t>::max())
634 : return ThrowPCIDSKException("Unable to open extremely large file on 32-bit system.");
635 : #endif
636 :
637 : // Read the blocks from disk.
638 4 : uint8 * pabyBlockDir = (uint8 *) malloc(static_cast<size_t>(nReadSize));
639 :
640 4 : if (pabyBlockDir == nullptr)
641 0 : return ThrowPCIDSKException("Out of memory in AsciiTileDir::InitBlockList().");
642 :
643 4 : PCIDSKBuffer oBlockDirAutoPtr;
644 4 : oBlockDirAutoPtr.buffer = (char *) pabyBlockDir;
645 :
646 4 : uint8 * pabyBlockDirIter = pabyBlockDir;
647 :
648 4 : mpoFile->ReadFromSegment(mnSegment, pabyBlockDir, 512 + nOffset, nReadSize);
649 :
650 : // Setup the block list.
651 : try
652 : {
653 4 : poLayer->moBlockList.resize(psLayer->nBlockCount);
654 : }
655 0 : catch (const std::exception & ex)
656 : {
657 0 : return ThrowPCIDSKException("Out of memory in AsciiTileDir::InitBlockList(): %s", ex.what());
658 : }
659 :
660 13 : for (uint32 iBlock = 0; iBlock < psLayer->nBlockCount; iBlock++)
661 : {
662 9 : BlockInfo * psBlock = &poLayer->moBlockList[iBlock];
663 :
664 9 : psBlock->nSegment = ScanInt4(pabyBlockDirIter);
665 9 : pabyBlockDirIter += 4;
666 :
667 9 : psBlock->nStartBlock = ScanInt8(pabyBlockDirIter);
668 9 : pabyBlockDirIter += 8;
669 :
670 : //psBlock->nLayer = ScanInt8(pabyBlockDirIter);
671 9 : pabyBlockDirIter += 8;
672 :
673 : //psBlock->nNextBlock = ScanInt8(pabyBlockDirIter);
674 9 : pabyBlockDirIter += 8;
675 : }
676 : }
677 :
678 : /************************************************************************/
679 : /* ReadLayerBlocks() */
680 : /************************************************************************/
681 4 : void AsciiTileDir::ReadLayerBlocks(uint32 iLayer)
682 : {
683 4 : InitBlockList((AsciiTileLayer *) moLayerList[iLayer]);
684 4 : }
685 :
686 : /************************************************************************/
687 : /* ReadFreeBlockLayer() */
688 : /************************************************************************/
689 0 : void AsciiTileDir::ReadFreeBlockLayer(void)
690 : {
691 0 : mpoFreeBlockLayer = new AsciiTileLayer(this, INVALID_LAYER,
692 0 : &msFreeBlockLayer, nullptr);
693 :
694 0 : InitBlockList((AsciiTileLayer *) mpoFreeBlockLayer);
695 0 : }
696 :
697 : /************************************************************************/
698 : /* WriteDir() */
699 : /************************************************************************/
700 2 : void AsciiTileDir::WriteDir(void)
701 : {
702 2 : UpdateBlockDirInfo();
703 :
704 : // Make sure all the layer's block list are valid.
705 2 : if (mbOnDisk)
706 : {
707 0 : for (size_t iLayer = 0; iLayer < moLayerList.size(); iLayer++)
708 : {
709 0 : AsciiTileLayer * poLayer = GetTileLayer((uint32) iLayer);
710 :
711 0 : if (poLayer->moBlockList.size() != poLayer->GetBlockCount())
712 0 : InitBlockList(poLayer);
713 : }
714 : }
715 :
716 : // What is the size of the block directory.
717 2 : size_t nDirSize = GetDirSize();
718 :
719 : // If we are resizing the segment, resize it to the optimized size.
720 2 : if (nDirSize > mpoFile->GetSegmentSize(mnSegment))
721 0 : nDirSize = std::max(nDirSize, GetOptimizedDirSize(mpoFile));
722 :
723 : // Write the block directory to disk.
724 2 : char * pabyBlockDir = (char *) malloc(nDirSize + 1); // +1 for '\0'.
725 :
726 2 : if (pabyBlockDir == nullptr)
727 0 : return ThrowPCIDSKException("Out of memory in AsciiTileDir::WriteDir().");
728 :
729 4 : PCIDSKBuffer oBlockDirAutoPtr;
730 2 : oBlockDirAutoPtr.buffer = pabyBlockDir;
731 :
732 2 : char * pabyBlockDirIter = pabyBlockDir;
733 :
734 : // Initialize the header.
735 2 : memset(pabyBlockDir, ' ', 512);
736 :
737 : // The first 10 bytes are for the version.
738 2 : memcpy(pabyBlockDirIter, "VERSION", 7);
739 2 : snprintf(pabyBlockDirIter + 7, 9, "%3d", mnVersion);
740 2 : pabyBlockDirIter += 10;
741 :
742 : // Write the block directory info.
743 2 : snprintf(pabyBlockDirIter, 9, "%8d", msBlockDir.nLayerCount);
744 2 : pabyBlockDirIter += 8;
745 :
746 2 : snprintf(pabyBlockDirIter, 9, "%8d", msBlockDir.nBlockCount);
747 2 : pabyBlockDirIter += 8;
748 :
749 2 : snprintf(pabyBlockDirIter, 9, "%8d", msBlockDir.nFirstFreeBlock);
750 :
751 : // The bytes from 128 to 140 are for the subversion.
752 2 : memcpy(pabyBlockDir + 128, "SUBVERSION 1", 12);
753 :
754 : // The third last byte is for the endianness.
755 2 : pabyBlockDir[512 - 3] = mchEndianness;
756 :
757 : // The last 2 bytes of the header are for the validity info.
758 2 : uint16 nValidInfo = ++mnValidInfo;
759 2 : SwapValue(&nValidInfo);
760 2 : memcpy(pabyBlockDir + 512 - 2, &nValidInfo, 2);
761 :
762 : // The header is 512 bytes.
763 2 : pabyBlockDirIter = pabyBlockDir + 512;
764 :
765 : // Write the block info list.
766 2 : uint32 nNextBlock = 1;
767 :
768 4 : for (size_t iLayer = 0; iLayer < moLayerInfoList.size(); iLayer++)
769 : {
770 2 : BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
771 :
772 2 : AsciiTileLayer * poLayer = GetTileLayer((uint32) iLayer);
773 :
774 6 : for (size_t iBlock = 0; iBlock < psLayer->nBlockCount; iBlock++)
775 : {
776 4 : BlockInfo * psBlock = &poLayer->moBlockList[iBlock];
777 :
778 4 : snprintf(pabyBlockDirIter, 9, "%4d", psBlock->nSegment);
779 4 : pabyBlockDirIter += 4;
780 :
781 4 : snprintf(pabyBlockDirIter, 9, "%8d", psBlock->nStartBlock);
782 4 : pabyBlockDirIter += 8;
783 :
784 4 : snprintf(pabyBlockDirIter, 9, "%8d", (uint32) iLayer);
785 4 : pabyBlockDirIter += 8;
786 :
787 4 : if (iBlock != psLayer->nBlockCount - 1)
788 2 : snprintf(pabyBlockDirIter, 9, "%8d", nNextBlock);
789 : else
790 2 : snprintf(pabyBlockDirIter, 9, "%8d", -1);
791 4 : pabyBlockDirIter += 8;
792 :
793 4 : nNextBlock++;
794 : }
795 : }
796 :
797 : // Write the free block info list.
798 2 : AsciiTileLayer * poLayer = (AsciiTileLayer *) mpoFreeBlockLayer;
799 :
800 30 : for (size_t iBlock = 0; iBlock < msFreeBlockLayer.nBlockCount; iBlock++)
801 : {
802 28 : BlockInfo * psBlock = &poLayer->moBlockList[iBlock];
803 :
804 28 : snprintf(pabyBlockDirIter, 9, "%4d", psBlock->nSegment);
805 28 : pabyBlockDirIter += 4;
806 :
807 28 : snprintf(pabyBlockDirIter, 9, "%8d", psBlock->nStartBlock);
808 28 : pabyBlockDirIter += 8;
809 :
810 28 : snprintf(pabyBlockDirIter, 9, "%8d", -1);
811 28 : pabyBlockDirIter += 8;
812 :
813 28 : if (iBlock != msFreeBlockLayer.nBlockCount - 1)
814 26 : snprintf(pabyBlockDirIter, 9, "%8d", nNextBlock);
815 : else
816 2 : snprintf(pabyBlockDirIter, 9, "%8d", -1);
817 28 : pabyBlockDirIter += 8;
818 :
819 28 : nNextBlock++;
820 : }
821 :
822 : // Write the block layers.
823 2 : uint32 nStartBlock = 0;
824 :
825 4 : for (BlockLayerInfo * psLayer : moLayerInfoList)
826 : {
827 2 : snprintf(pabyBlockDirIter, 9, "%4d", psLayer->nLayerType);
828 2 : pabyBlockDirIter += 4;
829 :
830 2 : if (psLayer->nBlockCount != 0)
831 2 : snprintf(pabyBlockDirIter, 9, "%8d", nStartBlock);
832 : else
833 0 : snprintf(pabyBlockDirIter, 9, "%8d", -1);
834 2 : pabyBlockDirIter += 8;
835 :
836 2 : snprintf(pabyBlockDirIter, 13, "%12" PCIDSK_FRMT_64_WITHOUT_PREFIX "d", psLayer->nLayerSize);
837 2 : pabyBlockDirIter += 12;
838 :
839 2 : nStartBlock += psLayer->nBlockCount;
840 : }
841 :
842 : // Write the tile layers.
843 4 : for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
844 : {
845 2 : size_t nSize = sizeof(TileLayerInfo);
846 2 : memcpy(pabyBlockDirIter, moTileLayerInfoList[iLayer], nSize);
847 2 : SwapTileLayer((TileLayerInfo *) pabyBlockDirIter);
848 2 : pabyBlockDirIter += nSize;
849 : }
850 :
851 : // Initialize the remaining bytes so that Valgrind doesn't complain.
852 2 : size_t nRemainingBytes = pabyBlockDir + nDirSize - pabyBlockDirIter;
853 :
854 2 : if (nRemainingBytes)
855 0 : memset(pabyBlockDirIter, 0, nRemainingBytes);
856 :
857 : // Write the block directory to disk.
858 2 : mpoFile->WriteToSegment(mnSegment, pabyBlockDir, 0, nDirSize);
859 : }
860 :
861 : /************************************************************************/
862 : /* _CreateLayer() */
863 : /************************************************************************/
864 :
865 : /**
866 : * Creates a block layer of the specified type at the specified index.
867 : *
868 : * @param nLayerType The type of the block layer to create.
869 : * @param iLayer The index of the block layer to create.
870 : *
871 : * @return The new block layer.
872 : */
873 2 : BlockLayer * AsciiTileDir::_CreateLayer(uint16 nLayerType, uint32 iLayer)
874 : {
875 2 : if (iLayer == moLayerInfoList.size())
876 : {
877 : try
878 : {
879 2 : moLayerInfoList.resize(moLayerInfoList.size() + 1);
880 2 : moTileLayerInfoList.resize(moLayerInfoList.size());
881 : }
882 0 : catch (const std::exception & ex)
883 : {
884 0 : return (BlockLayer *) ThrowPCIDSKExceptionPtr("Out of memory in AsciiTileDir::_CreateLayer(): %s", ex.what());
885 : }
886 :
887 2 : moLayerInfoList[iLayer] = new BlockLayerInfo;
888 2 : moTileLayerInfoList[iLayer] = new TileLayerInfo;
889 : }
890 :
891 : // Setup the block layer info.
892 2 : BlockLayerInfo * psBlockLayer = moLayerInfoList[iLayer];
893 :
894 2 : psBlockLayer->nLayerType = nLayerType;
895 2 : psBlockLayer->nBlockCount = 0;
896 2 : psBlockLayer->nLayerSize = 0;
897 :
898 : // Setup the tile layer info.
899 2 : TileLayerInfo * psTileLayer = moTileLayerInfoList[iLayer];
900 :
901 2 : memset(psTileLayer, 0, sizeof(TileLayerInfo));
902 :
903 2 : return new AsciiTileLayer(this, iLayer, psBlockLayer, psTileLayer);
904 : }
905 :
906 : /************************************************************************/
907 : /* _DeleteLayer() */
908 : /************************************************************************/
909 :
910 : /**
911 : * Deletes the block layer with the specified index.
912 : *
913 : * @param iLayer The index of the block layer to delete.
914 : */
915 0 : void AsciiTileDir::_DeleteLayer(uint32 iLayer)
916 : {
917 : // Invalidate the block layer info.
918 0 : BlockLayerInfo * psBlockLayer = moLayerInfoList[iLayer];
919 :
920 0 : psBlockLayer->nLayerType = BLTDead;
921 0 : psBlockLayer->nBlockCount = 0;
922 0 : psBlockLayer->nLayerSize = 0;
923 :
924 : // Invalidate the tile layer info.
925 0 : TileLayerInfo * psTileLayer = moTileLayerInfoList[iLayer];
926 :
927 0 : memset(psTileLayer, 0, sizeof(TileLayerInfo));
928 0 : }
929 :
930 : /************************************************************************/
931 : /* GetDataSegmentName() */
932 : /************************************************************************/
933 2 : std::string AsciiTileDir::GetDataSegmentName(void) const
934 : {
935 2 : return "SysBData";
936 : }
937 :
938 : /************************************************************************/
939 : /* GetDataSegmentDesc() */
940 : /************************************************************************/
941 2 : std::string AsciiTileDir::GetDataSegmentDesc(void) const
942 : {
943 2 : return "Block Tile Data - Do not modify.";
944 : }
945 :
946 : /************************************************************************/
947 : /* ValidateNewBlocks() */
948 : /************************************************************************/
949 6 : void AsciiTileDir::ValidateNewBlocks(uint32 & nNewBlockCount, bool bFreeBlocks)
950 : {
951 6 : uint32 nLimitBlockCount = 99999999;
952 6 : uint32 nTotalBlockCount = GetLayerBlockCount() + GetFreeBlockCount();
953 :
954 6 : if (nTotalBlockCount >= nLimitBlockCount)
955 : {
956 0 : Sync(); // Make sure the directory is synchronized to disk.
957 :
958 0 : ThrowPCIDSKException("The file size limit has been reached.");
959 : }
960 :
961 6 : if (nTotalBlockCount + nNewBlockCount > nLimitBlockCount)
962 : {
963 0 : if (!bFreeBlocks)
964 : {
965 0 : Sync(); // Make sure the directory is synchronized to disk.
966 :
967 0 : ThrowPCIDSKException("The file size limit has been reached.");
968 : }
969 :
970 0 : nNewBlockCount = nLimitBlockCount - nTotalBlockCount;
971 : }
972 6 : }
|