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