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