• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.81
/ogr/ogrsf_frmts/mitab/mitab_mapindexblock.cpp
1
/**********************************************************************
2
 *
3
 * Name:     mitab_mapindexblock.cpp
4
 * Project:  MapInfo TAB Read/Write library
5
 * Language: C++
6
 * Purpose:  Implementation of the TABMAPIndexBlock class used to handle
7
 *           reading/writing of the .MAP files' index blocks
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 <cmath>
21
#include <cstdlib>
22
#include <cstring>
23

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 TABMAPIndexBlock
33
 *====================================================================*/
34

35
/**********************************************************************
36
 *                   TABMAPIndexBlock::TABMAPIndexBlock()
37
 *
38
 * Constructor.
39
 **********************************************************************/
40
TABMAPIndexBlock::TABMAPIndexBlock(TABAccess eAccessMode /*= TABRead*/)
35,431✔
41
    : TABRawBinBlock(eAccessMode, TRUE), m_numEntries(0), m_nMinX(1000000000),
42
      m_nMinY(1000000000), m_nMaxX(-1000000000), m_nMaxY(-1000000000),
43
      m_poBlockManagerRef(nullptr), m_nCurChildIndex(-1), m_poParentRef(nullptr)
35,431✔
44
{
45
    memset(m_asEntries, 0, sizeof(m_asEntries));
35,431✔
46
}
35,431✔
47

48
/**********************************************************************
49
 *                   TABMAPIndexBlock::~TABMAPIndexBlock()
50
 *
51
 * Destructor.
52
 **********************************************************************/
53
TABMAPIndexBlock::~TABMAPIndexBlock()
70,862✔
54
{
55
    UnsetCurChild();
35,431✔
56
}
70,862✔
57

58
/**********************************************************************
59
 *                   TABMAPIndexBlock::UnsetCurChild()
60
 **********************************************************************/
61

62
void TABMAPIndexBlock::UnsetCurChild()
89,614✔
63
{
64
    if (m_poCurChild)
89,614✔
65
    {
66
        if (m_eAccess == TABWrite || m_eAccess == TABReadWrite)
684✔
67
            m_poCurChild->CommitToFile();
684✔
68
        m_poCurChild.reset();
684✔
69
    }
70
    m_nCurChildIndex = -1;
89,614✔
71
}
89,614✔
72

73
/**********************************************************************
74
 *                   TABMAPIndexBlock::InitBlockFromData()
75
 *
76
 * Perform some initialization on the block after its binary data has
77
 * been set or changed (or loaded from a file).
78
 *
79
 * Returns 0 if successful or -1 if an error happened, in which case
80
 * CPLError() will have been called.
81
 **********************************************************************/
82
int TABMAPIndexBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
35,313✔
83
                                        int nSizeUsed,
84
                                        GBool bMakeCopy /* = TRUE */,
85
                                        VSILFILE *fpSrc /* = NULL */,
86
                                        int nOffset /* = 0 */)
87
{
88
    /*-----------------------------------------------------------------
89
     * First of all, we must call the base class' InitBlockFromData()
90
     *----------------------------------------------------------------*/
91
    const int nStatus = TABRawBinBlock::InitBlockFromData(
35,313✔
92
        pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
93
    if (nStatus != 0)
35,313✔
94
        return nStatus;
×
95

96
    /*-----------------------------------------------------------------
97
     * Validate block type
98
     *----------------------------------------------------------------*/
99
    if (m_nBlockType != TABMAP_INDEX_BLOCK)
35,313✔
100
    {
101
        CPLError(CE_Failure, CPLE_FileIO,
×
102
                 "InitBlockFromData(): Invalid Block Type: got %d expected %d",
103
                 m_nBlockType, TABMAP_INDEX_BLOCK);
104
        CPLFree(m_pabyBuf);
×
105
        m_pabyBuf = nullptr;
×
106
        return -1;
×
107
    }
108

109
    /*-----------------------------------------------------------------
110
     * Init member variables
111
     *----------------------------------------------------------------*/
112
    GotoByteInBlock(0x002);
35,313✔
113
    m_numEntries = ReadInt16();
35,313✔
114

115
    if (m_numEntries > 0)
35,313✔
116
        ReadAllEntries();
35,313✔
117

118
    return 0;
35,313✔
119
}
120

121
/**********************************************************************
122
 *                   TABMAPIndexBlock::CommitToFile()
123
 *
124
 * Commit the current state of the binary block to the file to which
125
 * it has been previously attached.
126
 *
127
 * This method makes sure all values are properly set in the map object
128
 * block header and then calls TABRawBinBlock::CommitToFile() to do
129
 * the actual writing to disk.
130
 *
131
 * Returns 0 if successful or -1 if an error happened, in which case
132
 * CPLError() will have been called.
133
 **********************************************************************/
134
int TABMAPIndexBlock::CommitToFile()
12,530✔
135
{
136
    if (m_pabyBuf == nullptr)
12,530✔
137
    {
138
        CPLError(CE_Failure, CPLE_AssertionFailed,
×
139
                 "CommitToFile(): Block has not been initialized yet!");
140
        return -1;
×
141
    }
142

143
    /*-----------------------------------------------------------------
144
     * Commit child first
145
     *----------------------------------------------------------------*/
146
    if (m_poCurChild)
12,530✔
147
    {
148
        if (m_poCurChild->CommitToFile() != 0)
687✔
149
            return -1;
×
150
    }
151

152
    /*-----------------------------------------------------------------
153
     * Nothing to do here if block has not been modified
154
     *----------------------------------------------------------------*/
155
    if (!m_bModified)
12,530✔
156
        return 0;
10,519✔
157

158
    /*-----------------------------------------------------------------
159
     * Make sure 4 bytes block header is up to date.
160
     *----------------------------------------------------------------*/
161
    GotoByteInBlock(0x000);
2,011✔
162

163
    WriteInt16(TABMAP_INDEX_BLOCK);  // Block type code
2,011✔
164
    WriteInt16(static_cast<GInt16>(m_numEntries));
2,011✔
165

166
    int nStatus = CPLGetLastErrorType() == CE_Failure ? -1 : 0;
2,011✔
167

168
    /*-----------------------------------------------------------------
169
     * Loop through all entries, writing each of them, and calling
170
     * CommitToFile() (recursively) on any child index entries we may
171
     * encounter.
172
     *----------------------------------------------------------------*/
173
    for (int i = 0; nStatus == 0 && i < m_numEntries; i++)
25,222✔
174
    {
175
        nStatus = WriteNextEntry(&(m_asEntries[i]));
23,211✔
176
    }
177

178
    /*-----------------------------------------------------------------
179
     * OK, call the base class to write the block to disk.
180
     *----------------------------------------------------------------*/
181
    if (nStatus == 0)
2,011✔
182
    {
183
#ifdef DEBUG_VERBOSE
184
        CPLDebug("MITAB", "Committing INDEX block to offset %d", m_nFileOffset);
185
#endif
186
        nStatus = TABRawBinBlock::CommitToFile();
2,011✔
187
    }
188

189
    return nStatus;
2,011✔
190
}
191

192
/**********************************************************************
193
 *                   TABMAPIndexBlock::InitNewBlock()
194
 *
195
 * Initialize a newly created block so that it knows to which file it
196
 * is attached, its block size, etc . and then perform any specific
197
 * initialization for this block type, including writing a default
198
 * block header, etc. and leave the block ready to receive data.
199
 *
200
 * This is an alternative to calling ReadFromFile() or InitBlockFromData()
201
 * that puts the block in a stable state without loading any initial
202
 * data in it.
203
 *
204
 * Returns 0 if successful or -1 if an error happened, in which case
205
 * CPLError() will have been called.
206
 **********************************************************************/
207
int TABMAPIndexBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
118✔
208
                                   int nFileOffset /* = 0*/)
209
{
210
    /*-----------------------------------------------------------------
211
     * Start with the default initialization
212
     *----------------------------------------------------------------*/
213
    if (TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
118✔
214
        return -1;
×
215

216
    /*-----------------------------------------------------------------
217
     * And then set default values for the block header.
218
     *----------------------------------------------------------------*/
219
    m_numEntries = 0;
118✔
220

221
    m_nMinX = 1000000000;
118✔
222
    m_nMinY = 1000000000;
118✔
223
    m_nMaxX = -1000000000;
118✔
224
    m_nMaxY = -1000000000;
118✔
225

226
    if (m_eAccess != TABRead && nFileOffset != 0)
118✔
227
    {
228
        GotoByteInBlock(0x000);
118✔
229

230
        WriteInt16(TABMAP_INDEX_BLOCK);  // Block type code
118✔
231
        WriteInt16(0);                   // num. index entries
118✔
232
    }
233

234
    if (CPLGetLastErrorType() == CE_Failure)
118✔
235
        return -1;
×
236

237
    return 0;
118✔
238
}
239

240
/**********************************************************************
241
 *                   TABMAPIndexBlock::ReadNextEntry()
242
 *
243
 * Read the next index entry from the block and fill the sEntry
244
 * structure.
245
 *
246
 * Returns 0 if successful or -1 if we reached the end of the block.
247
 **********************************************************************/
248
int TABMAPIndexBlock::ReadNextEntry(TABMAPIndexEntry *psEntry)
599,161✔
249
{
250
    if (m_nCurPos < 4)
599,161✔
251
        GotoByteInBlock(0x004);
×
252

253
    if (m_nCurPos > 4 + (20 * m_numEntries))
599,161✔
254
    {
255
        // End of BLock
256
        return -1;
×
257
    }
258

259
    psEntry->XMin = ReadInt32();
599,161✔
260
    psEntry->YMin = ReadInt32();
599,161✔
261
    psEntry->XMax = ReadInt32();
599,161✔
262
    psEntry->YMax = ReadInt32();
599,161✔
263
    psEntry->nBlockPtr = ReadInt32();
599,161✔
264

265
    if (CPLGetLastErrorType() == CE_Failure)
599,161✔
266
        return -1;
×
267

268
    return 0;
599,161✔
269
}
270

271
/**********************************************************************
272
 *                   TABMAPIndexBlock::ReadAllEntries()
273
 *
274
 * Init the block by reading all entries from the data block.
275
 *
276
 * Returns 0 if successful or -1 on error.
277
 **********************************************************************/
278
int TABMAPIndexBlock::ReadAllEntries()
35,313✔
279
{
280
    CPLAssert(m_numEntries <= GetMaxEntries());
35,313✔
281
    if (m_numEntries == 0)
35,313✔
282
        return 0;
×
283

284
    if (GotoByteInBlock(0x004) != 0)
35,313✔
285
        return -1;
×
286

287
    for (int i = 0; i < m_numEntries; i++)
634,474✔
288
    {
289
        if (ReadNextEntry(&(m_asEntries[i])) != 0)
599,161✔
290
            return -1;
×
291
    }
292

293
    return 0;
35,313✔
294
}
295

296
/**********************************************************************
297
 *                   TABMAPIndexBlock::WriteNextEntry()
298
 *
299
 * Write the sEntry index entry at current position in the block.
300
 *
301
 * Returns 0 if successful or -1 if we reached the end of the block.
302
 **********************************************************************/
303
int TABMAPIndexBlock::WriteNextEntry(TABMAPIndexEntry *psEntry)
23,211✔
304
{
305
    if (m_nCurPos < 4)
23,211✔
306
        GotoByteInBlock(0x004);
×
307

308
    WriteInt32(psEntry->XMin);
23,211✔
309
    WriteInt32(psEntry->YMin);
23,211✔
310
    WriteInt32(psEntry->XMax);
23,211✔
311
    WriteInt32(psEntry->YMax);
23,211✔
312
    WriteInt32(psEntry->nBlockPtr);
23,211✔
313

314
    if (CPLGetLastErrorType() == CE_Failure)
23,211✔
315
        return -1;
×
316

317
    return 0;
23,211✔
318
}
319

320
/**********************************************************************
321
 *                   TABMAPIndexBlock::GetNumFreeEntries()
322
 *
323
 * Return the number of available entries in this block.
324
 *
325
 * __TODO__ This function could eventually be improved to search
326
 *          children leaves as well.
327
 **********************************************************************/
328
int TABMAPIndexBlock::GetNumFreeEntries()
1,613✔
329
{
330
    return (m_nBlockSize - 4) / 20 - m_numEntries;
1,613✔
331
}
332

333
/**********************************************************************
334
 *                   TABMAPIndexBlock::GetEntry()
335
 *
336
 * Fetch a reference to the requested entry.
337
 *
338
 * @param iIndex index of entry, must be from 0 to GetNumEntries()-1.
339
 *
340
 * @return a reference to the internal copy of the entry, or NULL if out
341
 * of range.
342
 **********************************************************************/
343
TABMAPIndexEntry *TABMAPIndexBlock::GetEntry(int iIndex)
453,654✔
344
{
345
    if (iIndex < 0 || iIndex >= m_numEntries)
453,654✔
346
        return nullptr;
×
347

348
    return m_asEntries + iIndex;
453,654✔
349
}
350

351
/**********************************************************************
352
 *                   TABMAPIndexBlock::GetCurMaxDepth()
353
 *
354
 * Return maximum depth in the currently loaded part of the index tree
355
 **********************************************************************/
356
int TABMAPIndexBlock::GetCurMaxDepth()
2,207✔
357
{
358
    if (m_poCurChild)
2,207✔
359
        return m_poCurChild->GetCurMaxDepth() + 1;
576✔
360

361
    return 1; /* No current child... this node counts for one. */
1,631✔
362
}
363

364
/**********************************************************************
365
 *                   TABMAPIndexBlock::GetMBR()
366
 *
367
 * Return the MBR for the current block.
368
 **********************************************************************/
369
void TABMAPIndexBlock::GetMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
16,237✔
370
                              GInt32 &nYMax)
371
{
372
    nXMin = m_nMinX;
16,237✔
373
    nYMin = m_nMinY;
16,237✔
374
    nXMax = m_nMaxX;
16,237✔
375
    nYMax = m_nMaxY;
16,237✔
376
}
16,237✔
377

378
/**********************************************************************
379
 *                   TABMAPIndexBlock::SetMBR()
380
 *
381
 **********************************************************************/
382
void TABMAPIndexBlock::SetMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
1,044✔
383
                              GInt32 nYMax)
384
{
385
    m_nMinX = nXMin;
1,044✔
386
    m_nMinY = nYMin;
1,044✔
387
    m_nMaxX = nXMax;
1,044✔
388
    m_nMaxY = nYMax;
1,044✔
389
}
1,044✔
390

391
/**********************************************************************
392
 *                   TABMAPIndexBlock::InsertEntry()
393
 *
394
 * Add a new entry to this index block.  It is assumed that there is at
395
 * least one free slot available, so if the block has to be split then it
396
 * should have been done prior to calling this function.
397
 *
398
 * Returns 0 on success, -1 on error.
399
 **********************************************************************/
400
int TABMAPIndexBlock::InsertEntry(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
1,119✔
401
                                  GInt32 nYMax, GInt32 nBlockPtr)
402
{
403
    if (m_eAccess != TABWrite && m_eAccess != TABReadWrite)
1,119✔
404
    {
405
        CPLError(
×
406
            CE_Failure, CPLE_AssertionFailed,
407
            "Failed adding index entry: File not opened for write access.");
408
        return -1;
×
409
    }
410

411
    if (GetNumFreeEntries() < 1)
1,119✔
412
    {
413
        CPLError(CE_Failure, CPLE_AssertionFailed,
×
414
                 "Current Block Index is full, cannot add new entry.");
415
        return -1;
×
416
    }
417

418
    /*-----------------------------------------------------------------
419
     * Update count of entries and store new entry.
420
     *----------------------------------------------------------------*/
421
    m_numEntries++;
1,119✔
422
    CPLAssert(m_numEntries <= GetMaxEntries());
1,119✔
423

424
    m_asEntries[m_numEntries - 1].XMin = nXMin;
1,119✔
425
    m_asEntries[m_numEntries - 1].YMin = nYMin;
1,119✔
426
    m_asEntries[m_numEntries - 1].XMax = nXMax;
1,119✔
427
    m_asEntries[m_numEntries - 1].YMax = nYMax;
1,119✔
428
    m_asEntries[m_numEntries - 1].nBlockPtr = nBlockPtr;
1,119✔
429

430
    m_bModified = TRUE;
1,119✔
431

432
    return 0;
1,119✔
433
}
434

435
/**********************************************************************
436
 *                   TABMAPIndexBlock::ChooseSubEntryForInsert()
437
 *
438
 * Select the entry in this index block in which the new entry should
439
 * be inserted. The criteria used is to select the node whose MBR needs
440
 * the least enlargement to include the new entry. We resolve ties by
441
 * choosing the entry with the rectangle of smallest area.
442
 * (This is the ChooseSubtree part of Guttman's "ChooseLeaf" algorithm.)
443
 *
444
 * Returns the index of the best candidate or -1 of node is empty.
445
 **********************************************************************/
446
int TABMAPIndexBlock::ChooseSubEntryForInsert(GInt32 nXMin, GInt32 nYMin,
22,591✔
447
                                              GInt32 nXMax, GInt32 nYMax)
448
{
449
    GInt32 nBestCandidate = -1;
22,591✔
450

451
    double dOptimalAreaDiff = 0.0;
22,591✔
452

453
    const double dNewEntryArea = MITAB_AREA(nXMin, nYMin, nXMax, nYMax);
22,591✔
454

455
    for (GInt32 i = 0; i < m_numEntries; i++)
387,690✔
456
    {
457
        double dAreaDiff = 0.0;
365,099✔
458
        const double dAreaBefore =
365,099✔
459
            MITAB_AREA(m_asEntries[i].XMin, m_asEntries[i].YMin,
365,099✔
460
                       m_asEntries[i].XMax, m_asEntries[i].YMax);
461

462
        /* Does this entry fully contain the new entry's MBR ?
463
         */
464
        const GBool bIsContained =
365,099✔
465
            nXMin >= m_asEntries[i].XMin && nYMin >= m_asEntries[i].YMin &&
247,688✔
466
            nXMax <= m_asEntries[i].XMax && nYMax <= m_asEntries[i].YMax;
612,787✔
467

468
        if (bIsContained)
365,099✔
469
        {
470
            /* If new entry is fully contained in this entry then
471
             * the area difference will be the difference between the area
472
             * of the entry to insert and the area of m_asEntries[i]
473
             *
474
             * The diff value is negative in this case.
475
             */
476
            dAreaDiff = dNewEntryArea - dAreaBefore;
31,234✔
477
        }
478
        else
479
        {
480
            /* Need to calculate the expanded MBR to calculate the area
481
             * difference.
482
             */
483
            GInt32 nXMin2 = std::min(m_asEntries[i].XMin, nXMin);
333,865✔
484
            GInt32 nYMin2 = std::min(m_asEntries[i].YMin, nYMin);
333,865✔
485
            GInt32 nXMax2 = std::max(m_asEntries[i].XMax, nXMax);
333,865✔
486
            GInt32 nYMax2 = std::max(m_asEntries[i].YMax, nYMax);
333,865✔
487

488
            dAreaDiff =
333,865✔
489
                MITAB_AREA(nXMin2, nYMin2, nXMax2, nYMax2) - dAreaBefore;
333,865✔
490
        }
491

492
        /* Is this a better candidate?
493
         * Note, possible Optimization: In case of tie, we could to pick the
494
         * candidate with the smallest area
495
         */
496

497
        if (/* No best candidate yet */
365,099✔
498
            (nBestCandidate == -1)
499
            /* or current candidate is contained and best candidate is not
500
               contained */
501
            || (dAreaDiff < 0 && dOptimalAreaDiff >= 0)
342,508✔
502
            /* or if both are either contained or not contained then use the one
503
             * with the smallest area diff, which means maximum coverage in the
504
             * case of contained rects, or minimum area increase when not
505
             * contained
506
             */
507
            || (((dOptimalAreaDiff < 0 && dAreaDiff < 0) ||
860,916✔
508
                 (dOptimalAreaDiff > 0 && dAreaDiff > 0)) &&
304,303✔
509
                std::abs(dAreaDiff) < std::abs(dOptimalAreaDiff)))
153,256✔
510
        {
511
            nBestCandidate = i;
80,403✔
512
            dOptimalAreaDiff = dAreaDiff;
80,403✔
513
        }
514
    }
515

516
    return nBestCandidate;
22,591✔
517
}
518

519
/**********************************************************************
520
 *                   TABMAPIndexBlock::ChooseLeafForInsert()
521
 *
522
 * Recursively search the tree until we find the best leaf to
523
 * contain the specified object MBR.
524
 *
525
 * Returns the nBlockPtr of the selected leaf node entry (should be a
526
 * ref to a TABMAPObjectBlock) or -1 on error.
527
 *
528
 * After this call, m_poCurChild will be pointing at the selected child
529
 * node, for use by later calls to UpdateLeafEntry()
530
 **********************************************************************/
531
GInt32 TABMAPIndexBlock::ChooseLeafForInsert(GInt32 nXMin, GInt32 nYMin,
21,970✔
532
                                             GInt32 nXMax, GInt32 nYMax)
533
{
534
    GBool bFound = FALSE;
21,970✔
535

536
    if (m_numEntries < 0)
21,970✔
537
        return -1;
×
538

539
    /*-----------------------------------------------------------------
540
     * Look for the best candidate to contain the new entry
541
     *----------------------------------------------------------------*/
542

543
    // Make sure blocks currently in memory are written to disk.
544
    // TODO: Could we avoid deleting m_poCurChild if it is already
545
    //       the best candidate for insert?
546
    if (m_poCurChild)
21,970✔
547
    {
548
        m_poCurChild->CommitToFile();
9,762✔
549
        m_poCurChild.reset();
9,762✔
550
        m_nCurChildIndex = -1;
9,762✔
551
    }
552

553
    int nBestCandidate = ChooseSubEntryForInsert(nXMin, nYMin, nXMax, nYMax);
21,970✔
554

555
    CPLAssert(nBestCandidate != -1);
21,970✔
556
    if (nBestCandidate == -1)
21,970✔
557
        return -1; /* This should never happen! */
×
558

559
    // Try to load corresponding child... if it fails then we are
560
    // likely in a leaf node, so we'll add the new entry in the current
561
    // node.
562

563
    // Prevent error message if referred block not committed yet.
564
    CPLPushErrorHandler(CPLQuietErrorHandler);
21,970✔
565

566
    auto poBlock = std::unique_ptr<TABRawBinBlock>(
567
        TABCreateMAPBlockFromFile(m_fp, m_asEntries[nBestCandidate].nBlockPtr,
568
                                  m_nBlockSize, TRUE, TABReadWrite));
43,940✔
569
    if (poBlock != nullptr && poBlock->GetBlockClass() == TABMAP_INDEX_BLOCK)
21,970✔
570
    {
571
        m_poCurChild.reset(
10,408✔
572
            cpl::down_cast<TABMAPIndexBlock *>(poBlock.release()));
573
        m_nCurChildIndex = nBestCandidate;
10,408✔
574
        m_poCurChild->SetParentRef(this);
10,408✔
575
        m_poCurChild->SetMAPBlockManagerRef(m_poBlockManagerRef);
10,408✔
576
        bFound = TRUE;
10,408✔
577
    }
578

579
    CPLPopErrorHandler();
21,970✔
580
    CPLErrorReset();
21,970✔
581

582
    if (bFound)
21,970✔
583
    {
584
        /*-------------------------------------------------------------
585
         * Found a child leaf... pass the call to it.
586
         *------------------------------------------------------------*/
587
        return m_poCurChild->ChooseLeafForInsert(nXMin, nYMin, nXMax, nYMax);
10,408✔
588
    }
589

590
    /*-------------------------------------------------------------
591
     * Found no child index node... we must be at the leaf level
592
     * (leaf points at map object data blocks) so we return a ref
593
     * to the TABMAPObjBlock for insertion
594
     *------------------------------------------------------------*/
595
    return m_asEntries[nBestCandidate].nBlockPtr;
11,562✔
596
}
597

598
/**********************************************************************
599
 *                   TABMAPIndexBlock::GetCurLeafEntryMBR()
600
 *
601
 * Get the MBR for specified nBlockPtr in the leaf at the end of the
602
 * chain of m_poCurChild refs.
603
 *
604
 * This method requires that the chain of m_poCurChild refs already point
605
 * to a leaf that contains the specified nBlockPtr, it is usually called
606
 * right after ChooseLeafForInsert().
607
 *
608
 * Returns 0 on success, -1 on error.
609
 **********************************************************************/
610
int TABMAPIndexBlock::GetCurLeafEntryMBR(GInt32 nBlockPtr, GInt32 &nXMin,
20,953✔
611
                                         GInt32 &nYMin, GInt32 &nXMax,
612
                                         GInt32 &nYMax)
613
{
614
    if (m_poCurChild)
20,953✔
615
    {
616
        /* Pass the call down to current child */
617
        return m_poCurChild->GetCurLeafEntryMBR(nBlockPtr, nXMin, nYMin, nXMax,
10,220✔
618
                                                nYMax);
10,220✔
619
    }
620

621
    /* We're at the leaf level, look for the entry */
622
    for (int i = 0; i < m_numEntries; i++)
90,364✔
623
    {
624
        if (m_asEntries[i].nBlockPtr == nBlockPtr)
90,364✔
625
        {
626
            /* Found it. Return its MBR */
627
            nXMin = m_asEntries[i].XMin;
10,733✔
628
            nYMin = m_asEntries[i].YMin;
10,733✔
629
            nXMax = m_asEntries[i].XMax;
10,733✔
630
            nYMax = m_asEntries[i].YMax;
10,733✔
631

632
            return 0;
10,733✔
633
        }
634
    }
635

636
    /* Not found! This should not happen if method is used properly. */
637
    CPLError(CE_Failure, CPLE_AssertionFailed,
×
638
             "Entry to update not found in GetCurLeafEntryMBR()!");
639
    return -1;
×
640
}
641

642
/**********************************************************************
643
 *                   TABMAPIndexBlock::UpdateLeafEntry()
644
 *
645
 * Update the MBR for specified nBlockPtr in the leaf at the end of the
646
 * chain of m_poCurChild refs and update MBR of parents if required.
647
 *
648
 * This method requires that the chain of m_poCurChild refs already point
649
 * to a leaf that contains the specified nBlockPtr, it is usually called
650
 * right after ChooseLeafForInsert().
651
 *
652
 * Returns 0 on success, -1 on error.
653
 **********************************************************************/
654
int TABMAPIndexBlock::UpdateLeafEntry(GInt32 nBlockPtr, GInt32 nXMin,
21,976✔
655
                                      GInt32 nYMin, GInt32 nXMax, GInt32 nYMax)
656
{
657
    if (m_poCurChild)
21,976✔
658
    {
659
        /* Pass the call down to current child */
660
        return m_poCurChild->UpdateLeafEntry(nBlockPtr, nXMin, nYMin, nXMax,
10,408✔
661
                                             nYMax);
10,408✔
662
    }
663

664
    /* We're at the leaf level, look for the entry to update */
665
    for (int i = 0; i < m_numEntries; i++)
91,498✔
666
    {
667
        if (m_asEntries[i].nBlockPtr == nBlockPtr)
91,498✔
668
        {
669
            /* Found it. */
670
            TABMAPIndexEntry *psEntry = &m_asEntries[i];
11,568✔
671

672
            if (psEntry->XMin != nXMin || psEntry->YMin != nYMin ||
11,568✔
673
                psEntry->XMax != nXMax || psEntry->YMax != nYMax)
11,371✔
674
            {
675
                /* MBR changed. Update MBR of entry */
676
                psEntry->XMin = nXMin;
1,399✔
677
                psEntry->YMin = nYMin;
1,399✔
678
                psEntry->XMax = nXMax;
1,399✔
679
                psEntry->YMax = nYMax;
1,399✔
680

681
                m_bModified = TRUE;
1,399✔
682

683
                /* Update MBR of this node and all parents */
684
                RecomputeMBR();
1,399✔
685
            }
686

687
            return 0;
11,568✔
688
        }
689
    }
690

691
    /* Not found! This should not happen if method is used properly. */
692
    CPLError(CE_Failure, CPLE_AssertionFailed,
×
693
             "Entry to update not found in UpdateLeafEntry()!");
694
    return -1;
×
695
}
696

697
/**********************************************************************
698
 *                   TABMAPIndexBlock::AddEntry()
699
 *
700
 * Recursively search the tree until we encounter the best leaf to
701
 * contain the specified object MBR and add the new entry to it.
702
 *
703
 * In the even that the selected leaf node would be full, then it will be
704
 * split and this split can propagate up to its parent, etc.
705
 *
706
 * If bAddInThisNodeOnly=TRUE, then the entry is added only locally and
707
 * we do not try to update the child node.  This is used when the parent
708
 * of a node that is being split has to be updated.
709
 *
710
 * Returns 0 on success, -1 on error.
711
 **********************************************************************/
712
int TABMAPIndexBlock::AddEntry(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
739✔
713
                               GInt32 nYMax, GInt32 nBlockPtr,
714
                               GBool bAddInThisNodeOnly /*=FALSE*/)
715
{
716
    GBool bFound = FALSE;
739✔
717

718
    if (m_eAccess != TABWrite && m_eAccess != TABReadWrite)
739✔
719
    {
720
        CPLError(
×
721
            CE_Failure, CPLE_AssertionFailed,
722
            "Failed adding index entry: File not opened for write access.");
723
        return -1;
×
724
    }
725

726
    /*-----------------------------------------------------------------
727
     * Look for the best candidate to contain the new entry
728
     *----------------------------------------------------------------*/
729

730
    /*-----------------------------------------------------------------
731
     * If bAddInThisNodeOnly=TRUE then we add the entry only locally
732
     * and do not need to look for the proper leaf to insert it.
733
     *----------------------------------------------------------------*/
734
    if (bAddInThisNodeOnly)
739✔
735
        bFound = TRUE;
25✔
736

737
    if (!bFound && m_numEntries > 0)
739✔
738
    {
739
        // Make sure blocks currently in memory are written to disk.
740
        if (m_poCurChild)
621✔
741
        {
742
            m_poCurChild->CommitToFile();
213✔
743
            m_poCurChild.reset();
213✔
744
            m_nCurChildIndex = -1;
213✔
745
        }
746

747
        int nBestCandidate =
748
            ChooseSubEntryForInsert(nXMin, nYMin, nXMax, nYMax);
621✔
749

750
        CPLAssert(nBestCandidate != -1);
621✔
751

752
        if (nBestCandidate != -1)
621✔
753
        {
754
            // Try to load corresponding child... if it fails then we are
755
            // likely in a leaf node, so we'll add the new entry in the current
756
            // node.
757

758
            // Prevent error message if referred block not committed yet.
759
            CPLPushErrorHandler(CPLQuietErrorHandler);
621✔
760

761
            auto poBlock =
762
                std::unique_ptr<TABRawBinBlock>(TABCreateMAPBlockFromFile(
763
                    m_fp, m_asEntries[nBestCandidate].nBlockPtr, m_nBlockSize,
764
                    TRUE, TABReadWrite));
1,242✔
765
            if (poBlock != nullptr &&
1,242✔
766
                poBlock->GetBlockClass() == TABMAP_INDEX_BLOCK)
621✔
767
            {
768
                m_poCurChild.reset(
245✔
769
                    cpl::down_cast<TABMAPIndexBlock *>(poBlock.release()));
770
                m_nCurChildIndex = nBestCandidate;
245✔
771
                m_poCurChild->SetParentRef(this);
245✔
772
                m_poCurChild->SetMAPBlockManagerRef(m_poBlockManagerRef);
245✔
773
                bFound = TRUE;
245✔
774
            }
775

776
            CPLPopErrorHandler();
621✔
777
            CPLErrorReset();
621✔
778
        }
779
    }
780

781
    if (bFound && !bAddInThisNodeOnly)
739✔
782
    {
783
        /*-------------------------------------------------------------
784
         * Found a child leaf... pass the call to it.
785
         *------------------------------------------------------------*/
786
        if (m_poCurChild->AddEntry(nXMin, nYMin, nXMax, nYMax, nBlockPtr) != 0)
245✔
787
            return -1;
×
788
    }
789
    else
790
    {
791
        /*-------------------------------------------------------------
792
         * Found no child to store new object... we're likely at the leaf
793
         * level so we'll store new object in current node
794
         *------------------------------------------------------------*/
795

796
        /*-------------------------------------------------------------
797
         * First thing to do is make sure that there is room for a new
798
         * entry in this node, and to split it if necessary.
799
         *------------------------------------------------------------*/
800
        if (GetNumFreeEntries() < 1)
494✔
801
        {
802
            if (m_poParentRef == nullptr)
19✔
803
            {
804
                /*-----------------------------------------------------
805
                 * Splitting the root node adds one level to the tree, so
806
                 * after splitting we just redirect the call to the new
807
                 * child that's just been created.
808
                 *----------------------------------------------------*/
809
                if (SplitRootNode(nXMin, nYMin, nXMax, nYMax) != 0)
6✔
810
                    return -1;  // Error happened and has already been reported
×
811

812
                CPLAssert(m_poCurChild);
6✔
813
                return m_poCurChild->AddEntry(nXMin, nYMin, nXMax, nYMax,
6✔
814
                                              nBlockPtr, TRUE);
6✔
815
            }
816
            else
817
            {
818
                /*-----------------------------------------------------
819
                 * Splitting a regular node
820
                 *----------------------------------------------------*/
821
                if (SplitNode(nXMin, nYMin, nXMax, nYMax) != 0)
13✔
822
                    return -1;
×
823
            }
824
        }
825

826
        if (InsertEntry(nXMin, nYMin, nXMax, nYMax, nBlockPtr) != 0)
488✔
827
            return -1;
×
828
    }
829

830
    /*-----------------------------------------------------------------
831
     * Update current node MBR and the reference to it in our parent.
832
     *----------------------------------------------------------------*/
833
    RecomputeMBR();
733✔
834

835
    return 0;
733✔
836
}
837

838
/**********************************************************************
839
 *                   TABMAPIndexBlock::ComputeAreaDiff()
840
 *
841
 * (static method, also used by the TABMAPObjBlock class)
842
 *
843
 * Compute the area difference between two MBRs. Used in the SplitNode
844
 * algorithm to decide to which of the two nodes an entry should be added.
845
 *
846
 * The returned AreaDiff value is positive if NodeMBR has to be enlarged
847
 * and negative if new Entry is fully contained in the NodeMBR.
848
 **********************************************************************/
849
double TABMAPIndexBlock::ComputeAreaDiff(GInt32 nNodeXMin, GInt32 nNodeYMin,
17,854✔
850
                                         GInt32 nNodeXMax, GInt32 nNodeYMax,
851
                                         GInt32 nEntryXMin, GInt32 nEntryYMin,
852
                                         GInt32 nEntryXMax, GInt32 nEntryYMax)
853
{
854
    double dAreaDiff = 0.0;
17,854✔
855

856
    const double dNodeAreaBefore =
17,854✔
857
        MITAB_AREA(nNodeXMin, nNodeYMin, nNodeXMax, nNodeYMax);
17,854✔
858

859
    // Does the node fully contain the new entry's MBR?
860
    const GBool bIsContained =
17,854✔
861
        nEntryXMin >= nNodeXMin && nEntryYMin >= nNodeYMin &&
14,163✔
862
        nEntryXMax <= nNodeXMax && nEntryYMax <= nNodeYMax;
32,017✔
863

864
    if (bIsContained)
17,854✔
865
    {
866
        /* If new entry is fully contained in this entry then
867
         * the area difference will be the difference between the area
868
         * of the entry to insert and the area of the node
869
         */
870
        dAreaDiff = MITAB_AREA(nEntryXMin, nEntryYMin, nEntryXMax, nEntryYMax) -
6,872✔
871
                    dNodeAreaBefore;
872
    }
873
    else
874
    {
875
        /* Need to calculate the expanded MBR to calculate the area
876
         * difference.
877
         */
878
        nNodeXMin = std::min(nNodeXMin, nEntryXMin);
10,982✔
879
        nNodeYMin = std::min(nNodeYMin, nEntryYMin);
10,982✔
880
        nNodeXMax = std::max(nNodeXMax, nEntryXMax);
10,982✔
881
        nNodeYMax = std::max(nNodeYMax, nEntryYMax);
10,982✔
882

883
        dAreaDiff = MITAB_AREA(nNodeXMin, nNodeYMin, nNodeXMax, nNodeYMax) -
10,982✔
884
                    dNodeAreaBefore;
885
    }
886

887
    return dAreaDiff;
17,854✔
888
}
889

890
/**********************************************************************
891
 *                   TABMAPIndexBlock::PickSeedsForSplit()
892
 *
893
 * (static method, also used by the TABMAPObjBlock class)
894
 *
895
 * Pick two seeds to use to start splitting this node.
896
 *
897
 * Guttman's LinearPickSeed:
898
 * - Along each dimension find the entry whose rectangle has the
899
 *   highest low side, and the one with the lowest high side
900
 * - Calculate the separation for each pair
901
 * - Normalize the separation by dividing by the extents of the
902
 *   corresponding dimension
903
 * - Choose the pair with the greatest normalized separation along
904
 *   any dimension
905
 **********************************************************************/
906
int TABMAPIndexBlock::PickSeedsForSplit(
274✔
907
    TABMAPIndexEntry *pasEntries, int numEntries, int nSrcCurChildIndex,
908
    GInt32 nNewEntryXMin, GInt32 nNewEntryYMin, GInt32 nNewEntryXMax,
909
    GInt32 nNewEntryYMax, int &nSeed1, int &nSeed2)
910
{
911
    GInt32 nSrcMinX = 0;
274✔
912
    GInt32 nSrcMinY = 0;
274✔
913
    GInt32 nSrcMaxX = 0;
274✔
914
    GInt32 nSrcMaxY = 0;
274✔
915
    int nLowestMaxX = -1;
274✔
916
    int nHighestMinX = -1;
274✔
917
    int nLowestMaxY = -1;
274✔
918
    int nHighestMinY = -1;
274✔
919
    GInt32 nLowestMaxXId = -1;
274✔
920
    GInt32 nHighestMinXId = -1;
274✔
921
    GInt32 nLowestMaxYId = -1;
274✔
922
    GInt32 nHighestMinYId = -1;
274✔
923

924
    nSeed1 = -1;
274✔
925
    nSeed2 = -1;
274✔
926

927
    // Along each dimension find the entry whose rectangle has the
928
    // highest low side, and the one with the lowest high side
929
    for (int iEntry = 0; iEntry < numEntries; iEntry++)
9,476✔
930
    {
931
        if (nLowestMaxXId == -1 || pasEntries[iEntry].XMax < nLowestMaxX)
9,202✔
932
        {
933
            nLowestMaxX = pasEntries[iEntry].XMax;
729✔
934
            nLowestMaxXId = iEntry;
729✔
935
        }
936

937
        if (nHighestMinXId == -1 || pasEntries[iEntry].XMin > nHighestMinX)
9,202✔
938
        {
939
            nHighestMinX = pasEntries[iEntry].XMin;
1,735✔
940
            nHighestMinXId = iEntry;
1,735✔
941
        }
942

943
        if (nLowestMaxYId == -1 || pasEntries[iEntry].YMax < nLowestMaxY)
9,202✔
944
        {
945
            nLowestMaxY = pasEntries[iEntry].YMax;
610✔
946
            nLowestMaxYId = iEntry;
610✔
947
        }
948

949
        if (nHighestMinYId == -1 || pasEntries[iEntry].YMin > nHighestMinY)
9,202✔
950
        {
951
            nHighestMinY = pasEntries[iEntry].YMin;
1,648✔
952
            nHighestMinYId = iEntry;
1,648✔
953
        }
954

955
        // Also keep track of MBR of all entries
956
        if (iEntry == 0)
9,202✔
957
        {
958
            nSrcMinX = pasEntries[iEntry].XMin;
274✔
959
            nSrcMinY = pasEntries[iEntry].YMin;
274✔
960
            nSrcMaxX = pasEntries[iEntry].XMax;
274✔
961
            nSrcMaxY = pasEntries[iEntry].YMax;
274✔
962
        }
963
        else
964
        {
965
            nSrcMinX = std::min(nSrcMinX, pasEntries[iEntry].XMin);
8,928✔
966
            nSrcMinY = std::min(nSrcMinY, pasEntries[iEntry].YMin);
8,928✔
967
            nSrcMaxX = std::max(nSrcMaxX, pasEntries[iEntry].XMax);
8,928✔
968
            nSrcMaxY = std::max(nSrcMaxY, pasEntries[iEntry].YMax);
8,928✔
969
        }
970
    }
971

972
    const double dfSrcWidth =
973
        std::abs(static_cast<double>(nSrcMaxX) - nSrcMinX);
274✔
974
    const double dfSrcHeight =
975
        std::abs(static_cast<double>(nSrcMaxY) - nSrcMinY);
274✔
976

977
    // Calculate the separation for each pair (note that it may be negative
978
    // in case of overlap)
979
    // Normalize the separation by dividing by the extents of the
980
    // corresponding dimension
981
    const double dX =
274✔
982
        dfSrcWidth == 0.0
983
            ? 0.0
274✔
984
            : (static_cast<double>(nHighestMinX) - nLowestMaxX) / dfSrcWidth;
254✔
985
    const double dY =
274✔
986
        dfSrcHeight == 0.0
987
            ? 0.0
274✔
988
            : (static_cast<double>(nHighestMinY) - nLowestMaxY) / dfSrcHeight;
254✔
989

990
    // Choose the pair with the greatest normalized separation along
991
    // any dimension
992
    if (dX > dY)
274✔
993
    {
994
        nSeed1 = nHighestMinXId;
5✔
995
        nSeed2 = nLowestMaxXId;
5✔
996
    }
997
    else
998
    {
999
        nSeed1 = nHighestMinYId;
269✔
1000
        nSeed2 = nLowestMaxYId;
269✔
1001
    }
1002

1003
    // If nSeed1==nSeed2 then just pick any two (giving pref to current child)
1004
    if (nSeed1 == nSeed2)
274✔
1005
    {
1006
        if (nSeed1 != nSrcCurChildIndex && nSrcCurChildIndex != -1)
20✔
1007
            nSeed1 = nSrcCurChildIndex;
×
1008
        else if (nSeed1 != 0)
20✔
1009
            nSeed1 = 0;
×
1010
        else
1011
            nSeed1 = 1;
20✔
1012
    }
1013

1014
    // Decide which of the two seeds best matches the new entry. That seed and
1015
    // the new entry will stay in current node (new entry will be added by the
1016
    // caller later). The other seed will go in the 2nd node
1017
    const double dAreaDiff1 = ComputeAreaDiff(
548✔
1018
        pasEntries[nSeed1].XMin, pasEntries[nSeed1].YMin,
274✔
1019
        pasEntries[nSeed1].XMax, pasEntries[nSeed1].YMax, nNewEntryXMin,
274✔
1020
        nNewEntryYMin, nNewEntryXMax, nNewEntryYMax);
1021

1022
    const double dAreaDiff2 = ComputeAreaDiff(
548✔
1023
        pasEntries[nSeed2].XMin, pasEntries[nSeed2].YMin,
274✔
1024
        pasEntries[nSeed2].XMax, pasEntries[nSeed2].YMax, nNewEntryXMin,
274✔
1025
        nNewEntryYMin, nNewEntryXMax, nNewEntryYMax);
1026

1027
    /* Note that we want to keep this node's current child in here.
1028
     * Since splitting happens only during an addentry() operation and
1029
     * then both the current child and the New Entry should fit in the same
1030
     * area.
1031
     */
1032
    if (nSeed1 != nSrcCurChildIndex &&
274✔
1033
        (dAreaDiff1 > dAreaDiff2 || nSeed2 == nSrcCurChildIndex))
173✔
1034
    {
1035
        // Seed2 stays in this node, Seed1 moves to new node
1036
        // ... swap Seed1 and Seed2 indices
1037
        int nTmp = nSeed1;
101✔
1038
        nSeed1 = nSeed2;
101✔
1039
        nSeed2 = nTmp;
101✔
1040
    }
1041

1042
    return 0;
274✔
1043
}
1044

1045
/**********************************************************************
1046
 *                   TABMAPIndexBlock::SplitNode()
1047
 *
1048
 * Split current Node, update the references in the parent node, etc.
1049
 * Note that Root Nodes cannot be split using this method... SplitRootNode()
1050
 * should be used instead.
1051
 *
1052
 * nNewEntry* are the coord. of the new entry that
1053
 * will be added after the split.  The split is done so that the current
1054
 * node will be the one in which the new object should be stored.
1055
 *
1056
 * Returns 0 on success, -1 on error.
1057
 **********************************************************************/
1058
int TABMAPIndexBlock::SplitNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin,
19✔
1059
                                GInt32 nNewEntryXMax, GInt32 nNewEntryYMax)
1060
{
1061
    CPLAssert(m_poBlockManagerRef);
19✔
1062

1063
    /*-----------------------------------------------------------------
1064
     * Create a 2nd node
1065
     *----------------------------------------------------------------*/
1066
    TABMAPIndexBlock *poNewNode = new TABMAPIndexBlock(m_eAccess);
19✔
1067
    if (poNewNode->InitNewBlock(m_fp, m_nBlockSize,
19✔
1068
                                m_poBlockManagerRef->AllocNewBlock("INDEX")) !=
38✔
1069
        0)
1070
    {
1071
        return -1;
×
1072
    }
1073
    poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef);
19✔
1074

1075
    /*-----------------------------------------------------------------
1076
     * Make a temporary copy of the entries in current node
1077
     *----------------------------------------------------------------*/
1078
    int nSrcEntries = m_numEntries;
19✔
1079
    TABMAPIndexEntry *pasSrcEntries = static_cast<TABMAPIndexEntry *>(
1080
        CPLMalloc(m_numEntries * sizeof(TABMAPIndexEntry)));
19✔
1081
    memcpy(pasSrcEntries, &m_asEntries,
19✔
1082
           m_numEntries * sizeof(TABMAPIndexEntry));
19✔
1083

1084
    int nSrcCurChildIndex = m_nCurChildIndex;
19✔
1085

1086
    /*-----------------------------------------------------------------
1087
     * Pick Seeds for each node
1088
     *----------------------------------------------------------------*/
1089
    int nSeed1, nSeed2;
1090
    PickSeedsForSplit(pasSrcEntries, nSrcEntries, nSrcCurChildIndex,
19✔
1091
                      nNewEntryXMin, nNewEntryYMin, nNewEntryXMax,
1092
                      nNewEntryYMax, nSeed1, nSeed2);
1093

1094
    /*-----------------------------------------------------------------
1095
     * Reset number of entries in this node and start moving new entries
1096
     *----------------------------------------------------------------*/
1097
    m_numEntries = 0;
19✔
1098

1099
    // Insert nSeed1 in this node
1100
    InsertEntry(pasSrcEntries[nSeed1].XMin, pasSrcEntries[nSeed1].YMin,
19✔
1101
                pasSrcEntries[nSeed1].XMax, pasSrcEntries[nSeed1].YMax,
19✔
1102
                pasSrcEntries[nSeed1].nBlockPtr);
19✔
1103

1104
    // Move nSeed2 to 2nd node
1105
    poNewNode->InsertEntry(
19✔
1106
        pasSrcEntries[nSeed2].XMin, pasSrcEntries[nSeed2].YMin,
19✔
1107
        pasSrcEntries[nSeed2].XMax, pasSrcEntries[nSeed2].YMax,
19✔
1108
        pasSrcEntries[nSeed2].nBlockPtr);
19✔
1109

1110
    // Update cur child index if necessary
1111
    if (nSeed1 == nSrcCurChildIndex)
19✔
1112
        m_nCurChildIndex = m_numEntries - 1;
×
1113

1114
    /*-----------------------------------------------------------------
1115
     * Go through the rest of the entries and assign them to one
1116
     * of the 2 nodes.
1117
     *
1118
     * Criteria is minimal area difference.
1119
     * Resolve ties by adding the entry to the node with smaller total
1120
     * area, then to the one with fewer entries, then to either.
1121
     *----------------------------------------------------------------*/
1122
    for (int iEntry = 0; iEntry < nSrcEntries; iEntry++)
494✔
1123
    {
1124
        if (iEntry == nSeed1 || iEntry == nSeed2)
475✔
1125
            continue;
39✔
1126

1127
        // If one of the two nodes is almost full then all remaining
1128
        // entries should go to the other node
1129
        // The entry corresponding to the current child also automatically
1130
        // stays in this node.
1131
        if (iEntry == nSrcCurChildIndex)
437✔
1132
        {
1133
            InsertEntry(pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
1✔
1134
                        pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
1✔
1135
                        pasSrcEntries[iEntry].nBlockPtr);
1✔
1136

1137
            // Update current child index
1138
            m_nCurChildIndex = m_numEntries - 1;
1✔
1139

1140
            continue;
1✔
1141
        }
1142
        else if (m_numEntries >= GetMaxEntries() - 1)
436✔
1143
        {
1144
            poNewNode->InsertEntry(
×
1145
                pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
×
1146
                pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
×
1147
                pasSrcEntries[iEntry].nBlockPtr);
×
1148
            continue;
×
1149
        }
1150
        else if (poNewNode->GetNumEntries() >= GetMaxEntries() - 1)
436✔
1151
        {
1152
            InsertEntry(pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
×
1153
                        pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
×
1154
                        pasSrcEntries[iEntry].nBlockPtr);
×
1155
            continue;
×
1156
        }
1157

1158
        // Decide which of the two nodes to put this entry in
1159
        RecomputeMBR();
436✔
1160
        const double dAreaDiff1 = ComputeAreaDiff(
872✔
1161
            m_nMinX, m_nMinY, m_nMaxX, m_nMaxY, pasSrcEntries[iEntry].XMin,
436✔
1162
            pasSrcEntries[iEntry].YMin, pasSrcEntries[iEntry].XMax,
436✔
1163
            pasSrcEntries[iEntry].YMax);
436✔
1164

1165
        GInt32 nXMin2 = 0;
436✔
1166
        GInt32 nYMin2 = 0;
436✔
1167
        GInt32 nXMax2 = 0;
436✔
1168
        GInt32 nYMax2 = 0;
436✔
1169
        poNewNode->RecomputeMBR();
436✔
1170
        poNewNode->GetMBR(nXMin2, nYMin2, nXMax2, nYMax2);
436✔
1171
        const double dAreaDiff2 = ComputeAreaDiff(
872✔
1172
            nXMin2, nYMin2, nXMax2, nYMax2, pasSrcEntries[iEntry].XMin,
436✔
1173
            pasSrcEntries[iEntry].YMin, pasSrcEntries[iEntry].XMax,
436✔
1174
            pasSrcEntries[iEntry].YMax);
436✔
1175
        if (dAreaDiff1 < dAreaDiff2)
436✔
1176
        {
1177
            // This entry stays in this node.
1178
            InsertEntry(pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
169✔
1179
                        pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
169✔
1180
                        pasSrcEntries[iEntry].nBlockPtr);
169✔
1181
        }
1182
        else
1183
        {
1184
            // This entry goes to new node
1185
            poNewNode->InsertEntry(
267✔
1186
                pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
267✔
1187
                pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
267✔
1188
                pasSrcEntries[iEntry].nBlockPtr);
267✔
1189
        }
1190
    }
1191

1192
    /*-----------------------------------------------------------------
1193
     * Recompute MBR and update current node info in parent
1194
     *----------------------------------------------------------------*/
1195
    RecomputeMBR();
19✔
1196
    poNewNode->RecomputeMBR();
19✔
1197

1198
    /*-----------------------------------------------------------------
1199
     * Add second node info to parent and then flush it to disk.
1200
     * This may trigger splitting of parent
1201
     *----------------------------------------------------------------*/
1202
    CPLAssert(m_poParentRef);
19✔
1203
    int nMinX, nMinY, nMaxX, nMaxY;
1204
    poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
19✔
1205
    m_poParentRef->AddEntry(nMinX, nMinY, nMaxX, nMaxY,
19✔
1206
                            poNewNode->GetNodeBlockPtr(), TRUE);
1207
    poNewNode->CommitToFile();
19✔
1208
    delete poNewNode;
19✔
1209

1210
    CPLFree(pasSrcEntries);
19✔
1211

1212
    return 0;
19✔
1213
}
1214

1215
/**********************************************************************
1216
 *                   TABMAPIndexBlock::SplitRootNode()
1217
 *
1218
 * (private method)
1219
 *
1220
 * Split a Root Node.
1221
 * First, a level of nodes must be added to the tree, then the contents
1222
 * of what used to be the root node is moved 1 level down and then that
1223
 * node is split like a regular node.
1224
 *
1225
 * Returns 0 on success, -1 on error
1226
 **********************************************************************/
1227
int TABMAPIndexBlock::SplitRootNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin,
6✔
1228
                                    GInt32 nNewEntryXMax, GInt32 nNewEntryYMax)
1229
{
1230
    CPLAssert(m_poBlockManagerRef);
6✔
1231
    CPLAssert(m_poParentRef == nullptr);
6✔
1232

1233
    /*-----------------------------------------------------------------
1234
     * Since a root note cannot be split, we add a level of nodes
1235
     * under it and we'll do the split at that level.
1236
     *----------------------------------------------------------------*/
1237
    auto poNewNode = std::make_unique<TABMAPIndexBlock>(m_eAccess);
12✔
1238

1239
    if (poNewNode->InitNewBlock(m_fp, m_nBlockSize,
12✔
1240
                                m_poBlockManagerRef->AllocNewBlock("INDEX")) !=
12✔
1241
        0)
1242
    {
1243
        return -1;
×
1244
    }
1245
    poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef);
6✔
1246

1247
    // Move all entries to the new child
1248
    int nSrcEntries = m_numEntries;
6✔
1249
    m_numEntries = 0;
6✔
1250
    for (int iEntry = 0; iEntry < nSrcEntries; iEntry++)
156✔
1251
    {
1252
        poNewNode->InsertEntry(
150✔
1253
            m_asEntries[iEntry].XMin, m_asEntries[iEntry].YMin,
1254
            m_asEntries[iEntry].XMax, m_asEntries[iEntry].YMax,
1255
            m_asEntries[iEntry].nBlockPtr);
1256
    }
1257

1258
    /*-----------------------------------------------------------------
1259
     * Transfer current child object to new node.
1260
     *----------------------------------------------------------------*/
1261
    if (m_poCurChild)
6✔
1262
    {
1263
        poNewNode->SetCurChild(std::move(m_poCurChild), m_nCurChildIndex);
1✔
1264
        m_nCurChildIndex = -1;
1✔
1265
    }
1266

1267
    /*-----------------------------------------------------------------
1268
     * Place info about new child in current node.
1269
     *----------------------------------------------------------------*/
1270
    poNewNode->RecomputeMBR();
6✔
1271
    int nMinX, nMinY, nMaxX, nMaxY;
1272
    poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
6✔
1273
    InsertEntry(nMinX, nMinY, nMaxX, nMaxY, poNewNode->GetNodeBlockPtr());
6✔
1274

1275
    /*-----------------------------------------------------------------
1276
     * Keep a reference to the new child
1277
     *----------------------------------------------------------------*/
1278
    poNewNode->SetParentRef(this);
6✔
1279
    m_poCurChild = std::move(poNewNode);
6✔
1280
    m_nCurChildIndex = m_numEntries - 1;
6✔
1281

1282
    /*-----------------------------------------------------------------
1283
     * And finally force the child to split itself
1284
     *----------------------------------------------------------------*/
1285
    return m_poCurChild->SplitNode(nNewEntryXMin, nNewEntryYMin, nNewEntryXMax,
6✔
1286
                                   nNewEntryYMax);
6✔
1287
}
1288

1289
/**********************************************************************
1290
 *                   TABMAPIndexBlock::RecomputeMBR()
1291
 *
1292
 * Recompute current block MBR, and update info in parent.
1293
 **********************************************************************/
1294
void TABMAPIndexBlock::RecomputeMBR()
3,048✔
1295
{
1296
    GInt32 nMinX, nMinY, nMaxX, nMaxY;
1297

1298
    nMinX = 1000000000;
3,048✔
1299
    nMinY = 1000000000;
3,048✔
1300
    nMaxX = -1000000000;
3,048✔
1301
    nMaxY = -1000000000;
3,048✔
1302

1303
    for (int i = 0; i < m_numEntries; i++)
36,660✔
1304
    {
1305
        if (m_asEntries[i].XMin < nMinX)
33,612✔
1306
            nMinX = m_asEntries[i].XMin;
8,439✔
1307
        if (m_asEntries[i].XMax > nMaxX)
33,612✔
1308
            nMaxX = m_asEntries[i].XMax;
5,135✔
1309

1310
        if (m_asEntries[i].YMin < nMinY)
33,612✔
1311
            nMinY = m_asEntries[i].YMin;
5,813✔
1312
        if (m_asEntries[i].YMax > nMaxY)
33,612✔
1313
            nMaxY = m_asEntries[i].YMax;
6,173✔
1314
    }
1315

1316
    if (m_nMinX != nMinX || m_nMinY != nMinY || m_nMaxX != nMaxX ||
3,048✔
1317
        m_nMaxY != nMaxY)
1,067✔
1318
    {
1319
        m_nMinX = nMinX;
2,001✔
1320
        m_nMinY = nMinY;
2,001✔
1321
        m_nMaxX = nMaxX;
2,001✔
1322
        m_nMaxY = nMaxY;
2,001✔
1323

1324
        m_bModified = TRUE;
2,001✔
1325

1326
        if (m_poParentRef)
2,001✔
1327
            m_poParentRef->UpdateCurChildMBR(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
889✔
1328
                                             GetNodeBlockPtr());
1329
    }
1330
}
3,048✔
1331

1332
/**********************************************************************
1333
 *                   TABMAPIndexBlock::UpdateCurChildMBR()
1334
 *
1335
 * Update current child MBR info, and propagate info in parent.
1336
 *
1337
 * nBlockPtr is passed only to validate the consistency of the tree.
1338
 **********************************************************************/
1339
void TABMAPIndexBlock::UpdateCurChildMBR(GInt32 nXMin, GInt32 nYMin,
909✔
1340
                                         GInt32 nXMax, GInt32 nYMax,
1341
                                         CPL_UNUSED GInt32 nBlockPtr)
1342
{
1343
    CPLAssert(m_poCurChild);
909✔
1344
    CPLAssert(m_asEntries[m_nCurChildIndex].nBlockPtr == nBlockPtr);
909✔
1345

1346
    if (m_asEntries[m_nCurChildIndex].XMin == nXMin &&
909✔
1347
        m_asEntries[m_nCurChildIndex].YMin == nYMin &&
876✔
1348
        m_asEntries[m_nCurChildIndex].XMax == nXMax &&
855✔
1349
        m_asEntries[m_nCurChildIndex].YMax == nYMax)
511✔
1350
    {
1351
        return; /* Nothing changed... nothing to do */
497✔
1352
    }
1353

1354
    m_bModified = TRUE;
412✔
1355

1356
    m_asEntries[m_nCurChildIndex].XMin = nXMin;
412✔
1357
    m_asEntries[m_nCurChildIndex].YMin = nYMin;
412✔
1358
    m_asEntries[m_nCurChildIndex].XMax = nXMax;
412✔
1359
    m_asEntries[m_nCurChildIndex].YMax = nYMax;
412✔
1360

1361
    m_nMinX = 1000000000;
412✔
1362
    m_nMinY = 1000000000;
412✔
1363
    m_nMaxX = -1000000000;
412✔
1364
    m_nMaxY = -1000000000;
412✔
1365

1366
    for (int i = 0; i < m_numEntries; i++)
2,501✔
1367
    {
1368
        if (m_asEntries[i].XMin < m_nMinX)
2,089✔
1369
            m_nMinX = m_asEntries[i].XMin;
1,151✔
1370
        if (m_asEntries[i].XMax > m_nMaxX)
2,089✔
1371
            m_nMaxX = m_asEntries[i].XMax;
429✔
1372

1373
        if (m_asEntries[i].YMin < m_nMinY)
2,089✔
1374
            m_nMinY = m_asEntries[i].YMin;
771✔
1375
        if (m_asEntries[i].YMax > m_nMaxY)
2,089✔
1376
            m_nMaxY = m_asEntries[i].YMax;
574✔
1377
    }
1378

1379
    if (m_poParentRef)
412✔
1380
        m_poParentRef->UpdateCurChildMBR(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
20✔
1381
                                         GetNodeBlockPtr());
1382
}
1383

1384
/**********************************************************************
1385
 *                   TABMAPIndexBlock::SetMAPBlockManagerRef()
1386
 *
1387
 * Pass a reference to the block manager object for the file this
1388
 * block belongs to.  The block manager will be used by this object
1389
 * when it needs to automatically allocate a new block.
1390
 **********************************************************************/
1391
void TABMAPIndexBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
35,431✔
1392
{
1393
    m_poBlockManagerRef = poBlockMgr;
35,431✔
1394
}
35,431✔
1395

1396
/**********************************************************************
1397
 *                   TABMAPIndexBlock::SetParentRef()
1398
 *
1399
 * Used to pass a reference to this node's parent.
1400
 **********************************************************************/
1401
void TABMAPIndexBlock::SetParentRef(TABMAPIndexBlock *poParent)
34,271✔
1402
{
1403
    m_poParentRef = poParent;
34,271✔
1404
}
34,271✔
1405

1406
/**********************************************************************
1407
 *                   TABMAPIndexBlock::SetCurChild()
1408
 *
1409
 * Used to transfer a child object from one node to another
1410
 **********************************************************************/
1411
void TABMAPIndexBlock::SetCurChild(std::unique_ptr<TABMAPIndexBlock> &&poChild,
477,266✔
1412
                                   int nChildIndex)
1413
{
1414
    if (poChild)
477,266✔
1415
        poChild->SetParentRef(this);
23,612✔
1416
    m_poCurChild = std::move(poChild);
477,266✔
1417
    m_nCurChildIndex = nChildIndex;
477,266✔
1418
}
477,266✔
1419

1420
/**********************************************************************
1421
 *                   TABMAPIndexBlock::Dump()
1422
 *
1423
 * Dump block contents... available only in DEBUG mode.
1424
 **********************************************************************/
1425
#ifdef DEBUG
1426

1427
void TABMAPIndexBlock::Dump(FILE *fpOut /*=NULL*/)
×
1428
{
1429
    if (fpOut == nullptr)
×
1430
        fpOut = stdout;
×
1431

1432
    fprintf(fpOut, "----- TABMAPIndexBlock::Dump() -----\n");
×
1433
    if (m_pabyBuf == nullptr)
×
1434
    {
1435
        fprintf(fpOut, "Block has not been initialized yet.");
×
1436
    }
1437
    else
1438
    {
1439
        fprintf(fpOut, "Index Block (type %d) at offset %d.\n", m_nBlockType,
×
1440
                m_nFileOffset);
1441
        fprintf(fpOut, "  m_numEntries          = %d\n", m_numEntries);
×
1442

1443
        /*-------------------------------------------------------------
1444
         * Loop through all entries, dumping each of them
1445
         *------------------------------------------------------------*/
1446
        if (m_numEntries > 0)
×
1447
            ReadAllEntries();
×
1448

1449
        for (int i = 0; i < m_numEntries; i++)
×
1450
        {
1451
            fprintf(fpOut, "    %6d -> (%d, %d) - (%d, %d)\n",
×
1452
                    m_asEntries[i].nBlockPtr, m_asEntries[i].XMin,
1453
                    m_asEntries[i].YMin, m_asEntries[i].XMax,
1454
                    m_asEntries[i].YMax);
1455
        }
1456
    }
1457

1458
    fflush(fpOut);
×
1459
}
×
1460
#endif  // DEBUG
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc