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