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 : * 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/blockdir.h"
29 : #include "blockdir/blocklayer.h"
30 : #include "blockdir/blockfile.h"
31 : #include "core/pcidsk_utils.h"
32 : #include "pcidsk_exception.h"
33 : #include <sstream>
34 : #include <cstring>
35 : #include <cassert>
36 : #include <algorithm>
37 :
38 : using namespace PCIDSK;
39 :
40 : /************************************************************************/
41 : /* BlockDir() */
42 : /************************************************************************/
43 :
44 : /**
45 : * Constructor.
46 : *
47 : * @param poFile The associated file object.
48 : * @param nSegment The segment of the block directory.
49 : */
50 24 : BlockDir::BlockDir(BlockFile * poFile, uint16 nSegment)
51 : : mpoFile(poFile),
52 : mnSegment(nSegment),
53 : mnVersion(0),
54 24 : mchEndianness(BigEndianSystem() ? 'B' : 'L'),
55 : mbNeedsSwap(false),
56 : mnValidInfo(0),
57 : mbModified(false),
58 48 : mbOnDisk(true)
59 : {
60 24 : assert(poFile && nSegment != INVALID_SEGMENT);
61 :
62 24 : mpoFreeBlockLayer = nullptr;
63 24 : }
64 :
65 : /************************************************************************/
66 : /* BlockDir() */
67 : /************************************************************************/
68 :
69 : /**
70 : * Constructor.
71 : *
72 : * @param poFile The associated file object.
73 : * @param nSegment The segment of the block directory.
74 : * @param nVersion The version of the block directory.
75 : */
76 8 : BlockDir::BlockDir(BlockFile * poFile, uint16 nSegment, uint16 nVersion)
77 :
78 : : mpoFile(poFile),
79 : mnSegment(nSegment),
80 : mnVersion(nVersion),
81 8 : mchEndianness(BigEndianSystem() ? 'B' : 'L'),
82 : mbNeedsSwap(false),
83 : mnValidInfo(0),
84 : mbModified(true),
85 16 : mbOnDisk(false)
86 : {
87 8 : assert(poFile && nSegment != INVALID_SEGMENT);
88 :
89 8 : mpoFreeBlockLayer = nullptr;
90 8 : }
91 :
92 : /************************************************************************/
93 : /* ~BlockDir() */
94 : /************************************************************************/
95 :
96 : /**
97 : * Destructor.
98 : */
99 32 : BlockDir::~BlockDir(void)
100 : {
101 68 : for (size_t iLayer = 0; iLayer < moLayerList.size(); iLayer++)
102 36 : delete moLayerList[iLayer];
103 :
104 32 : delete mpoFreeBlockLayer;
105 :
106 32 : delete mpoFile;
107 32 : }
108 :
109 : /************************************************************************/
110 : /* Sync() */
111 : /************************************************************************/
112 :
113 : /**
114 : * Synchronizes the block directory to disk.
115 : */
116 50 : void BlockDir::Sync(void)
117 : {
118 50 : if (!mbModified)
119 42 : return;
120 :
121 8 : if (!mpoFile->GetUpdatable())
122 0 : return;
123 :
124 8 : if (!IsValid())
125 : {
126 0 : ThrowPCIDSKException("Failed to save: %s",
127 0 : mpoFile->GetFilename().c_str());
128 : }
129 :
130 8 : WriteDir();
131 :
132 8 : mbModified = false;
133 : }
134 :
135 : /************************************************************************/
136 : /* GetFile() */
137 : /************************************************************************/
138 :
139 : /**
140 : * Gets the associated file of the block directory.
141 : *
142 : * @return The associated file of the block directory.
143 : */
144 90 : BlockFile * BlockDir::GetFile(void) const
145 : {
146 90 : return mpoFile;
147 : }
148 :
149 : /************************************************************************/
150 : /* GetSegmentIndex() */
151 : /************************************************************************/
152 :
153 : /**
154 : * Gets the index of the block directory segment.
155 : *
156 : * @return The index of the block directory segment.
157 : */
158 0 : uint16 BlockDir::GetSegmentIndex(void) const
159 : {
160 0 : return mnSegment;
161 : }
162 :
163 : /************************************************************************/
164 : /* GetVersion() */
165 : /************************************************************************/
166 :
167 : /**
168 : * Gets the version of the block directory.
169 : *
170 : * @return The version of the block directory.
171 : */
172 0 : uint16 BlockDir::GetVersion(void) const
173 : {
174 0 : return mnVersion;
175 : }
176 :
177 : /************************************************************************/
178 : /* NeedsSwap() */
179 : /************************************************************************/
180 :
181 : /**
182 : * Checks if the block directory on disk needs swapping.
183 : *
184 : * @return If the block directory on disk needs swapping.
185 : */
186 19 : bool BlockDir::NeedsSwap(void) const
187 : {
188 19 : return mbNeedsSwap;
189 : }
190 :
191 : /************************************************************************/
192 : /* IsValid() */
193 : /************************************************************************/
194 :
195 : /**
196 : * Checks if the block directory is valid.
197 : *
198 : * @note The block directory is considered valid if the last
199 : * two bytes of the header are the same as when the header was read.
200 : *
201 : * @return If the block directory is valid.
202 : */
203 8 : bool BlockDir::IsValid(void) const
204 : {
205 8 : if (!mbOnDisk)
206 8 : return true;
207 :
208 : // Read the block directory header from disk.
209 : uint8 abyHeader[512];
210 :
211 0 : mpoFile->ReadFromSegment(mnSegment, abyHeader, 0, 512);
212 :
213 : // The last 2 bytes of the header are for the valid info.
214 : uint16 nValidInfo;
215 :
216 0 : memcpy(&nValidInfo, abyHeader + 512 - 2, 2);
217 :
218 0 : SwapValue(&nValidInfo);
219 :
220 : // Check if the valid info has changed since the last read.
221 0 : return nValidInfo == mnValidInfo;
222 : }
223 :
224 : /************************************************************************/
225 : /* IsModified() */
226 : /************************************************************************/
227 :
228 : /**
229 : * Checks if the block directory is modified.
230 : *
231 : * @return If the block directory is modified.
232 : */
233 0 : bool BlockDir::IsModified(void) const
234 : {
235 0 : return mbModified;
236 : }
237 :
238 : /************************************************************************/
239 : /* GetLayerCount() */
240 : /************************************************************************/
241 :
242 : /**
243 : * Gets the number of block layers.
244 : *
245 : * @return The number of block layers.
246 : */
247 0 : uint32 BlockDir::GetLayerCount(void) const
248 : {
249 0 : return (uint32) moLayerList.size();
250 : }
251 :
252 : /************************************************************************/
253 : /* GetLayerType() */
254 : /************************************************************************/
255 :
256 : /**
257 : * Gets the type of the block layer specified index.
258 : *
259 : * @param iLayer The index of the block layer.
260 : *
261 : * @return The type of the specified block layer.
262 : */
263 0 : uint16 BlockDir::GetLayerType(uint32 iLayer) const
264 : {
265 0 : if (iLayer >= moLayerList.size())
266 0 : return BLTDead;
267 :
268 0 : return moLayerList[iLayer]->GetLayerType();
269 : }
270 :
271 : /************************************************************************/
272 : /* GetLayerSize() */
273 : /************************************************************************/
274 :
275 : /**
276 : * Gets the size in bytes of the block layer specified index.
277 : *
278 : * @param iLayer The index of the block layer.
279 : *
280 : * @return The size in bytes of the block layer specified index.
281 : */
282 0 : uint64 BlockDir::GetLayerSize(uint32 iLayer) const
283 : {
284 0 : if (iLayer >= moLayerList.size())
285 0 : return 0;
286 :
287 0 : return moLayerList[iLayer]->GetLayerSize();
288 : }
289 :
290 : /************************************************************************/
291 : /* IsValid() */
292 : /************************************************************************/
293 :
294 : /**
295 : * Checks if the block layer at the specified index is valid.
296 : *
297 : * @param iLayer The index of the block layer.
298 : *
299 : * @return If the the specified block layer is valid.
300 : */
301 0 : bool BlockDir::IsLayerValid(uint32 iLayer) const
302 : {
303 0 : return GetLayerType(iLayer) != BLTDead;
304 : }
305 :
306 : /************************************************************************/
307 : /* GetLayer() */
308 : /************************************************************************/
309 :
310 : /**
311 : * Gets the block layer at the specified index.
312 : *
313 : * @param iLayer The index of the block layer.
314 : *
315 : * @return The block layer at the specified index.
316 : */
317 62 : BlockLayer * BlockDir::GetLayer(uint32 iLayer)
318 : {
319 62 : if (iLayer >= moLayerList.size())
320 0 : return nullptr;
321 :
322 62 : return moLayerList[iLayer];
323 : }
324 :
325 : /************************************************************************/
326 : /* CreateLayer() */
327 : /************************************************************************/
328 :
329 : /**
330 : * Creates a block layer of the specified type.
331 : *
332 : * @param nLayerType The type of the block layer to create.
333 : *
334 : * @return The index of the new block layer.
335 : */
336 8 : uint32 BlockDir::CreateLayer(int16 nLayerType)
337 : {
338 : // Try to find an invalid layer.
339 8 : uint32 nNewLayerIndex = INVALID_LAYER;
340 :
341 8 : for (size_t iLayer = 0; iLayer < moLayerList.size(); iLayer++)
342 : {
343 0 : if (!moLayerList[iLayer]->IsValid())
344 : {
345 0 : nNewLayerIndex = (uint32) iLayer;
346 :
347 0 : break;
348 : }
349 : }
350 :
351 8 : if (nNewLayerIndex == INVALID_LAYER)
352 : {
353 8 : nNewLayerIndex = (uint32) moLayerList.size();
354 :
355 : try
356 : {
357 8 : moLayerList.resize(moLayerList.size() + 1);
358 : }
359 0 : catch (const std::exception & ex)
360 : {
361 0 : return ThrowPCIDSKException(0, "Out of memory in BlockDir::CreateLayer(): %s", ex.what());
362 : }
363 : }
364 : else
365 : {
366 0 : delete moLayerList[nNewLayerIndex];
367 : }
368 :
369 : // Call the virtual method _CreateLayer() to create the layer.
370 8 : moLayerList[nNewLayerIndex] = _CreateLayer(nLayerType, nNewLayerIndex);
371 :
372 8 : mbModified = true;
373 :
374 8 : return nNewLayerIndex;
375 : }
376 :
377 : /************************************************************************/
378 : /* DeleteLayer() */
379 : /************************************************************************/
380 :
381 : /**
382 : * Deletes the block layer with the specified index.
383 : *
384 : * @param iLayer The index of the block layer to delete.
385 : */
386 0 : void BlockDir::DeleteLayer(uint32 iLayer)
387 : {
388 0 : BlockLayer * poLayer = GetLayer(iLayer);
389 :
390 0 : assert(poLayer && poLayer->IsValid());
391 0 : if (!poLayer || !poLayer->IsValid())
392 0 : return;
393 :
394 0 : poLayer->Resize(0);
395 :
396 : // Call the virtual method _DeleteLayer() to delete the layer.
397 0 : _DeleteLayer(iLayer);
398 :
399 0 : mbModified = true;
400 : }
401 :
402 : /************************************************************************/
403 : /* CreateNewBlocks() */
404 : /************************************************************************/
405 :
406 : /**
407 : * Creates the specified number of new blocks.
408 : *
409 : * @param nBlockCount The number of blocks to create.
410 : *
411 : * @return The specified number of new blocks.
412 : */
413 16 : BlockInfoList BlockDir::CreateNewBlocks(uint32 nBlockCount)
414 : {
415 16 : ValidateNewBlocks(nBlockCount, false);
416 :
417 16 : BlockInfoList oNewBlocks(nBlockCount);
418 :
419 16 : BlockInfoList::iterator oIter = oNewBlocks.begin();
420 16 : BlockInfoList::iterator oEnd = oNewBlocks.end();
421 :
422 32 : for (; oIter != oEnd; ++oIter)
423 : {
424 16 : oIter->nSegment = INVALID_SEGMENT;
425 16 : oIter->nStartBlock = INVALID_BLOCK;
426 : }
427 :
428 16 : mbModified = true;
429 :
430 32 : return oNewBlocks;
431 : }
432 :
433 : /************************************************************************/
434 : /* CreateFreeBlocks() */
435 : /************************************************************************/
436 :
437 : /**
438 : * Creates the specified number of free blocks.
439 : *
440 : * @note The new blocks are going to be added to the free block layer.
441 : *
442 : * @param nBlockCount The number of blocks to create.
443 : */
444 8 : void BlockDir::CreateFreeBlocks(uint32 nBlockCount)
445 : {
446 8 : if (!mpoFreeBlockLayer)
447 0 : ReadFreeBlockLayer();
448 :
449 8 : ValidateNewBlocks(nBlockCount, true);
450 :
451 8 : uint32 nBlockSize = GetBlockSize();
452 :
453 : uint16 nDataSegment =
454 8 : mpoFile->ExtendSegment(GetDataSegmentName(), GetDataSegmentDesc(),
455 8 : (uint64) nBlockCount * nBlockSize);
456 :
457 8 : uint64 nBlockOffset = mpoFile->GetSegmentSize(nDataSegment);
458 :
459 8 : assert(nBlockOffset % nBlockSize == 0);
460 :
461 : // Reverse the block list because GetFreeBlock() is LIFO.
462 8 : BlockInfoList oFreeBlockList;
463 :
464 8 : oFreeBlockList.reserve(nBlockCount);
465 :
466 136 : for (uint32 iBlock = 0; iBlock < nBlockCount; iBlock++)
467 : {
468 : BlockInfo sFreeBlock;
469 :
470 128 : nBlockOffset -= nBlockSize;
471 :
472 128 : sFreeBlock.nSegment = nDataSegment;
473 128 : sFreeBlock.nStartBlock = (uint32) (nBlockOffset / nBlockSize);
474 :
475 128 : oFreeBlockList.push_back(sFreeBlock);
476 : }
477 :
478 8 : mpoFreeBlockLayer->PushBlocks(oFreeBlockList);
479 :
480 8 : mbModified = true;
481 8 : }
482 :
483 : /************************************************************************/
484 : /* AddFreeBlocks() */
485 : /************************************************************************/
486 :
487 : /**
488 : * Adds the the specified block list to the free block layer.
489 : *
490 : * @note Only the blocks which are allocated will be added to the
491 : * free block layer.
492 : *
493 : * @param oBlockList The block list to add.
494 : */
495 0 : void BlockDir::AddFreeBlocks(const BlockInfoList & oBlockList)
496 : {
497 0 : if (!mpoFreeBlockLayer)
498 0 : ReadFreeBlockLayer();
499 :
500 0 : BlockInfoList oValidBlockList;
501 :
502 0 : oValidBlockList.reserve(oBlockList.size());
503 :
504 : // Reverse the block list because GetFreeBlock() is LIFO.
505 0 : BlockInfoList::const_reverse_iterator oIter = oBlockList.rbegin();
506 0 : BlockInfoList::const_reverse_iterator oEnd = oBlockList.rend();
507 :
508 0 : for (; oIter != oEnd; ++oIter)
509 : {
510 0 : if (oIter->nSegment != INVALID_SEGMENT &&
511 0 : oIter->nStartBlock != INVALID_BLOCK)
512 : {
513 0 : oValidBlockList.push_back(*oIter);
514 : }
515 : }
516 :
517 0 : mpoFreeBlockLayer->PushBlocks(oValidBlockList);
518 :
519 0 : mbModified = true;
520 0 : }
521 :
522 : /************************************************************************/
523 : /* GetFreeBlock() */
524 : /************************************************************************/
525 :
526 : /**
527 : * Gets a free block from the free block layer.
528 : *
529 : * @note The block will be removed from the free block layer.
530 : *
531 : * @return A free block from the free block layer.
532 : */
533 16 : BlockInfo BlockDir::GetFreeBlock(void)
534 : {
535 16 : if (!mpoFreeBlockLayer)
536 0 : ReadFreeBlockLayer();
537 :
538 : // If we need more free blocks, create a minimum of 16 blocks.
539 16 : if (mpoFreeBlockLayer->GetBlockCount() == 0)
540 8 : CreateFreeBlocks(std::max((uint32) 16, GetNewBlockCount()));
541 :
542 16 : if (mpoFreeBlockLayer->GetBlockCount() <= 0)
543 0 : ThrowPCIDSKException("Cannot create new blocks.");
544 :
545 : BlockInfo sFreeBlock;
546 16 : sFreeBlock.nSegment = INVALID_SEGMENT;
547 16 : sFreeBlock.nStartBlock = INVALID_BLOCK;
548 :
549 32 : const BlockInfoList & oFreeBlockList = mpoFreeBlockLayer->PopBlocks(1);
550 :
551 16 : assert(oFreeBlockList.size() == 1);
552 :
553 16 : if (!oFreeBlockList.empty())
554 16 : sFreeBlock = oFreeBlockList[0];
555 :
556 16 : mbModified = true;
557 :
558 32 : return sFreeBlock;
559 : }
560 :
561 : /************************************************************************/
562 : /* SwapValue() */
563 : /************************************************************************/
564 :
565 : /**
566 : * Swaps the specified value.
567 : *
568 : * @param pnValue The value to swap.
569 : */
570 32 : void BlockDir::SwapValue(uint16 * pnValue) const
571 : {
572 32 : if (!mbNeedsSwap)
573 32 : return;
574 :
575 0 : SwapData(pnValue, 2, 1);
576 : }
|