Coverage Report

Created: 2025-12-31 08:30

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