Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: mitab_rawbinblock.cpp
4 : * Project: MapInfo TAB Read/Write library
5 : * Language: C++
6 : * Purpose: Implementation of the TABRawBinBlock class used to handle
7 : * reading/writing blocks in the .MAP files
8 : * Author: Daniel Morissette, dmorissette@dmsolutions.ca
9 : *
10 : **********************************************************************
11 : * Copyright (c) 1999, 2000, Daniel Morissette
12 : * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
13 : *
14 : * SPDX-License-Identifier: MIT
15 : **********************************************************************/
16 :
17 : #include "cpl_port.h"
18 : #include "mitab.h"
19 :
20 : #include <cctype>
21 : #include <cstddef>
22 : #include <cstdio>
23 : #include <cstring>
24 : #include <algorithm>
25 :
26 : #include "cpl_conv.h"
27 : #include "cpl_error.h"
28 : #include "cpl_vsi.h"
29 : #include "mitab_priv.h"
30 :
31 : /*=====================================================================
32 : * class TABRawBinBlock
33 : *====================================================================*/
34 :
35 : /**********************************************************************
36 : * TABRawBinBlock::TABRawBinBlock()
37 : *
38 : * Constructor.
39 : **********************************************************************/
40 88856 : TABRawBinBlock::TABRawBinBlock(TABAccess eAccessMode /*= TABRead*/,
41 88856 : GBool bHardBlockSize /*= TRUE*/)
42 : : m_fp(nullptr), m_eAccess(eAccessMode), m_nBlockType(0),
43 : m_pabyBuf(nullptr), m_nBlockSize(0), m_nSizeUsed(0),
44 : m_bHardBlockSize(bHardBlockSize), m_nFileOffset(0), m_nCurPos(0),
45 88856 : m_nFirstBlockPtr(0), m_nFileSize(-1), m_bModified(FALSE)
46 : {
47 88856 : }
48 :
49 : /**********************************************************************
50 : * TABRawBinBlock::~TABRawBinBlock()
51 : *
52 : * Destructor.
53 : **********************************************************************/
54 182309 : TABRawBinBlock::~TABRawBinBlock()
55 : {
56 88856 : if (m_pabyBuf)
57 88832 : CPLFree(m_pabyBuf);
58 93453 : }
59 :
60 : /**********************************************************************
61 : * TABRawBinBlock::ReadFromFile()
62 : *
63 : * Load data from the specified file location and initialize the block.
64 : *
65 : * Returns 0 if successful or -1 if an error happened, in which case
66 : * CPLError() will have been called.
67 : **********************************************************************/
68 296539 : int TABRawBinBlock::ReadFromFile(VSILFILE *fpSrc, int nOffset, int nSize)
69 : {
70 296539 : if (fpSrc == nullptr || nSize == 0)
71 : {
72 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
73 : "TABRawBinBlock::ReadFromFile(): Assertion Failed!");
74 0 : return -1;
75 : }
76 :
77 296539 : m_fp = fpSrc;
78 :
79 296539 : VSIFSeekL(fpSrc, 0, SEEK_END);
80 296539 : m_nFileSize = static_cast<int>(VSIFTellL(m_fp));
81 :
82 296539 : m_nFileOffset = nOffset;
83 296539 : m_nCurPos = 0;
84 296539 : m_bModified = FALSE;
85 :
86 : /*----------------------------------------------------------------
87 : * Alloc a buffer to contain the data
88 : *---------------------------------------------------------------*/
89 296539 : GByte *pabyBuf = static_cast<GByte *>(CPLMalloc(nSize * sizeof(GByte)));
90 :
91 : /*----------------------------------------------------------------
92 : * Read from the file
93 : *---------------------------------------------------------------*/
94 296539 : if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 ||
95 296539 : (m_nSizeUsed = static_cast<int>(
96 593078 : VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc))) == 0 ||
97 296539 : (m_bHardBlockSize && m_nSizeUsed != nSize))
98 : {
99 0 : CPLError(CE_Failure, CPLE_FileIO,
100 : "ReadFromFile() failed reading %d bytes at offset %d.", nSize,
101 : nOffset);
102 0 : CPLFree(pabyBuf);
103 0 : return -1;
104 : }
105 :
106 : /*----------------------------------------------------------------
107 : * Init block with the data we just read
108 : *---------------------------------------------------------------*/
109 296539 : return InitBlockFromData(pabyBuf, nSize, m_nSizeUsed, FALSE, fpSrc,
110 296539 : nOffset);
111 : }
112 :
113 : /**********************************************************************
114 : * TABRawBinBlock::CommitToFile()
115 : *
116 : * Commit the current state of the binary block to the file to which
117 : * it has been previously attached.
118 : *
119 : * Derived classes may want to (optionally) reimplement this method if
120 : * they need to do special processing before committing the block to disk.
121 : *
122 : * For files created with bHardBlockSize=TRUE, a complete block of
123 : * the specified size is always written, otherwise only the number of
124 : * used bytes in the block will be written to disk.
125 : *
126 : * Returns 0 if successful or -1 if an error happened, in which case
127 : * CPLError() will have been called.
128 : **********************************************************************/
129 52967 : int TABRawBinBlock::CommitToFile()
130 : {
131 52967 : int nStatus = 0;
132 :
133 52967 : if (m_fp == nullptr || m_nBlockSize <= 0 || m_pabyBuf == nullptr ||
134 52967 : m_nFileOffset < 0)
135 : {
136 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
137 : "TABRawBinBlock::CommitToFile(): Block has not been "
138 : "initialized yet!");
139 0 : return -1;
140 : }
141 :
142 : /*----------------------------------------------------------------
143 : * If block has not been modified, then just return... nothing to do.
144 : *---------------------------------------------------------------*/
145 52967 : if (!m_bModified)
146 7186 : return 0;
147 :
148 : /*----------------------------------------------------------------
149 : * Move the output file pointer to the right position...
150 : *---------------------------------------------------------------*/
151 45781 : if (VSIFSeekL(m_fp, m_nFileOffset, SEEK_SET) != 0)
152 : {
153 : /*------------------------------------------------------------
154 : * Moving pointer failed... we may need to pad with zeros if
155 : * block destination is beyond current end of file.
156 : *-----------------------------------------------------------*/
157 0 : int nCurPos = static_cast<int>(VSIFTellL(m_fp));
158 :
159 0 : if (nCurPos < m_nFileOffset && VSIFSeekL(m_fp, 0L, SEEK_END) == 0 &&
160 0 : (nCurPos = static_cast<int>(VSIFTellL(m_fp))) < m_nFileOffset)
161 : {
162 0 : const GByte cZero = 0;
163 :
164 0 : while (nCurPos < m_nFileOffset && nStatus == 0)
165 : {
166 0 : if (VSIFWriteL(&cZero, 1, 1, m_fp) != 1)
167 : {
168 0 : CPLError(CE_Failure, CPLE_FileIO,
169 : "Failed writing 1 byte at offset %d.", nCurPos);
170 0 : nStatus = -1;
171 0 : break;
172 : }
173 0 : nCurPos++;
174 : }
175 : }
176 :
177 0 : if (nCurPos != m_nFileOffset)
178 0 : nStatus = -1; // Error message will follow below
179 : }
180 :
181 : /*----------------------------------------------------------------
182 : * At this point we are ready to write to the file.
183 : *
184 : * If m_bHardBlockSize==FALSE, then we do not write a complete block;
185 : * we write only the part of the block that was used.
186 : *---------------------------------------------------------------*/
187 45781 : int numBytesToWrite = m_bHardBlockSize ? m_nBlockSize : m_nSizeUsed;
188 :
189 : /*CPLDebug("MITAB", "Committing to offset %d", m_nFileOffset);*/
190 :
191 91562 : if (nStatus != 0 ||
192 45781 : VSIFWriteL(m_pabyBuf, sizeof(GByte), numBytesToWrite, m_fp) !=
193 45781 : static_cast<size_t>(numBytesToWrite))
194 : {
195 10 : CPLError(CE_Failure, CPLE_FileIO,
196 : "Failed writing %d bytes at offset %d.", numBytesToWrite,
197 : m_nFileOffset);
198 10 : return -1;
199 : }
200 45771 : if (m_nFileOffset + numBytesToWrite > m_nFileSize)
201 : {
202 31244 : m_nFileSize = m_nFileOffset + numBytesToWrite;
203 : }
204 :
205 45771 : VSIFFlushL(m_fp);
206 :
207 45771 : m_bModified = FALSE;
208 :
209 45771 : return 0;
210 : }
211 :
212 : /**********************************************************************
213 : * TABRawBinBlock::CommitAsDeleted()
214 : *
215 : * Commit current block to file using block type 4 (garbage block)
216 : *
217 : * Returns 0 if successful or -1 if an error happened, in which case
218 : * CPLError() will have been called.
219 : **********************************************************************/
220 12 : int TABRawBinBlock::CommitAsDeleted(GInt32 nNextBlockPtr)
221 : {
222 12 : CPLErrorReset();
223 :
224 12 : if (m_pabyBuf == nullptr)
225 : {
226 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
227 : "CommitAsDeleted(): Block has not been initialized yet!");
228 0 : return -1;
229 : }
230 :
231 : /*-----------------------------------------------------------------
232 : * Create deleted block header
233 : *----------------------------------------------------------------*/
234 12 : GotoByteInBlock(0x000);
235 12 : WriteInt16(TABMAP_GARB_BLOCK); // Block type code
236 12 : WriteInt32(nNextBlockPtr);
237 :
238 12 : int nStatus = CPLGetLastErrorType() == CE_Failure ? -1 : 0;
239 :
240 : /*-----------------------------------------------------------------
241 : * OK, call the base class to write the block to disk.
242 : *----------------------------------------------------------------*/
243 12 : if (nStatus == 0)
244 : {
245 : #ifdef DEBUG_VERBOSE
246 : CPLDebug("MITAB", "Committing GARBAGE block to offset %d",
247 : m_nFileOffset);
248 : #endif
249 12 : nStatus = TABRawBinBlock::CommitToFile();
250 12 : m_nSizeUsed = 0;
251 : }
252 :
253 12 : return nStatus;
254 : }
255 :
256 : /**********************************************************************
257 : * TABRawBinBlock::InitBlockFromData()
258 : *
259 : * Set the binary data buffer and initialize the block.
260 : *
261 : * Calling ReadFromFile() will automatically call InitBlockFromData() to
262 : * complete the initialization of the block after the data is read from the
263 : * file. Derived classes should implement their own version of
264 : * InitBlockFromData() if they need specific initialization... in this
265 : * case the derived InitBlockFromData() should call
266 : * TABRawBinBlock::InitBlockFromData() before doing anything else.
267 : *
268 : * By default, the buffer will be copied, but if bMakeCopy = FALSE then
269 : * it won't be copied, and the object will keep a reference to the
270 : * user's buffer... and this object will eventually free the user's buffer.
271 : *
272 : * Returns 0 if successful or -1 if an error happened, in which case
273 : * CPLError() will have been called.
274 : **********************************************************************/
275 376470 : int TABRawBinBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
276 : int nSizeUsed,
277 : GBool bMakeCopy /* = TRUE */,
278 : VSILFILE *fpSrc /* = NULL */,
279 : int nOffset /* = 0 */)
280 : {
281 376470 : m_fp = fpSrc;
282 376470 : m_nFileOffset = nOffset;
283 376470 : m_nCurPos = 0;
284 376470 : m_bModified = FALSE;
285 :
286 : /*----------------------------------------------------------------
287 : * Alloc or realloc the buffer to contain the data if necessary
288 : *---------------------------------------------------------------*/
289 376470 : if (!bMakeCopy)
290 : {
291 376470 : if (m_pabyBuf != nullptr)
292 293816 : CPLFree(m_pabyBuf);
293 376470 : m_pabyBuf = pabyBuf;
294 376470 : m_nBlockSize = nBlockSize;
295 376470 : m_nSizeUsed = nSizeUsed;
296 : }
297 0 : else if (m_pabyBuf == nullptr || nBlockSize != m_nBlockSize)
298 : {
299 0 : m_pabyBuf = static_cast<GByte *>(
300 0 : CPLRealloc(m_pabyBuf, nBlockSize * sizeof(GByte)));
301 0 : m_nBlockSize = nBlockSize;
302 0 : m_nSizeUsed = nSizeUsed;
303 0 : memcpy(m_pabyBuf, pabyBuf, m_nSizeUsed);
304 : }
305 :
306 : /*----------------------------------------------------------------
307 : * Extract block type... header block (first block in a file) has
308 : * no block type, so we assign one by default.
309 : *---------------------------------------------------------------*/
310 376470 : if (m_nFileOffset == 0)
311 5613 : m_nBlockType = TABMAP_HEADER_BLOCK;
312 : else
313 : {
314 : // Block type will be validated only if GetBlockType() is called
315 370857 : m_nBlockType = static_cast<int>(m_pabyBuf[0]);
316 : }
317 :
318 376470 : return 0;
319 : }
320 :
321 : /**********************************************************************
322 : * TABRawBinBlock::InitNewBlock()
323 : *
324 : * Initialize the block so that it knows to which file is attached,
325 : * its block size, etc.
326 : *
327 : * This is an alternative to calling ReadFromFile() or InitBlockFromData()
328 : * that puts the block in a stable state without loading any initial
329 : * data in it.
330 : *
331 : * Returns 0 if successful or -1 if an error happened, in which case
332 : * CPLError() will have been called.
333 : **********************************************************************/
334 23101 : int TABRawBinBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
335 : int nFileOffset /* = 0*/)
336 : {
337 23101 : m_fp = fpSrc;
338 23101 : m_nBlockSize = nBlockSize;
339 23101 : m_nSizeUsed = 0;
340 23101 : m_nCurPos = 0;
341 23101 : m_bModified = FALSE;
342 :
343 23101 : if (nFileOffset > 0)
344 17394 : m_nFileOffset = nFileOffset;
345 : else
346 5707 : m_nFileOffset = 0;
347 :
348 23101 : if (m_fp != nullptr && m_nFileSize < 0 && m_eAccess == TABReadWrite)
349 : {
350 4084 : int nCurPos = static_cast<int>(VSIFTellL(m_fp));
351 4084 : VSIFSeekL(fpSrc, 0, SEEK_END);
352 4084 : m_nFileSize = static_cast<int>(VSIFTellL(m_fp));
353 4084 : VSIFSeekL(fpSrc, nCurPos, SEEK_SET);
354 : }
355 :
356 23101 : m_nBlockType = -1;
357 :
358 23101 : m_pabyBuf = static_cast<GByte *>(
359 23101 : CPLRealloc(m_pabyBuf, m_nBlockSize * sizeof(GByte)));
360 23101 : if (m_nBlockSize)
361 23074 : memset(m_pabyBuf, 0, m_nBlockSize);
362 :
363 23101 : return 0;
364 : }
365 :
366 : /**********************************************************************
367 : * TABRawBinBlock::GetBlockType()
368 : *
369 : * Return the block type for the current object.
370 : *
371 : * Returns a block type >= 0 if successful or -1 if an error happened, in
372 : * which case CPLError() will have been called.
373 : **********************************************************************/
374 103778 : int TABRawBinBlock::GetBlockType()
375 : {
376 103778 : if (m_pabyBuf == nullptr)
377 : {
378 0 : CPLError(CE_Failure, CPLE_AppDefined,
379 : "GetBlockType(): Block has not been initialized.");
380 0 : return -1;
381 : }
382 :
383 103778 : if (m_nBlockType > TABMAP_LAST_VALID_BLOCK_TYPE)
384 : {
385 0 : CPLError(CE_Failure, CPLE_NotSupported,
386 : "GetBlockType(): Unsupported block type %d.", m_nBlockType);
387 0 : return -1;
388 : }
389 :
390 103778 : return m_nBlockType;
391 : }
392 :
393 : /**********************************************************************
394 : * TABRawBinBlock::GotoByteInBlock()
395 : *
396 : * Move the block pointer to the specified position relative to the
397 : * beginning of the block.
398 : *
399 : * Returns 0 if successful or -1 if an error happened, in which case
400 : * CPLError() will have been called.
401 : **********************************************************************/
402 638015 : int TABRawBinBlock::GotoByteInBlock(int nOffset)
403 : {
404 638015 : if ((m_eAccess == TABRead && nOffset > m_nSizeUsed) ||
405 638015 : (m_eAccess != TABRead && nOffset > m_nBlockSize))
406 : {
407 0 : CPLError(CE_Failure, CPLE_AppDefined,
408 : "GotoByteInBlock(): Attempt to go past end of data block.");
409 0 : return -1;
410 : }
411 :
412 638015 : if (nOffset < 0)
413 : {
414 0 : CPLError(
415 : CE_Failure, CPLE_AppDefined,
416 : "GotoByteInBlock(): Attempt to go before start of data block.");
417 0 : return -1;
418 : }
419 :
420 638015 : m_nCurPos = nOffset;
421 :
422 638015 : m_nSizeUsed = std::max(m_nSizeUsed, m_nCurPos);
423 :
424 638015 : return 0;
425 : }
426 :
427 : /**********************************************************************
428 : * TABRawBinBlock::GotoByteRel()
429 : *
430 : * Move the block pointer by the specified number of bytes relative
431 : * to its current position.
432 : *
433 : * Returns 0 if successful or -1 if an error happened, in which case
434 : * CPLError() will have been called.
435 : **********************************************************************/
436 43 : int TABRawBinBlock::GotoByteRel(int nOffset)
437 : {
438 43 : return GotoByteInBlock(m_nCurPos + nOffset);
439 : }
440 :
441 : /**********************************************************************
442 : * TABRawBinBlock::GotoByteInFile()
443 : *
444 : * Move the block pointer to the specified position relative to the
445 : * beginning of the file.
446 : *
447 : * In read access, the current block may be reloaded to contain a right
448 : * block of binary data if necessary.
449 : *
450 : * In write mode, the current block may automagically be committed to
451 : * disk and a new block initialized if necessary.
452 : *
453 : * bForceReadFromFile is used in write mode to read the new block data from
454 : * file instead of creating an empty block. (Useful for TABCollection
455 : * or other cases that need to do random access in the file in write mode.)
456 : *
457 : * bOffsetIsEndOfData is set to TRUE to indicate that the nOffset
458 : * to which we are attempting to go is the end of the used data in this
459 : * block (we are positioning ourselves to append data), so if the nOffset
460 : * corresponds to the beginning of a block then we should really
461 : * be positioning ourselves at the end of the block that ends at this
462 : * address instead of at the beginning of the blocks that starts at this
463 : * address. This case can happen when going back and forth to write collection
464 : * objects to a Coordblock and is documented in bug 1657.
465 : *
466 : * Returns 0 if successful or -1 if an error happened, in which case
467 : * CPLError() will have been called.
468 : **********************************************************************/
469 1636880 : int TABRawBinBlock::GotoByteInFile(int nOffset,
470 : GBool bForceReadFromFile /*=FALSE*/,
471 : GBool bOffsetIsEndOfData /*=FALSE*/)
472 : {
473 1636880 : if (nOffset < 0)
474 : {
475 0 : CPLError(CE_Failure, CPLE_AppDefined,
476 : "GotoByteInFile(): Attempt to go before start of file.");
477 0 : return -1;
478 : }
479 :
480 1636880 : int nNewBlockPtr =
481 1636880 : ((nOffset - m_nFirstBlockPtr) / m_nBlockSize) * m_nBlockSize +
482 1636880 : m_nFirstBlockPtr;
483 :
484 1636880 : if (m_eAccess == TABRead)
485 : {
486 4613000 : if ((nOffset < m_nFileOffset ||
487 1821540 : nOffset >= m_nFileOffset + m_nSizeUsed) &&
488 277067 : ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)
489 : {
490 : // Failed reading new block... error has already been reported.
491 0 : return -1;
492 : }
493 : }
494 92405 : else if (m_eAccess == TABWrite)
495 : {
496 10359 : if ((nOffset < m_nFileOffset ||
497 3453 : nOffset >= m_nFileOffset + m_nBlockSize) &&
498 0 : (CommitToFile() != 0 ||
499 0 : InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0))
500 : {
501 : // Failed reading new block... error has already been reported.
502 0 : return -1;
503 : }
504 : }
505 88952 : else if (m_eAccess == TABReadWrite)
506 : {
507 : // TODO: THIS IS NOT REAL read/write access (it is more extended write)
508 : // Currently we try to read from file only if explicitly requested.
509 : // If we ever want true read/write mode we should implement
510 : // more smarts to detect whether the caller wants an existing block to
511 : // be read, or a new one to be created from scratch.
512 : // CommitToFile() should only be called only if something changed.
513 : //
514 88952 : if (bOffsetIsEndOfData && nOffset % m_nBlockSize == 0)
515 : {
516 : /* We're trying to go byte m_nBlockSize of a block that's full of
517 : * data. In this case it is okay to place the m_nCurPos at byte
518 : * m_nBlockSize which is past the end of the block.
519 : */
520 :
521 : /* Make sure we request the block that ends with requested
522 : * address and not the following block that doesn't exist
523 : * yet on disk */
524 0 : nNewBlockPtr -= m_nBlockSize;
525 :
526 0 : if ((nOffset < m_nFileOffset ||
527 0 : nOffset > m_nFileOffset + m_nBlockSize) &&
528 0 : (CommitToFile() != 0 ||
529 0 : (!bForceReadFromFile &&
530 0 : InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
531 0 : (bForceReadFromFile &&
532 0 : ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)))
533 : {
534 : // Failed reading new block... error has already been reported.
535 0 : return -1;
536 : }
537 : }
538 : else
539 : {
540 88952 : if (!bForceReadFromFile && m_nFileSize > 0 && nOffset < m_nFileSize)
541 : {
542 20844 : bForceReadFromFile = TRUE;
543 20844 : if (!(nOffset < m_nFileOffset ||
544 20751 : nOffset >= m_nFileOffset + m_nBlockSize))
545 : {
546 20477 : if ((nOffset >= m_nFileOffset + m_nSizeUsed) &&
547 4559 : (CommitToFile() != 0 ||
548 4559 : ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0))
549 : {
550 : // Failed reading new block... error has already been
551 : // reported.
552 0 : return -1;
553 : }
554 : }
555 : }
556 :
557 265999 : if ((nOffset < m_nFileOffset ||
558 101465 : nOffset >= m_nFileOffset + m_nBlockSize) &&
559 12513 : (CommitToFile() != 0 ||
560 323 : (!bForceReadFromFile &&
561 12513 : InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
562 12190 : (bForceReadFromFile &&
563 12190 : ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)))
564 : {
565 : // Failed reading new block... error has already been reported.
566 0 : return -1;
567 : }
568 : }
569 : }
570 : else
571 : {
572 0 : CPLError(CE_Failure, CPLE_NotSupported,
573 : "Access mode not supported yet!");
574 0 : return -1;
575 : }
576 :
577 1636880 : m_nCurPos = nOffset - m_nFileOffset;
578 :
579 1636880 : m_nSizeUsed = std::max(m_nSizeUsed, m_nCurPos);
580 :
581 1636880 : return 0;
582 : }
583 :
584 : /**********************************************************************
585 : * TABRawBinBlock::SetFirstBlockPtr()
586 : *
587 : * Set the position in the file at which the first block starts.
588 : * This value will usually be the header size and needs to be specified
589 : * only if the header size is different from the other blocks size.
590 : *
591 : * This value will be used by GotoByteInFile() to properly align the data
592 : * blocks that it loads automatically when a requested position is outside
593 : * of the block currently in memory.
594 : **********************************************************************/
595 1495 : void TABRawBinBlock::SetFirstBlockPtr(int nOffset)
596 : {
597 1495 : m_nFirstBlockPtr = nOffset;
598 1495 : }
599 :
600 : /**********************************************************************
601 : * TABRawBinBlock::GetNumUnusedBytes()
602 : *
603 : * Return the number of unused bytes in this block.
604 : **********************************************************************/
605 44572 : int TABRawBinBlock::GetNumUnusedBytes()
606 : {
607 44572 : return m_nBlockSize - m_nSizeUsed;
608 : }
609 :
610 : /**********************************************************************
611 : * TABRawBinBlock::GetFirstUnusedByteOffset()
612 : *
613 : * Return the position of the first unused byte in this block relative
614 : * to the beginning of the file, or -1 if the block is full.
615 : **********************************************************************/
616 25890 : int TABRawBinBlock::GetFirstUnusedByteOffset()
617 : {
618 25890 : if (m_nSizeUsed < m_nBlockSize)
619 25890 : return m_nFileOffset + m_nSizeUsed;
620 : else
621 0 : return -1;
622 : }
623 :
624 : /**********************************************************************
625 : * TABRawBinBlock::GetCurAddress()
626 : *
627 : * Return the current pointer position, relative to beginning of file.
628 : **********************************************************************/
629 503 : int TABRawBinBlock::GetCurAddress()
630 : {
631 503 : return m_nFileOffset + m_nCurPos;
632 : }
633 :
634 : /**********************************************************************
635 : * TABRawBinBlock::ReadBytes()
636 : *
637 : * Copy the number of bytes from the data block's internal buffer to
638 : * the user's buffer pointed by pabyDstBuf.
639 : *
640 : * Passing pabyDstBuf = NULL will only move the read pointer by the
641 : * specified number of bytes as if the copy had happened... but it
642 : * won't crash.
643 : *
644 : * Returns 0 if successful or -1 if an error happened, in which case
645 : * CPLError() will have been called.
646 : **********************************************************************/
647 9139210 : int TABRawBinBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
648 : {
649 : /*----------------------------------------------------------------
650 : * Make sure block is initialized with Read access and that the
651 : * operation won't go beyond the buffer's size.
652 : *---------------------------------------------------------------*/
653 9139210 : if (m_pabyBuf == nullptr)
654 : {
655 0 : CPLError(CE_Failure, CPLE_AppDefined,
656 : "ReadBytes(): Block has not been initialized.");
657 0 : return -1;
658 : }
659 :
660 9139210 : if (m_nCurPos + numBytes > m_nSizeUsed)
661 : {
662 0 : CPLError(CE_Failure, CPLE_AppDefined,
663 : "ReadBytes(): Attempt to read past end of data block.");
664 0 : return -1;
665 : }
666 :
667 9139210 : if (pabyDstBuf)
668 : {
669 9139210 : memcpy(pabyDstBuf, m_pabyBuf + m_nCurPos, numBytes);
670 : }
671 :
672 9139210 : m_nCurPos += numBytes;
673 :
674 9139210 : return 0;
675 : }
676 :
677 : /**********************************************************************
678 : * TABRawBinBlock::Read<datatype>()
679 : *
680 : * MapInfo files are binary files with LSB first (Intel) byte
681 : * ordering. The following functions will read from the input file
682 : * and return a value with the bytes ordered properly for the current
683 : * platform.
684 : **********************************************************************/
685 2476870 : GByte TABRawBinBlock::ReadByte()
686 : {
687 2476870 : GByte byValue = 0;
688 :
689 2476870 : ReadBytes(1, &byValue);
690 :
691 2476870 : return byValue;
692 : }
693 :
694 178074 : GInt16 TABRawBinBlock::ReadInt16()
695 : {
696 178074 : GInt16 n16Value = 0;
697 :
698 178074 : ReadBytes(2, reinterpret_cast<GByte *>(&n16Value));
699 :
700 : #ifdef CPL_MSB
701 : return static_cast<GInt16>(CPL_SWAP16(n16Value));
702 : #else
703 178074 : return n16Value;
704 : #endif
705 : }
706 :
707 6423030 : GInt32 TABRawBinBlock::ReadInt32()
708 : {
709 6423030 : GInt32 n32Value = 0;
710 :
711 6423030 : ReadBytes(4, reinterpret_cast<GByte *>(&n32Value));
712 :
713 : #ifdef CPL_MSB
714 : return static_cast<GInt32>(CPL_SWAP32(n32Value));
715 : #else
716 6423030 : return n32Value;
717 : #endif
718 : }
719 :
720 6 : GInt64 TABRawBinBlock::ReadInt64()
721 : {
722 6 : GInt64 n64Value = 0;
723 :
724 6 : ReadBytes(8, reinterpret_cast<GByte *>(&n64Value));
725 :
726 : #ifdef CPL_MSB
727 : CPL_LSBPTR64(&n64Value);
728 : #endif
729 6 : return n64Value;
730 : }
731 :
732 0 : float TABRawBinBlock::ReadFloat()
733 : {
734 0 : float fValue = 0.0f;
735 :
736 0 : ReadBytes(4, reinterpret_cast<GByte *>(&fValue));
737 :
738 : #ifdef CPL_MSB
739 : CPL_LSBPTR32(&fValue);
740 : #endif
741 0 : return fValue;
742 : }
743 :
744 50694 : double TABRawBinBlock::ReadDouble()
745 : {
746 50694 : double dValue = 0.0;
747 :
748 50694 : ReadBytes(8, reinterpret_cast<GByte *>(&dValue));
749 :
750 : #ifdef CPL_MSB
751 : CPL_SWAPDOUBLE(&dValue);
752 : #endif
753 :
754 50694 : return dValue;
755 : }
756 :
757 : /**********************************************************************
758 : * TABRawBinBlock::WriteBytes()
759 : *
760 : * Copy the number of bytes from the user's buffer pointed by pabySrcBuf
761 : * to the data block's internal buffer.
762 : * Note that this call only writes to the memory buffer... nothing is
763 : * written to the file until WriteToFile() is called.
764 : *
765 : * Passing pabySrcBuf = NULL will only move the write pointer by the
766 : * specified number of bytes as if the copy had happened... but it
767 : * won't crash.
768 : *
769 : * Returns 0 if successful or -1 if an error happened, in which case
770 : * CPLError() will have been called.
771 : **********************************************************************/
772 590090 : int TABRawBinBlock::WriteBytes(int nBytesToWrite, const GByte *pabySrcBuf)
773 : {
774 : /*----------------------------------------------------------------
775 : * Make sure block is initialized with Write access and that the
776 : * operation won't go beyond the buffer's size.
777 : *---------------------------------------------------------------*/
778 590090 : if (m_pabyBuf == nullptr)
779 : {
780 0 : CPLError(CE_Failure, CPLE_AppDefined,
781 : "WriteBytes(): Block has not been initialized.");
782 0 : return -1;
783 : }
784 :
785 590090 : if (m_eAccess == TABRead)
786 : {
787 0 : CPLError(CE_Failure, CPLE_AppDefined,
788 : "WriteBytes(): Block does not support write operations.");
789 0 : return -1;
790 : }
791 :
792 590090 : if (m_nCurPos + nBytesToWrite > m_nBlockSize)
793 : {
794 0 : CPLError(CE_Failure, CPLE_AppDefined,
795 : "WriteBytes(): Attempt to write past end of data block.");
796 0 : return -1;
797 : }
798 :
799 : /*----------------------------------------------------------------
800 : * Everything is OK... copy the data
801 : *---------------------------------------------------------------*/
802 590090 : if (pabySrcBuf)
803 : {
804 590090 : memcpy(m_pabyBuf + m_nCurPos, pabySrcBuf, nBytesToWrite);
805 : }
806 :
807 590090 : m_nCurPos += nBytesToWrite;
808 :
809 590090 : m_nSizeUsed = std::max(m_nSizeUsed, m_nCurPos);
810 :
811 590090 : m_bModified = TRUE;
812 :
813 590090 : return 0;
814 : }
815 :
816 : /**********************************************************************
817 : * TABRawBinBlock::Write<datatype>()
818 : *
819 : * Arc/Info files are binary files with MSB first (Motorola) byte
820 : * ordering. The following functions will reorder the byte for the
821 : * value properly and write that to the output file.
822 : *
823 : * If a problem happens, then CPLError() will be called and
824 : * CPLGetLastErrNo() can be used to test if a write operation was
825 : * successful.
826 : **********************************************************************/
827 118504 : int TABRawBinBlock::WriteByte(GByte byValue)
828 : {
829 118504 : return WriteBytes(1, &byValue);
830 : }
831 :
832 65389 : int TABRawBinBlock::WriteInt16(GInt16 n16Value)
833 : {
834 : #ifdef CPL_MSB
835 : n16Value = static_cast<GInt16>(CPL_SWAP16(n16Value));
836 : #endif
837 :
838 65389 : return WriteBytes(2, reinterpret_cast<GByte *>(&n16Value));
839 : }
840 :
841 335796 : int TABRawBinBlock::WriteInt32(GInt32 n32Value)
842 : {
843 : #ifdef CPL_MSB
844 : n32Value = static_cast<GInt32>(CPL_SWAP32(n32Value));
845 : #endif
846 :
847 335796 : return WriteBytes(4, reinterpret_cast<GByte *>(&n32Value));
848 : }
849 :
850 2 : int TABRawBinBlock::WriteInt64(GInt64 n64Value)
851 : {
852 : #ifdef CPL_MSB
853 : CPL_SWAP64PTR(&n64Value);
854 : #endif
855 :
856 2 : return WriteBytes(8, reinterpret_cast<GByte *>(&n64Value));
857 : }
858 :
859 160 : int TABRawBinBlock::WriteFloat(float fValue)
860 : {
861 : #ifdef CPL_MSB
862 : CPL_LSBPTR32(&fValue);
863 : #endif
864 :
865 160 : return WriteBytes(4, reinterpret_cast<GByte *>(&fValue));
866 : }
867 :
868 23121 : int TABRawBinBlock::WriteDouble(double dValue)
869 : {
870 : #ifdef CPL_MSB
871 : CPL_SWAPDOUBLE(&dValue);
872 : #endif
873 :
874 23121 : return WriteBytes(8, reinterpret_cast<GByte *>(&dValue));
875 : }
876 :
877 : /**********************************************************************
878 : * TABRawBinBlock::WriteZeros()
879 : *
880 : * Write a number of zeros (specified in bytes) at the current position
881 : * in the file.
882 : *
883 : * If a problem happens, then CPLError() will be called and
884 : * CPLGetLastErrNo() can be used to test if a write operation was
885 : * successful.
886 : **********************************************************************/
887 8776 : int TABRawBinBlock::WriteZeros(int nBytesToWrite)
888 : {
889 8776 : const GByte acZeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
890 8776 : int nStatus = 0;
891 :
892 : // Write by 8 bytes chunks. The last chunk may be less than 8 bytes.
893 51740 : for (int i = 0; nStatus == 0 && i < nBytesToWrite; i += 8)
894 : {
895 42964 : nStatus = WriteBytes(std::min(8, nBytesToWrite - i), acZeros);
896 : }
897 :
898 8776 : return nStatus;
899 : }
900 :
901 : /**********************************************************************
902 : * TABRawBinBlock::WritePaddedString()
903 : *
904 : * Write a string and pad the end of the field (up to nFieldSize) with
905 : * spaces number of spaces at the current position in the file.
906 : *
907 : * If a problem happens, then CPLError() will be called and
908 : * CPLGetLastErrNo() can be used to test if a write operation was
909 : * successful.
910 : **********************************************************************/
911 0 : int TABRawBinBlock::WritePaddedString(int nFieldSize, const char *pszString)
912 : {
913 0 : char acSpaces[8] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
914 : int i, nLen, numSpaces;
915 0 : int nStatus = 0;
916 :
917 0 : nLen = static_cast<int>(strlen(pszString));
918 0 : nLen = std::min(nLen, nFieldSize);
919 0 : numSpaces = nFieldSize - nLen;
920 :
921 0 : if (nLen > 0)
922 0 : nStatus = WriteBytes(nLen, reinterpret_cast<const GByte *>(pszString));
923 :
924 : /* Write spaces by 8 bytes chunks. The last chunk may be less than 8 bytes
925 : */
926 0 : for (i = 0; nStatus == 0 && i < numSpaces; i += 8)
927 : {
928 0 : nStatus = WriteBytes(std::min(8, numSpaces - i),
929 0 : reinterpret_cast<GByte *>(acSpaces));
930 : }
931 :
932 0 : return nStatus;
933 : }
934 :
935 : /**********************************************************************
936 : * TABRawBinBlock::Dump()
937 : *
938 : * Dump block contents... available only in DEBUG mode.
939 : **********************************************************************/
940 : #ifdef DEBUG
941 :
942 0 : void TABRawBinBlock::Dump(FILE *fpOut /*=NULL*/)
943 : {
944 0 : if (fpOut == nullptr)
945 0 : fpOut = stdout;
946 :
947 0 : fprintf(fpOut, "----- TABRawBinBlock::Dump() -----\n");
948 0 : if (m_pabyBuf == nullptr)
949 : {
950 0 : fprintf(fpOut, "Block has not been initialized yet.");
951 : }
952 : else
953 : {
954 0 : if (m_nBlockType == TABMAP_GARB_BLOCK)
955 : {
956 0 : fprintf(fpOut, "Garbage Block (type %d) at offset %d.\n",
957 : m_nBlockType, m_nFileOffset);
958 0 : int nNextGarbageBlock = 0;
959 0 : memcpy(&nNextGarbageBlock, m_pabyBuf + 2, 4);
960 0 : CPL_LSBPTR32(&nNextGarbageBlock);
961 0 : fprintf(fpOut, " m_nNextGarbageBlock = %d\n",
962 : nNextGarbageBlock);
963 : }
964 : else
965 : {
966 0 : fprintf(fpOut,
967 : "Block (type %d) size=%d bytes at offset %d in file.\n",
968 : m_nBlockType, m_nBlockSize, m_nFileOffset);
969 0 : fprintf(fpOut, "Current pointer at byte %d\n", m_nCurPos);
970 : }
971 : }
972 :
973 0 : fflush(fpOut);
974 0 : }
975 :
976 : #endif // DEBUG
977 :
978 : /**********************************************************************
979 : * DumpBytes()
980 : *
981 : * Read and dump the contents of an Binary file.
982 : **********************************************************************/
983 0 : void TABRawBinBlock::DumpBytes(GInt32 nValue, int nOffset /*=0*/,
984 : FILE *fpOut /*=NULL*/)
985 : {
986 0 : float fValue = 0.0f;
987 0 : memcpy(&fValue, &nValue, 4);
988 :
989 : char achValue[4];
990 0 : memcpy(achValue, &nValue, 4);
991 :
992 0 : GInt16 n16Val1 = 0;
993 0 : memcpy(&n16Val1, achValue + 2, sizeof(GInt16));
994 0 : GInt16 n16Val2 = 0;
995 0 : memcpy(&n16Val2, achValue, sizeof(GInt16));
996 :
997 : /* For double precision values, we only use the first half
998 : * of the height bytes... and leave the other 4 bytes as zeros!
999 : * It's a bit of a hack, but it seems to be enough for the
1000 : * precision of the values we print!
1001 : */
1002 : #ifdef CPL_MSB
1003 : const GInt32 anVal[2] = {nValue, 0};
1004 : #else
1005 0 : const GInt32 anVal[2] = {0, nValue};
1006 : #endif
1007 0 : double dValue = 0.0;
1008 0 : memcpy(&dValue, anVal, 8);
1009 :
1010 0 : if (fpOut == nullptr)
1011 0 : fpOut = stdout;
1012 :
1013 0 : fprintf(fpOut, "%d\t0x%8.8x %-5d\t%-6d %-6d %5.3e d=%5.3e", nOffset,
1014 : nValue, nValue, n16Val1, n16Val2, fValue, dValue);
1015 :
1016 0 : fprintf(fpOut, "\t[%c%c%c%c]\n", isprint(achValue[0]) ? achValue[0] : '.',
1017 0 : isprint(achValue[1]) ? achValue[1] : '.',
1018 0 : isprint(achValue[2]) ? achValue[2] : '.',
1019 0 : isprint(achValue[3]) ? achValue[3] : '.');
1020 0 : }
1021 :
1022 : /**********************************************************************
1023 : * TABCreateMAPBlockFromFile()
1024 : *
1025 : * Load data from the specified file location and create and initialize
1026 : * a TABMAP*Block of the right type to handle it.
1027 : *
1028 : * Returns the new object if successful or NULL if an error happened, in
1029 : * which case CPLError() will have been called.
1030 : **********************************************************************/
1031 36017 : TABRawBinBlock *TABCreateMAPBlockFromFile(VSILFILE *fpSrc, int nOffset,
1032 : int nSize,
1033 : GBool bHardBlockSize /*= TRUE */,
1034 : TABAccess eAccessMode /*= TABRead*/)
1035 : {
1036 36017 : if (fpSrc == nullptr || nSize == 0)
1037 : {
1038 0 : CPLError(CE_Failure, CPLE_AssertionFailed,
1039 : "TABCreateMAPBlockFromFile(): Assertion Failed!");
1040 0 : return nullptr;
1041 : }
1042 :
1043 : /*----------------------------------------------------------------
1044 : * Alloc a buffer to contain the data
1045 : *---------------------------------------------------------------*/
1046 36017 : GByte *pabyBuf = static_cast<GByte *>(CPLMalloc(nSize * sizeof(GByte)));
1047 :
1048 : /*----------------------------------------------------------------
1049 : * Read from the file
1050 : *---------------------------------------------------------------*/
1051 72034 : if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 ||
1052 36017 : VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc) !=
1053 36017 : static_cast<unsigned int>(nSize))
1054 : {
1055 14 : CPLError(
1056 : CE_Failure, CPLE_FileIO,
1057 : "TABCreateMAPBlockFromFile() failed reading %d bytes at offset %d.",
1058 : nSize, nOffset);
1059 14 : CPLFree(pabyBuf);
1060 14 : return nullptr;
1061 : }
1062 :
1063 : /*----------------------------------------------------------------
1064 : * Create an object of the right type
1065 : * Header block is different: it does not start with the object
1066 : * type byte but it is always the first block in a file
1067 : *---------------------------------------------------------------*/
1068 36003 : TABRawBinBlock *poBlock = nullptr;
1069 :
1070 36003 : if (nOffset == 0)
1071 : {
1072 2636 : poBlock = new TABMAPHeaderBlock(eAccessMode);
1073 : }
1074 : else
1075 : {
1076 33367 : switch (pabyBuf[0])
1077 : {
1078 10594 : case TABMAP_INDEX_BLOCK:
1079 10594 : poBlock = new TABMAPIndexBlock(eAccessMode);
1080 10594 : break;
1081 22626 : case TABMAP_OBJECT_BLOCK:
1082 22626 : poBlock = new TABMAPObjectBlock(eAccessMode);
1083 22626 : break;
1084 145 : case TABMAP_COORD_BLOCK:
1085 145 : poBlock = new TABMAPCoordBlock(eAccessMode);
1086 145 : break;
1087 0 : case TABMAP_TOOL_BLOCK:
1088 0 : poBlock = new TABMAPToolBlock(eAccessMode);
1089 0 : break;
1090 2 : case TABMAP_GARB_BLOCK:
1091 : default:
1092 2 : poBlock = new TABRawBinBlock(eAccessMode, bHardBlockSize);
1093 2 : break;
1094 : }
1095 : }
1096 :
1097 : /*----------------------------------------------------------------
1098 : * Init new object with the data we just read
1099 : *---------------------------------------------------------------*/
1100 72006 : if (poBlock->InitBlockFromData(pabyBuf, nSize, nSize, FALSE, fpSrc,
1101 36003 : nOffset) != 0)
1102 : {
1103 : // Some error happened... and CPLError() has been called
1104 0 : delete poBlock;
1105 0 : poBlock = nullptr;
1106 : }
1107 :
1108 36003 : return poBlock;
1109 : }
1110 :
1111 : /*=====================================================================
1112 : * class TABBinBlockManager
1113 : *====================================================================*/
1114 :
1115 : /**********************************************************************
1116 : * TABBinBlockManager::TABBinBlockManager()
1117 : *
1118 : * Constructor.
1119 : **********************************************************************/
1120 1452 : TABBinBlockManager::TABBinBlockManager()
1121 : : m_nBlockSize(0), m_nLastAllocatedBlock(-1),
1122 1452 : m_psGarbageBlocksFirst(nullptr), m_psGarbageBlocksLast(nullptr)
1123 : {
1124 1452 : m_szName[0] = '\0';
1125 1452 : }
1126 :
1127 : /**********************************************************************
1128 : * TABBinBlockManager::~TABBinBlockManager()
1129 : *
1130 : * Destructor.
1131 : **********************************************************************/
1132 2904 : TABBinBlockManager::~TABBinBlockManager()
1133 : {
1134 1452 : Reset();
1135 1452 : }
1136 :
1137 : /**********************************************************************
1138 : * TABBinBlockManager::SetBlockSize()
1139 : **********************************************************************/
1140 1475 : void TABBinBlockManager::SetBlockSize(int nBlockSize)
1141 : {
1142 1475 : m_nBlockSize = nBlockSize;
1143 1475 : }
1144 :
1145 : /**********************************************************************
1146 : * TABBinBlockManager::SetName()
1147 : **********************************************************************/
1148 1452 : void TABBinBlockManager::SetName(const char *pszName)
1149 : {
1150 1452 : strncpy(m_szName, pszName, sizeof(m_szName));
1151 1452 : m_szName[sizeof(m_szName) - 1] = '\0';
1152 1452 : }
1153 :
1154 : /**********************************************************************
1155 : * TABBinBlockManager::AllocNewBlock()
1156 : *
1157 : * Returns and reserves the address of the next available block, either a
1158 : * brand new block at end of file, or recycle a garbage block if one is
1159 : * available.
1160 : **********************************************************************/
1161 841 : GInt32 TABBinBlockManager::AllocNewBlock(CPL_UNUSED const char *pszReason)
1162 : {
1163 : // Try to reuse garbage blocks first
1164 841 : if (GetFirstGarbageBlock() > 0)
1165 : {
1166 12 : int nRetValue = PopGarbageBlock();
1167 : #ifdef DEBUG_VERBOSE
1168 : CPLDebug("MITAB",
1169 : "AllocNewBlock(%s, %s) = %d (recycling garbage block)",
1170 : m_szName, pszReason, nRetValue);
1171 : #endif
1172 12 : return nRetValue;
1173 : }
1174 :
1175 : // ... or alloc a new block at EOF
1176 829 : if (m_nLastAllocatedBlock == -1)
1177 48 : m_nLastAllocatedBlock = 0;
1178 : else
1179 : {
1180 781 : CPLAssert(m_nBlockSize);
1181 781 : m_nLastAllocatedBlock += m_nBlockSize;
1182 : }
1183 :
1184 : #ifdef DEBUG_VERBOSE
1185 : CPLDebug("MITAB", "AllocNewBlock(%s, %s) = %d", m_szName, pszReason,
1186 : m_nLastAllocatedBlock);
1187 : #endif
1188 829 : return m_nLastAllocatedBlock;
1189 : }
1190 :
1191 : /**********************************************************************
1192 : * TABBinBlockManager::Reset()
1193 : *
1194 : **********************************************************************/
1195 2943 : void TABBinBlockManager::Reset()
1196 : {
1197 2943 : m_nLastAllocatedBlock = -1;
1198 :
1199 : // Flush list of garbage blocks
1200 2943 : while (m_psGarbageBlocksFirst != nullptr)
1201 : {
1202 0 : TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext;
1203 0 : CPLFree(m_psGarbageBlocksFirst);
1204 0 : m_psGarbageBlocksFirst = psNext;
1205 : }
1206 2943 : m_psGarbageBlocksLast = nullptr;
1207 2943 : }
1208 :
1209 : /**********************************************************************
1210 : * TABBinBlockManager::PushGarbageBlockAsFirst()
1211 : *
1212 : * Insert a garbage block at the head of the list of garbage blocks.
1213 : **********************************************************************/
1214 12 : void TABBinBlockManager::PushGarbageBlockAsFirst(GInt32 nBlockPtr)
1215 : {
1216 : TABBlockRef *psNewBlockRef =
1217 12 : static_cast<TABBlockRef *>(CPLMalloc(sizeof(TABBlockRef)));
1218 :
1219 12 : psNewBlockRef->nBlockPtr = nBlockPtr;
1220 12 : psNewBlockRef->psPrev = nullptr;
1221 12 : psNewBlockRef->psNext = m_psGarbageBlocksFirst;
1222 :
1223 12 : if (m_psGarbageBlocksFirst != nullptr)
1224 0 : m_psGarbageBlocksFirst->psPrev = psNewBlockRef;
1225 12 : m_psGarbageBlocksFirst = psNewBlockRef;
1226 12 : if (m_psGarbageBlocksLast == nullptr)
1227 12 : m_psGarbageBlocksLast = m_psGarbageBlocksFirst;
1228 12 : }
1229 :
1230 : /**********************************************************************
1231 : * TABBinBlockManager::PushGarbageBlockAsLast()
1232 : *
1233 : * Insert a garbage block at the tail of the list of garbage blocks.
1234 : **********************************************************************/
1235 0 : void TABBinBlockManager::PushGarbageBlockAsLast(GInt32 nBlockPtr)
1236 : {
1237 : TABBlockRef *psNewBlockRef =
1238 0 : static_cast<TABBlockRef *>(CPLMalloc(sizeof(TABBlockRef)));
1239 :
1240 0 : psNewBlockRef->nBlockPtr = nBlockPtr;
1241 0 : psNewBlockRef->psPrev = m_psGarbageBlocksLast;
1242 0 : psNewBlockRef->psNext = nullptr;
1243 :
1244 0 : if (m_psGarbageBlocksLast != nullptr)
1245 0 : m_psGarbageBlocksLast->psNext = psNewBlockRef;
1246 0 : m_psGarbageBlocksLast = psNewBlockRef;
1247 0 : if (m_psGarbageBlocksFirst == nullptr)
1248 0 : m_psGarbageBlocksFirst = m_psGarbageBlocksLast;
1249 0 : }
1250 :
1251 : /**********************************************************************
1252 : * TABBinBlockManager::GetFirstGarbageBlock()
1253 : *
1254 : * Return address of the block at the head of the list of garbage blocks
1255 : * or 0 if the list is empty.
1256 : **********************************************************************/
1257 2054 : GInt32 TABBinBlockManager::GetFirstGarbageBlock()
1258 : {
1259 2054 : if (m_psGarbageBlocksFirst)
1260 12 : return m_psGarbageBlocksFirst->nBlockPtr;
1261 :
1262 2042 : return 0;
1263 : }
1264 :
1265 : /**********************************************************************
1266 : * TABBinBlockManager::PopGarbageBlock()
1267 : *
1268 : * Return address of the block at the head of the list of garbage blocks
1269 : * and remove that block from the list.
1270 : * Returns 0 if the list is empty.
1271 : **********************************************************************/
1272 12 : GInt32 TABBinBlockManager::PopGarbageBlock()
1273 : {
1274 12 : GInt32 nBlockPtr = 0;
1275 :
1276 12 : if (m_psGarbageBlocksFirst)
1277 : {
1278 12 : nBlockPtr = m_psGarbageBlocksFirst->nBlockPtr;
1279 12 : TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext;
1280 12 : CPLFree(m_psGarbageBlocksFirst);
1281 12 : if (psNext != nullptr)
1282 0 : psNext->psPrev = nullptr;
1283 : else
1284 12 : m_psGarbageBlocksLast = nullptr;
1285 12 : m_psGarbageBlocksFirst = psNext;
1286 : }
1287 :
1288 12 : return nBlockPtr;
1289 : }
|