Coverage Report

Created: 2025-07-23 09:13

/src/gdal/ogr/ogrsf_frmts/mitab/mitab_indfile.cpp
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
 *
3
 * Name:     mitab_indfile.cpp
4
 * Project:  MapInfo TAB Read/Write library
5
 * Language: C++
6
 * Purpose:  Implementation of the TABINDFile class used to handle
7
 *           access to .IND file (table field indexes) attached to a .DAT file
8
 * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
9
 *
10
 **********************************************************************
11
 * Copyright (c) 1999-2001, 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 <cstring>
22
#include <algorithm>
23
24
#include "cpl_conv.h"
25
#include "cpl_error.h"
26
#include "cpl_vsi.h"
27
#include "mitab_priv.h"
28
#include "mitab_utils.h"
29
30
/*=====================================================================
31
 *                      class TABINDFile
32
 *====================================================================*/
33
34
constexpr GUInt32 IND_MAGIC_COOKIE = 24242424;
35
36
/**********************************************************************
37
 *                   TABINDFile::TABINDFile()
38
 *
39
 * Constructor.
40
 **********************************************************************/
41
TABINDFile::TABINDFile()
42
571
    : m_pszFname(nullptr), m_fp(nullptr), m_eAccessMode(TABRead),
43
571
      m_numIndexes(0), m_papoIndexRootNodes(nullptr), m_papbyKeyBuffers(nullptr)
44
571
{
45
571
    m_oBlockManager.SetName("IND");
46
571
    m_oBlockManager.SetBlockSize(512);
47
571
}
48
49
/**********************************************************************
50
 *                   TABINDFile::~TABINDFile()
51
 *
52
 * Destructor.
53
 **********************************************************************/
54
TABINDFile::~TABINDFile()
55
571
{
56
571
    Close();
57
571
}
58
59
/**********************************************************************
60
 *                   TABINDFile::Open()
61
 *
62
 * Open a .IND file, read the header and the root nodes for all the
63
 * field indexes, and be ready to search the indexes.
64
 *
65
 * If the filename that is passed in contains a .DAT extension then
66
 * the extension will be changed to .IND before trying to open the file.
67
 *
68
 * Note that we pass a pszAccess flag, but only read access is supported
69
 * for now (and there are no plans to support write.)
70
 *
71
 * Set bTestOpenNoError=TRUE to silently return -1 with no error message
72
 * if the file cannot be opened because it does not exist.
73
 *
74
 * Returns 0 on success, -1 on error.
75
 **********************************************************************/
76
int TABINDFile::Open(const char *pszFname, const char *pszAccess,
77
                     GBool bTestOpenNoError /*=FALSE*/)
78
571
{
79
571
    if (m_fp)
80
0
    {
81
0
        CPLError(CE_Failure, CPLE_FileIO,
82
0
                 "Open() failed: object already contains an open file");
83
0
        return -1;
84
0
    }
85
86
    /*-----------------------------------------------------------------
87
     * Validate access mode and make sure we use binary access.
88
     * Note that for write access, we actually need read/write access to
89
     * the file.
90
     *----------------------------------------------------------------*/
91
571
    if (STARTS_WITH_CI(pszAccess, "r") && strchr(pszAccess, '+') != nullptr)
92
0
    {
93
0
        m_eAccessMode = TABReadWrite;
94
0
        pszAccess = "rb+";
95
0
    }
96
571
    else if (STARTS_WITH_CI(pszAccess, "r"))
97
571
    {
98
571
        m_eAccessMode = TABRead;
99
571
        pszAccess = "rb";
100
571
    }
101
0
    else if (STARTS_WITH_CI(pszAccess, "w"))
102
0
    {
103
0
        m_eAccessMode = TABWrite;
104
0
        pszAccess = "wb+";
105
0
    }
106
0
    else
107
0
    {
108
0
        CPLError(CE_Failure, CPLE_FileIO,
109
0
                 "Open() failed: access mode \"%s\" not supported", pszAccess);
110
0
        return -1;
111
0
    }
112
113
    /*-----------------------------------------------------------------
114
     * Change .DAT (or .TAB) extension to .IND if necessary
115
     *----------------------------------------------------------------*/
116
571
    m_pszFname = CPLStrdup(pszFname);
117
118
571
    const int nLen = static_cast<int>(strlen(m_pszFname));
119
571
    if (nLen > 4 && !EQUAL(m_pszFname + nLen - 4, ".IND"))
120
245
        strcpy(m_pszFname + nLen - 4, ".ind");
121
122
571
#ifndef _WIN32
123
571
    TABAdjustFilenameExtension(m_pszFname);
124
571
#endif
125
126
    /*-----------------------------------------------------------------
127
     * Open file
128
     *----------------------------------------------------------------*/
129
571
    m_fp = VSIFOpenL(m_pszFname, pszAccess);
130
131
571
    if (m_fp == nullptr)
132
27
    {
133
27
        if (!bTestOpenNoError)
134
0
            CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s (%s)",
135
0
                     m_pszFname, pszAccess);
136
137
27
        CPLFree(m_pszFname);
138
27
        m_pszFname = nullptr;
139
27
        return -1;
140
27
    }
141
142
    /*-----------------------------------------------------------------
143
     * Reset block manager to allocate first block at byte 512, after header.
144
     *----------------------------------------------------------------*/
145
544
    m_oBlockManager.Reset();
146
544
    m_oBlockManager.AllocNewBlock();
147
148
    /*-----------------------------------------------------------------
149
     * Read access: Read the header block
150
     * This will also alloc and init the array of index root nodes.
151
     *----------------------------------------------------------------*/
152
544
    if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) &&
153
544
        ReadHeader() != 0)
154
90
    {
155
        // Failed reading header... CPLError() has already been called
156
90
        Close();
157
90
        return -1;
158
90
    }
159
160
    /*-----------------------------------------------------------------
161
     * Write access: Init class members and write a dummy header block
162
     *----------------------------------------------------------------*/
163
454
    if (m_eAccessMode == TABWrite)
164
0
    {
165
0
        m_numIndexes = 0;
166
167
0
        if (WriteHeader() != 0)
168
0
        {
169
            // Failed writing header... CPLError() has already been called
170
0
            Close();
171
0
            return -1;
172
0
        }
173
0
    }
174
175
454
    return 0;
176
454
}
177
178
/**********************************************************************
179
 *                   TABINDFile::Close()
180
 *
181
 * Close current file, and release all memory used.
182
 *
183
 * Returns 0 on success, -1 on error.
184
 **********************************************************************/
185
int TABINDFile::Close()
186
1.18k
{
187
1.18k
    if (m_fp == nullptr)
188
636
        return 0;
189
190
    /*-----------------------------------------------------------------
191
     * In Write Mode, commit all indexes to the file
192
     *----------------------------------------------------------------*/
193
544
    if (m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite)
194
0
    {
195
0
        WriteHeader();
196
197
0
        for (int iIndex = 0; iIndex < m_numIndexes; iIndex++)
198
0
        {
199
0
            if (m_papoIndexRootNodes && m_papoIndexRootNodes[iIndex])
200
0
            {
201
0
                CPL_IGNORE_RET_VAL(
202
0
                    m_papoIndexRootNodes[iIndex]->CommitToFile());
203
0
            }
204
0
        }
205
0
    }
206
207
    /*-----------------------------------------------------------------
208
     * Free index nodes in memory
209
     *----------------------------------------------------------------*/
210
5.83k
    for (int iIndex = 0; iIndex < m_numIndexes; iIndex++)
211
5.29k
    {
212
5.29k
        if (m_papoIndexRootNodes && m_papoIndexRootNodes[iIndex])
213
441
            delete m_papoIndexRootNodes[iIndex];
214
5.29k
        if (m_papbyKeyBuffers && m_papbyKeyBuffers[iIndex])
215
411
            CPLFree(m_papbyKeyBuffers[iIndex]);
216
5.29k
    }
217
544
    CPLFree(m_papoIndexRootNodes);
218
544
    m_papoIndexRootNodes = nullptr;
219
544
    CPLFree(m_papbyKeyBuffers);
220
544
    m_papbyKeyBuffers = nullptr;
221
544
    m_numIndexes = 0;
222
223
    /*-----------------------------------------------------------------
224
     * Close file
225
     *----------------------------------------------------------------*/
226
544
    VSIFCloseL(m_fp);
227
544
    m_fp = nullptr;
228
229
544
    CPLFree(m_pszFname);
230
544
    m_pszFname = nullptr;
231
232
544
    return 0;
233
1.18k
}
234
235
/**********************************************************************
236
 *                   TABINDFile::ReadHeader()
237
 *
238
 * (private method)
239
 * Read the header block and init all class members for read access.
240
 *
241
 * Returns 0 on success, -1 on error.
242
 **********************************************************************/
243
int TABINDFile::ReadHeader()
244
544
{
245
246
544
    CPLAssert(m_fp);
247
544
    CPLAssert(m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite);
248
249
    /*-----------------------------------------------------------------
250
     * In ReadWrite mode, we need to init BlockManager with file size
251
     *----------------------------------------------------------------*/
252
544
    VSIStatBufL sStatBuf;
253
544
    if (m_eAccessMode == TABReadWrite && VSIStatL(m_pszFname, &sStatBuf) != -1)
254
0
    {
255
0
        m_oBlockManager.SetLastPtr(
256
0
            static_cast<int>(((sStatBuf.st_size - 1) / 512) * 512));
257
0
    }
258
259
    /*-----------------------------------------------------------------
260
     * Read the header block
261
     *----------------------------------------------------------------*/
262
544
    TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
263
544
    if (poHeaderBlock->ReadFromFile(m_fp, 0, 512) != 0)
264
42
    {
265
        // CPLError() has already been called.
266
42
        delete poHeaderBlock;
267
42
        return -1;
268
42
    }
269
270
502
    poHeaderBlock->GotoByteInBlock(0);
271
502
    GUInt32 nMagicCookie = poHeaderBlock->ReadInt32();
272
502
    if (nMagicCookie != IND_MAGIC_COOKIE)
273
1
    {
274
1
        CPLError(CE_Failure, CPLE_FileIO,
275
1
                 "%s: Invalid Magic Cookie: got %d, expected %d", m_pszFname,
276
1
                 nMagicCookie, IND_MAGIC_COOKIE);
277
1
        delete poHeaderBlock;
278
1
        return -1;
279
1
    }
280
281
501
    poHeaderBlock->GotoByteInBlock(12);
282
501
    m_numIndexes = poHeaderBlock->ReadInt16();
283
501
    if (m_numIndexes < 1 || m_numIndexes > 29)
284
17
    {
285
17
        CPLError(CE_Failure, CPLE_FileIO,
286
17
                 "Invalid number of indexes (%d) in file %s", m_numIndexes,
287
17
                 m_pszFname);
288
17
        delete poHeaderBlock;
289
17
        return -1;
290
17
    }
291
292
    /*-----------------------------------------------------------------
293
     * Alloc and init the array of index root nodes.
294
     *----------------------------------------------------------------*/
295
484
    m_papoIndexRootNodes = static_cast<TABINDNode **>(
296
484
        CPLCalloc(m_numIndexes, sizeof(TABINDNode *)));
297
298
484
    m_papbyKeyBuffers =
299
484
        static_cast<GByte **>(CPLCalloc(m_numIndexes, sizeof(GByte *)));
300
301
    /* First index def. starts at byte 48 */
302
484
    poHeaderBlock->GotoByteInBlock(48);
303
304
977
    for (int iIndex = 0; iIndex < m_numIndexes; iIndex++)
305
523
    {
306
        /*-------------------------------------------------------------
307
         * Read next index definition
308
         *------------------------------------------------------------*/
309
523
        GInt32 nRootNodePtr = poHeaderBlock->ReadInt32();
310
523
        poHeaderBlock->ReadInt16();  // skip... max. num of entries per node
311
523
        int nTreeDepth = poHeaderBlock->ReadByte();
312
523
        int nKeyLength = poHeaderBlock->ReadByte();
313
523
        poHeaderBlock->GotoByteRel(8);  // skip next 8 bytes;
314
315
        /*-------------------------------------------------------------
316
         * And init root node for this index.
317
         * Note that if nRootNodePtr==0 then this means that the
318
         * corresponding index does not exist (i.e. has been deleted?)
319
         * so we simply do not allocate the root node in this case.
320
         * An error will be produced if the user tries to access this index
321
         * later during execution.
322
         *------------------------------------------------------------*/
323
523
        if (nRootNodePtr > 0)
324
441
        {
325
441
            m_papoIndexRootNodes[iIndex] = new TABINDNode(m_eAccessMode);
326
441
            if (m_papoIndexRootNodes[iIndex]->InitNode(
327
441
                    m_fp, nRootNodePtr, nKeyLength, nTreeDepth, FALSE,
328
441
                    &m_oBlockManager) != 0)
329
30
            {
330
                // CPLError has already been called
331
30
                delete poHeaderBlock;
332
30
                return -1;
333
30
            }
334
335
            // Alloc a temporary key buffer for this index.
336
            // This buffer will be used by the BuildKey() method
337
411
            m_papbyKeyBuffers[iIndex] =
338
411
                static_cast<GByte *>(CPLCalloc(nKeyLength + 1, sizeof(GByte)));
339
411
        }
340
82
        else
341
82
        {
342
82
            m_papoIndexRootNodes[iIndex] = nullptr;
343
82
            m_papbyKeyBuffers[iIndex] = nullptr;
344
82
        }
345
523
    }
346
347
    /*-----------------------------------------------------------------
348
     * OK, we won't need the header block any more... free it.
349
     *----------------------------------------------------------------*/
350
454
    delete poHeaderBlock;
351
352
454
    return 0;
353
484
}
354
355
/**********************************************************************
356
 *                   TABINDFile::WriteHeader()
357
 *
358
 * (private method)
359
 * Write the header block based on current index information.
360
 *
361
 * Returns 0 on success, -1 on error.
362
 **********************************************************************/
363
int TABINDFile::WriteHeader()
364
0
{
365
0
    CPLAssert(m_fp);
366
0
    CPLAssert(m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite);
367
368
    /*-----------------------------------------------------------------
369
     * Write the 48 bytes of file header
370
     *----------------------------------------------------------------*/
371
0
    TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
372
0
    poHeaderBlock->InitNewBlock(m_fp, 512, 0);
373
374
0
    poHeaderBlock->WriteInt32(IND_MAGIC_COOKIE);
375
376
0
    poHeaderBlock->WriteInt16(100);  // ???
377
0
    poHeaderBlock->WriteInt16(512);  // ???
378
0
    poHeaderBlock->WriteInt32(0);    // ???
379
380
0
    poHeaderBlock->WriteInt16(static_cast<GInt16>(m_numIndexes));
381
382
0
    poHeaderBlock->WriteInt16(0x15e7);  // ???
383
384
0
    poHeaderBlock->WriteInt16(10);      // ???
385
0
    poHeaderBlock->WriteInt16(0x611d);  // ???
386
387
0
    poHeaderBlock->WriteZeros(28);
388
389
    /*-----------------------------------------------------------------
390
     * The first index definition starts at byte 48
391
     *----------------------------------------------------------------*/
392
0
    for (int iIndex = 0; iIndex < m_numIndexes; iIndex++)
393
0
    {
394
0
        TABINDNode *poRootNode = m_papoIndexRootNodes[iIndex];
395
396
0
        if (poRootNode)
397
0
        {
398
            /*---------------------------------------------------------
399
             * Write next index definition
400
             *--------------------------------------------------------*/
401
0
            poHeaderBlock->WriteInt32(poRootNode->GetNodeBlockPtr());
402
0
            poHeaderBlock->WriteInt16(
403
0
                static_cast<GInt16>(poRootNode->GetMaxNumEntries()));
404
0
            poHeaderBlock->WriteByte(
405
0
                static_cast<GByte>(poRootNode->GetSubTreeDepth()));
406
0
            poHeaderBlock->WriteByte(
407
0
                static_cast<GByte>(poRootNode->GetKeyLength()));
408
409
0
            poHeaderBlock->WriteZeros(8);
410
411
            /*---------------------------------------------------------
412
             * Look for overflow of the SubTreeDepth field (byte)
413
             *--------------------------------------------------------*/
414
0
            if (poRootNode->GetSubTreeDepth() > 255)
415
0
            {
416
0
                CPLError(CE_Failure, CPLE_AssertionFailed,
417
0
                         "Index no %d is too large and will not be usable. "
418
0
                         "(SubTreeDepth = %d, cannot exceed 255).",
419
0
                         iIndex + 1, poRootNode->GetSubTreeDepth());
420
0
                return -1;
421
0
            }
422
0
        }
423
0
        else
424
0
        {
425
            /*---------------------------------------------------------
426
             * NULL Root Node: This index has likely been deleted
427
             *--------------------------------------------------------*/
428
0
            poHeaderBlock->WriteZeros(16);
429
0
        }
430
0
    }
431
432
    /*-----------------------------------------------------------------
433
     * OK, we won't need the header block any more... write and free it.
434
     *----------------------------------------------------------------*/
435
0
    if (poHeaderBlock->CommitToFile() != 0)
436
0
        return -1;
437
438
0
    delete poHeaderBlock;
439
440
0
    return 0;
441
0
}
442
443
/**********************************************************************
444
 *                   TABINDFile::ValidateIndexNo()
445
 *
446
 * Private method to validate the index no parameter of some methods...
447
 * returns 0 if index no. is OK, or produces an error ands returns -1
448
 * if index no is not valid.
449
 **********************************************************************/
450
int TABINDFile::ValidateIndexNo(int nIndexNumber)
451
25.0k
{
452
25.0k
    if (m_fp == nullptr)
453
0
    {
454
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
455
0
                 "TABINDFile: File has not been opened yet!");
456
0
        return -1;
457
0
    }
458
459
25.0k
    if (nIndexNumber < 1 || nIndexNumber > m_numIndexes ||
460
25.0k
        m_papoIndexRootNodes == nullptr ||
461
25.0k
        m_papoIndexRootNodes[nIndexNumber - 1] == nullptr)
462
5.47k
    {
463
5.47k
        CPLError(CE_Failure, CPLE_AssertionFailed,
464
5.47k
                 "No field index number %d in %s: Valid range is [1..%d].",
465
5.47k
                 nIndexNumber, m_pszFname, m_numIndexes);
466
5.47k
        return -1;
467
5.47k
    }
468
469
19.6k
    return 0;  // Index seems valid
470
25.0k
}
471
472
/**********************************************************************
473
 *                   TABINDFile::SetIndexFieldType()
474
 *
475
 * Sets the field type for the specified index.
476
 * This information will then be used in building the key values, etc.
477
 *
478
 * Returns 0 on success, -1 on error.
479
 **********************************************************************/
480
int TABINDFile::SetIndexFieldType(int nIndexNumber, TABFieldType eType)
481
188
{
482
188
    if (ValidateIndexNo(nIndexNumber) != 0)
483
19
        return -1;
484
485
169
    return m_papoIndexRootNodes[nIndexNumber - 1]->SetFieldType(eType);
486
188
}
487
488
/**********************************************************************
489
 *                   TABINDFile::SetIndexUnique()
490
 *
491
 * Indicate that an index's keys are unique.  This allows for some
492
 * optimization with read access.  By default, an index is treated as if
493
 * its keys could have duplicates.
494
 *
495
 * Returns 0 on success, -1 on error.
496
 **********************************************************************/
497
int TABINDFile::SetIndexUnique(int nIndexNumber, GBool bUnique /*=TRUE*/)
498
0
{
499
0
    if (ValidateIndexNo(nIndexNumber) != 0)
500
0
        return -1;
501
502
0
    m_papoIndexRootNodes[nIndexNumber - 1]->SetUnique(bUnique);
503
504
0
    return 0;
505
0
}
506
507
/**********************************************************************
508
 *                   TABINDFile::BuildKey()
509
 *
510
 * Encode a field value in the form required to be compared with index
511
 * keys in the specified index.
512
 *
513
 * Note that index numbers are positive values starting at 1.
514
 *
515
 * Returns a reference to an internal buffer that is valid only until the
516
 * next call to BuildKey().  (should not be freed by the caller).
517
 * Returns NULL if field index is invalid.
518
 *
519
 * The first flavor of the function handles integer type of values, this
520
 * corresponds to MapInfo types: integer, smallint, logical and date
521
 **********************************************************************/
522
GByte *TABINDFile::BuildKey(int nIndexNumber, GInt32 nValue)
523
12.4k
{
524
12.4k
    if (ValidateIndexNo(nIndexNumber) != 0)
525
2.72k
        return nullptr;
526
527
9.72k
    int nKeyLength = m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength();
528
529
    /*-----------------------------------------------------------------
530
     * Convert all int values to MSB using the right number of bytes
531
     * Note:
532
     * The most significant bit has to be unset for negative values,
533
     * and to be set for positive ones... that's the reverse of what it
534
     * should usually be.  Adding 0x80 to the MSB byte will do the job.
535
     *----------------------------------------------------------------*/
536
9.72k
    switch (nKeyLength)
537
9.72k
    {
538
4.74k
        case 1:
539
4.74k
            m_papbyKeyBuffers[nIndexNumber - 1][0] =
540
4.74k
                static_cast<GByte>(nValue & 0xff) + 0x80;
541
4.74k
            break;
542
2.53k
        case 2:
543
2.53k
            m_papbyKeyBuffers[nIndexNumber - 1][0] =
544
2.53k
                static_cast<GByte>(nValue / 0x100 & 0xff) + 0x80;
545
2.53k
            m_papbyKeyBuffers[nIndexNumber - 1][1] =
546
2.53k
                static_cast<GByte>(nValue & 0xff);
547
2.53k
            break;
548
17
        case 4:
549
17
            m_papbyKeyBuffers[nIndexNumber - 1][0] =
550
17
                static_cast<GByte>(nValue / 0x1000000 & 0xff) + 0x80;
551
17
            m_papbyKeyBuffers[nIndexNumber - 1][1] =
552
17
                static_cast<GByte>(nValue / 0x10000 & 0xff);
553
17
            m_papbyKeyBuffers[nIndexNumber - 1][2] =
554
17
                static_cast<GByte>(nValue / 0x100 & 0xff);
555
17
            m_papbyKeyBuffers[nIndexNumber - 1][3] =
556
17
                static_cast<GByte>(nValue & 0xff);
557
17
            break;
558
2.43k
        default:
559
2.43k
            CPLError(CE_Failure, CPLE_AssertionFailed,
560
2.43k
                     "BuildKey(): %d bytes integer key length not supported",
561
2.43k
                     nKeyLength);
562
2.43k
            break;
563
9.72k
    }
564
565
9.72k
    return m_papbyKeyBuffers[nIndexNumber - 1];
566
9.72k
}
567
568
/**********************************************************************
569
 *                   TABINDFile::BuildKey()
570
 *
571
 * BuildKey() for string fields
572
 **********************************************************************/
573
GByte *TABINDFile::BuildKey(int nIndexNumber, const char *pszStr)
574
0
{
575
0
    if (ValidateIndexNo(nIndexNumber) != 0 || pszStr == nullptr)
576
0
        return nullptr;
577
578
0
    int nKeyLength = m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength();
579
580
    /*-----------------------------------------------------------------
581
     * Strings keys are all in uppercase, and padded with '\0'
582
     *----------------------------------------------------------------*/
583
0
    int i = 0;
584
0
    for (i = 0; i < nKeyLength && pszStr[i] != '\0'; i++)
585
0
    {
586
0
        m_papbyKeyBuffers[nIndexNumber - 1][i] = static_cast<GByte>(
587
0
            CPLToupper(static_cast<unsigned char>(pszStr[i])));
588
0
    }
589
590
    /* Pad the end of the buffer with '\0' */
591
0
    for (; i < nKeyLength; i++)
592
0
    {
593
0
        m_papbyKeyBuffers[nIndexNumber - 1][i] = '\0';
594
0
    }
595
596
0
    return m_papbyKeyBuffers[nIndexNumber - 1];
597
0
}
598
599
/**********************************************************************
600
 *                   TABINDFile::BuildKey()
601
 *
602
 * BuildKey() for float and decimal fields
603
 **********************************************************************/
604
GByte *TABINDFile::BuildKey(int nIndexNumber, double dValue)
605
0
{
606
0
    if (ValidateIndexNo(nIndexNumber) != 0)
607
0
        return nullptr;
608
609
0
    const int nKeyLength =
610
0
        m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength();
611
0
    CPLAssert(nKeyLength == 8 && sizeof(double) == 8);
612
613
    /*-----------------------------------------------------------------
614
     * Convert double and decimal values...
615
     * Reverse the sign of the value, and convert to MSB
616
     *----------------------------------------------------------------*/
617
0
    dValue = -dValue;
618
619
0
#ifndef CPL_MSB
620
0
    CPL_SWAPDOUBLE(&dValue);
621
0
#endif
622
623
0
    memcpy(m_papbyKeyBuffers[nIndexNumber - 1],
624
0
           reinterpret_cast<GByte *>(&dValue), nKeyLength);
625
626
0
    return m_papbyKeyBuffers[nIndexNumber - 1];
627
0
}
628
629
/**********************************************************************
630
 *                   TABINDFile::BuildKey()
631
 *
632
 * BuildKey() for LargeInt
633
 **********************************************************************/
634
GByte *TABINDFile::BuildKey(int nIndexNumber, GInt64 nValue)
635
0
{
636
0
    if (ValidateIndexNo(nIndexNumber) != 0)
637
0
        return nullptr;
638
639
0
    const int nKeyLength =
640
0
        m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength();
641
0
    CPLAssert(nKeyLength == 8 && sizeof(double) == 8);
642
643
0
#ifndef CPL_MSB
644
0
    CPL_SWAP64PTR(&nValue);
645
0
#endif
646
647
0
    memcpy(m_papbyKeyBuffers[nIndexNumber - 1],
648
0
           reinterpret_cast<GByte *>(&nValue), nKeyLength);
649
650
0
    return m_papbyKeyBuffers[nIndexNumber - 1];
651
0
}
652
653
/**********************************************************************
654
 *                   TABINDFile::FindFirst()
655
 *
656
 * Search one of the indexes for a key value.
657
 *
658
 * Note that index numbers are positive values starting at 1.
659
 *
660
 * Return value:
661
 *  - the key's corresponding record number in the .DAT file (greater than 0)
662
 *  - 0 if the key was not found
663
 *  - or -1 if an error happened
664
 **********************************************************************/
665
GInt32 TABINDFile::FindFirst(int nIndexNumber, GByte *pKeyValue)
666
12.4k
{
667
12.4k
    if (ValidateIndexNo(nIndexNumber) != 0)
668
2.72k
        return -1;
669
670
9.72k
    return m_papoIndexRootNodes[nIndexNumber - 1]->FindFirst(pKeyValue);
671
12.4k
}
672
673
/**********************************************************************
674
 *                   TABINDFile::FindNext()
675
 *
676
 * Continue the Search for pKeyValue previously initiated by FindFirst().
677
 * NOTE: FindFirst() MUST have been previously called for this call to
678
 *       work...
679
 *
680
 * Note that index numbers are positive values starting at 1.
681
 *
682
 * Return value:
683
 *  - the key's corresponding record number in the .DAT file (greater than 0)
684
 *  - 0 if the key was not found
685
 *  - or -1 if an error happened
686
 **********************************************************************/
687
GInt32 TABINDFile::FindNext(int nIndexNumber, GByte *pKeyValue)
688
0
{
689
0
    if (ValidateIndexNo(nIndexNumber) != 0)
690
0
        return -1;
691
692
0
    return m_papoIndexRootNodes[nIndexNumber - 1]->FindNext(pKeyValue);
693
0
}
694
695
/**********************************************************************
696
 *                   TABINDFile::CreateIndex()
697
 *
698
 * Create a new index with the specified field type and size.
699
 * Field size applies only to char field type... the other types have a
700
 * predefined key length.
701
 *
702
 * Key length is limited to 128 chars. char fields longer than 128 chars
703
 * will have their key truncated to 128 bytes.
704
 *
705
 * Note that a .IND file can contain only a maximum of 29 indexes.
706
 *
707
 * Returns the new field index on success (greater than 0), or -1 on error.
708
 **********************************************************************/
709
int TABINDFile::CreateIndex(TABFieldType eType, int nFieldSize)
710
0
{
711
0
    int i, nNewIndexNo = -1;
712
713
0
    if (m_fp == nullptr ||
714
0
        (m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite))
715
0
        return -1;
716
717
    // __TODO__
718
    // We'll need more work in TABDATFile::WriteDateTimeField() before
719
    // we can support indexes on fields of type DateTime (see bug #1844)
720
0
    if (eType == TABFDateTime)
721
0
    {
722
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
723
0
                 "Index on fields of type DateTime not supported yet.");
724
0
        return -1;
725
0
    }
726
727
    /*-----------------------------------------------------------------
728
     * Look for an empty slot in the current array, if there is none
729
     * then extend the array.
730
     *----------------------------------------------------------------*/
731
0
    for (i = 0; m_papoIndexRootNodes && i < m_numIndexes; i++)
732
0
    {
733
0
        if (m_papoIndexRootNodes[i] == nullptr)
734
0
        {
735
0
            nNewIndexNo = i;
736
0
            break;
737
0
        }
738
0
    }
739
740
0
    if (nNewIndexNo == -1 && m_numIndexes >= 29)
741
0
    {
742
0
        CPLError(CE_Failure, CPLE_AppDefined,
743
0
                 "Cannot add new index to %s.  A dataset can contain only a "
744
0
                 "maximum of 29 indexes.",
745
0
                 m_pszFname);
746
0
        return -1;
747
0
    }
748
749
0
    if (nNewIndexNo == -1)
750
0
    {
751
        /*-------------------------------------------------------------
752
         * Add a slot for new index at the end of the nodes array.
753
         *------------------------------------------------------------*/
754
0
        m_numIndexes++;
755
0
        m_papoIndexRootNodes = static_cast<TABINDNode **>(CPLRealloc(
756
0
            m_papoIndexRootNodes, m_numIndexes * sizeof(TABINDNode *)));
757
758
0
        m_papbyKeyBuffers = static_cast<GByte **>(
759
0
            CPLRealloc(m_papbyKeyBuffers, m_numIndexes * sizeof(GByte *)));
760
761
0
        nNewIndexNo = m_numIndexes - 1;
762
0
    }
763
764
    /*-----------------------------------------------------------------
765
     * Alloc and init new node
766
     * The call to InitNode() automatically allocates storage space for
767
     * the node in the file.
768
     * New nodes are created with a subtree_depth=1 since they start as
769
     * leaf nodes, i.e. their entries point directly to .DAT records
770
     *----------------------------------------------------------------*/
771
0
    int nKeyLength = ((eType == TABFInteger)    ? 4
772
0
                      : (eType == TABFSmallInt) ? 2
773
0
                      : (eType == TABFLargeInt) ? 8
774
0
                      : (eType == TABFFloat)    ? 8
775
0
                      : (eType == TABFDecimal)  ? 8
776
0
                      : (eType == TABFDate)     ? 4
777
0
                      : (eType == TABFTime)     ? 4
778
0
                                                :
779
                                            /*(eType == TABFDateTime) ? 8: */
780
0
                          (eType == TABFLogical) ? 4
781
0
                                                 : std::min(128, nFieldSize));
782
783
0
    m_papoIndexRootNodes[nNewIndexNo] = new TABINDNode(m_eAccessMode);
784
0
    if (m_papoIndexRootNodes[nNewIndexNo]->InitNode(m_fp, 0, nKeyLength,
785
0
                                                    1,      // subtree depth=1
786
0
                                                    FALSE,  // not unique
787
0
                                                    &m_oBlockManager, nullptr,
788
0
                                                    0, 0) != 0)
789
0
    {
790
        // CPLError has already been called
791
0
        return -1;
792
0
    }
793
794
    // Alloc a temporary key buffer for this index.
795
    // This buffer will be used by the BuildKey() method
796
0
    m_papbyKeyBuffers[nNewIndexNo] =
797
0
        static_cast<GByte *>(CPLCalloc(nKeyLength + 1, sizeof(GByte)));
798
799
    // Return 1-based index number
800
0
    return nNewIndexNo + 1;
801
0
}
802
803
/**********************************************************************
804
 *                   TABINDFile::AddEntry()
805
 *
806
 * Add an .DAT record entry for pKeyValue in the specified index.
807
 *
808
 * Note that index numbers are positive values starting at 1.
809
 * nRecordNo is the .DAT record number, record numbers start at 1.
810
 *
811
 * Returns 0 on success, -1 on error
812
 **********************************************************************/
813
int TABINDFile::AddEntry(int nIndexNumber, GByte *pKeyValue, GInt32 nRecordNo)
814
0
{
815
0
    if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
816
0
        ValidateIndexNo(nIndexNumber) != 0)
817
0
        return -1;
818
819
0
    return m_papoIndexRootNodes[nIndexNumber - 1]->AddEntry(pKeyValue,
820
0
                                                            nRecordNo);
821
0
}
822
823
/**********************************************************************
824
 *                   TABINDFile::Dump()
825
 *
826
 * Dump block contents... available only in DEBUG mode.
827
 **********************************************************************/
828
#ifdef DEBUG
829
830
void TABINDFile::Dump(FILE *fpOut /*=NULL*/)
831
{
832
    if (fpOut == nullptr)
833
        fpOut = stdout;
834
835
    fprintf(fpOut, "----- TABINDFile::Dump() -----\n");
836
837
    if (m_fp == nullptr)
838
    {
839
        fprintf(fpOut, "File is not opened.\n");
840
    }
841
    else
842
    {
843
        fprintf(fpOut, "File is opened: %s\n", m_pszFname);
844
        fprintf(fpOut, "   m_numIndexes   = %d\n", m_numIndexes);
845
        for (int i = 0; i < m_numIndexes && m_papoIndexRootNodes; i++)
846
        {
847
            if (m_papoIndexRootNodes[i])
848
            {
849
                fprintf(fpOut, "  ----- Index # %d -----\n", i + 1);
850
                m_papoIndexRootNodes[i]->Dump(fpOut);
851
            }
852
        }
853
    }
854
855
    fflush(fpOut);
856
}
857
858
#endif  // DEBUG
859
860
/*=====================================================================
861
 *                      class TABINDNode
862
 *====================================================================*/
863
864
/**********************************************************************
865
 *                   TABINDNode::TABINDNode()
866
 *
867
 * Constructor.
868
 **********************************************************************/
869
TABINDNode::TABINDNode(TABAccess eAccessMode /*=TABRead*/)
870
519
    : m_fp(nullptr), m_eAccessMode(eAccessMode), m_poCurChildNode(nullptr),
871
519
      m_poParentNodeRef(nullptr), m_poBlockManagerRef(nullptr),
872
519
      m_nSubTreeDepth(0), m_nKeyLength(0), m_eFieldType(TABFUnknown),
873
519
      m_bUnique(FALSE), m_nCurDataBlockPtr(0), m_nCurIndexEntry(0),
874
519
      m_poDataBlock(nullptr), m_numEntriesInNode(0), m_nPrevNodePtr(0),
875
519
      m_nNextNodePtr(0)
876
519
{
877
519
}
878
879
/**********************************************************************
880
 *                   TABINDNode::~TABINDNode()
881
 *
882
 * Destructor.
883
 **********************************************************************/
884
TABINDNode::~TABINDNode()
885
519
{
886
519
    if (m_poCurChildNode)
887
78
        delete m_poCurChildNode;
888
889
519
    if (m_poDataBlock)
890
519
        delete m_poDataBlock;
891
519
}
892
893
/**********************************************************************
894
 *                   TABINDNode::InitNode()
895
 *
896
 * Init a node... this function can be used either to initialize a new
897
 * node, or to make it point to a new data block in the file.
898
 *
899
 * By default, this call will read the data from the file at the
900
 * specified location if necessary, and leave the object ready to be searched.
901
 *
902
 * In write access, if the block does not exist (i.e. nBlockPtr=0) then a
903
 * new one is created and initialized.
904
 *
905
 * poParentNode is used in write access in order to update the parent node
906
 * when this node becomes full and has to be split.
907
 *
908
 * Returns 0 on success, -1 on error.
909
 **********************************************************************/
910
int TABINDNode::InitNode(VSILFILE *fp, int nBlockPtr, int nKeyLength,
911
                         int nSubTreeDepth, GBool bUnique,
912
                         TABBinBlockManager *poBlockMgr /*=NULL*/,
913
                         TABINDNode *poParentNode /*=NULL*/,
914
                         int nPrevNodePtr /*=0*/, int nNextNodePtr /*=0*/)
915
4.75k
{
916
    /*-----------------------------------------------------------------
917
     * If the block already points to the right block, then don't do
918
     * anything here.
919
     *----------------------------------------------------------------*/
920
4.75k
    if (m_fp == fp && nBlockPtr > 0 && m_nCurDataBlockPtr == nBlockPtr)
921
2.72k
        return 0;
922
923
    // Keep track of some info
924
2.02k
    m_fp = fp;
925
2.02k
    m_nKeyLength = nKeyLength;
926
2.02k
    m_nSubTreeDepth = nSubTreeDepth;
927
2.02k
    m_nCurDataBlockPtr = nBlockPtr;
928
2.02k
    m_bUnique = bUnique;
929
930
    // Do not overwrite the following values if we receive NULL (the defaults)
931
2.02k
    if (poBlockMgr)
932
519
        m_poBlockManagerRef = poBlockMgr;
933
2.02k
    if (poParentNode)
934
78
        m_poParentNodeRef = poParentNode;
935
936
    // Set some defaults
937
2.02k
    m_numEntriesInNode = 0;
938
2.02k
    m_nPrevNodePtr = nPrevNodePtr;
939
2.02k
    m_nNextNodePtr = nNextNodePtr;
940
941
2.02k
    m_nCurIndexEntry = 0;
942
943
    /*-----------------------------------------------------------------
944
     * Init RawBinBlock
945
     * The node's buffer has to be created with read/write access since
946
     * the index is a very dynamic structure!
947
     *----------------------------------------------------------------*/
948
2.02k
    if (m_poDataBlock == nullptr)
949
519
        m_poDataBlock = new TABRawBinBlock(TABReadWrite, TRUE);
950
951
2.02k
    if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) &&
952
2.02k
        nBlockPtr == 0 && m_poBlockManagerRef)
953
0
    {
954
        /*-------------------------------------------------------------
955
         * Write access: Create and init a new block
956
         *------------------------------------------------------------*/
957
0
        m_nCurDataBlockPtr = m_poBlockManagerRef->AllocNewBlock();
958
0
        m_poDataBlock->InitNewBlock(m_fp, 512, m_nCurDataBlockPtr);
959
960
0
        m_poDataBlock->WriteInt32(m_numEntriesInNode);
961
0
        m_poDataBlock->WriteInt32(m_nPrevNodePtr);
962
0
        m_poDataBlock->WriteInt32(m_nNextNodePtr);
963
0
    }
964
2.02k
    else
965
2.02k
    {
966
2.02k
        CPLAssert(m_nCurDataBlockPtr > 0);
967
        /*-------------------------------------------------------------
968
         * Read the data block from the file, applies to read access, or
969
         * to write access (to modify an existing block)
970
         *------------------------------------------------------------*/
971
2.02k
        if (m_poDataBlock->ReadFromFile(m_fp, m_nCurDataBlockPtr, 512) != 0)
972
1.02k
        {
973
            // CPLError() has already been called.
974
1.02k
            return -1;
975
1.02k
        }
976
977
1.00k
        m_poDataBlock->GotoByteInBlock(0);
978
1.00k
        m_numEntriesInNode = m_poDataBlock->ReadInt32();
979
1.00k
        m_nPrevNodePtr = m_poDataBlock->ReadInt32();
980
1.00k
        m_nNextNodePtr = m_poDataBlock->ReadInt32();
981
1.00k
    }
982
983
    // m_poDataBlock is now positioned at the beginning of the key entries
984
985
1.00k
    return 0;
986
2.02k
}
987
988
/**********************************************************************
989
 *                   TABINDNode::GotoNodePtr()
990
 *
991
 * Move to the specified node ptr, and read the new node data from the file.
992
 *
993
 * This is just a cover function on top of InitNode()
994
 **********************************************************************/
995
int TABINDNode::GotoNodePtr(GInt32 nNewNodePtr)
996
4.23k
{
997
    // First flush current changes if any.
998
4.23k
    if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) &&
999
4.23k
        m_poDataBlock && m_poDataBlock->CommitToFile() != 0)
1000
0
        return -1;
1001
1002
4.23k
    CPLAssert(nNewNodePtr % 512 == 0);
1003
1004
    // Then move to the requested location.
1005
4.23k
    return InitNode(m_fp, nNewNodePtr, m_nKeyLength, m_nSubTreeDepth,
1006
4.23k
                    m_bUnique);
1007
4.23k
}
1008
1009
/**********************************************************************
1010
 *                   TABINDNode::ReadIndexEntry()
1011
 *
1012
 * Read the key value and record/node ptr for the specified index entry
1013
 * inside the current node data.
1014
 *
1015
 * nEntryNo is the 0-based index of the index entry that we are interested
1016
 * in inside the current node.
1017
 *
1018
 * Returns the record/node ptr, and copies the key value inside the
1019
 * buffer pointed to by *pKeyValue... this assumes that *pKeyValue points
1020
 * to a buffer big enough to hold the key value (m_nKeyLength bytes).
1021
 * If pKeyValue == NULL, then this parameter is ignored and the key value
1022
 * is not copied.
1023
 **********************************************************************/
1024
GInt32 TABINDNode::ReadIndexEntry(int nEntryNo, GByte *pKeyValue)
1025
7.81k
{
1026
7.81k
    GInt32 nRecordPtr = 0;
1027
7.81k
    if (nEntryNo >= 0 && nEntryNo < m_numEntriesInNode)
1028
7.81k
    {
1029
7.81k
        if (pKeyValue)
1030
0
        {
1031
0
            m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4));
1032
0
            m_poDataBlock->ReadBytes(m_nKeyLength, pKeyValue);
1033
0
        }
1034
7.81k
        else
1035
7.81k
        {
1036
7.81k
            m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4) +
1037
7.81k
                                           m_nKeyLength);
1038
7.81k
        }
1039
1040
7.81k
        nRecordPtr = m_poDataBlock->ReadInt32();
1041
7.81k
    }
1042
1043
7.81k
    return nRecordPtr;
1044
7.81k
}
1045
1046
/**********************************************************************
1047
 *                   TABINDNode::IndexKeyCmp()
1048
 *
1049
 * Compare the specified index entry with the key value, and
1050
 * return 0 if equal, an integer less than 0 if key is smaller than
1051
 * index entry, and an integer greater than 0 if key is bigger than
1052
 * index entry.
1053
 *
1054
 * nEntryNo is the 0-based index of the index entry that we are interested
1055
 * in inside the current node.
1056
 **********************************************************************/
1057
int TABINDNode::IndexKeyCmp(const GByte *pKeyValue, int nEntryNo)
1058
263k
{
1059
263k
    CPLAssert(pKeyValue);
1060
263k
    CPLAssert(nEntryNo >= 0 && nEntryNo < m_numEntriesInNode);
1061
1062
263k
    m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4));
1063
263k
    CPLAssert(m_nKeyLength <= 255);
1064
263k
    GByte abyKey[255];
1065
263k
    if (m_poDataBlock->ReadBytes(m_nKeyLength, abyKey) != 0)
1066
847
        return -1;
1067
263k
    return memcmp(pKeyValue, abyKey, m_nKeyLength);
1068
263k
}
1069
1070
/**********************************************************************
1071
 *                   TABINDNode::SetFieldType()
1072
 *
1073
 * Sets the field type for the current index and recursively set all
1074
 * children as well.
1075
 * This information will then be used in building the key values, etc.
1076
 *
1077
 * Returns 0 on success, -1 on error.
1078
 **********************************************************************/
1079
int TABINDNode::SetFieldType(TABFieldType eType)
1080
201
{
1081
201
    if (m_fp == nullptr)
1082
0
    {
1083
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1084
0
                 "TABINDNode::SetFieldType(): File has not been opened yet!");
1085
0
        return -1;
1086
0
    }
1087
1088
    /*-----------------------------------------------------------------
1089
     * Validate field type with key length
1090
     *----------------------------------------------------------------*/
1091
201
    if ((eType == TABFInteger && m_nKeyLength != 4) ||
1092
201
        (eType == TABFSmallInt && m_nKeyLength != 2) ||
1093
201
        (eType == TABFLargeInt && m_nKeyLength != 8) ||
1094
201
        (eType == TABFFloat && m_nKeyLength != 8) ||
1095
201
        (eType == TABFDecimal && m_nKeyLength != 8) ||
1096
201
        (eType == TABFDate && m_nKeyLength != 4) ||
1097
201
        (eType == TABFTime && m_nKeyLength != 4) ||
1098
201
        (eType == TABFDateTime && m_nKeyLength != 8) ||
1099
201
        (eType == TABFLogical && m_nKeyLength != 4))
1100
128
    {
1101
128
        CPLError(CE_Failure, CPLE_IllegalArg,
1102
128
                 "Index key length (%d) does not match field type (%s).",
1103
128
                 m_nKeyLength, TABFIELDTYPE_2_STRING(eType));
1104
128
        return -1;
1105
128
    }
1106
1107
73
    m_eFieldType = eType;
1108
1109
    /*-----------------------------------------------------------------
1110
     * Pass the field type info to child nodes
1111
     *----------------------------------------------------------------*/
1112
73
    if (m_poCurChildNode)
1113
0
        return m_poCurChildNode->SetFieldType(eType);
1114
1115
73
    return 0;
1116
73
}
1117
1118
/**********************************************************************
1119
 *                   TABINDNode::FindFirst()
1120
 *
1121
 * Start a new search in this node and its children for a key value.
1122
 * If the index is not unique, then FindNext() can be used to return
1123
 * the other values that correspond to the key.
1124
 *
1125
 * Return value:
1126
 *  - the key's corresponding record number in the .DAT file (greater than 0)
1127
 *  - 0 if the key was not found
1128
 *  - or -1 if an error happened
1129
 **********************************************************************/
1130
GInt32 TABINDNode::FindFirst(const GByte *pKeyValue)
1131
9.72k
{
1132
9.72k
    std::set<int> oSetVisitedNodePtr;
1133
9.72k
    return FindFirst(pKeyValue, oSetVisitedNodePtr);
1134
9.72k
}
1135
1136
GInt32 TABINDNode::FindFirst(const GByte *pKeyValue,
1137
                             std::set<int> &oSetVisitedNodePtr)
1138
13.0k
{
1139
13.0k
    if (m_poDataBlock == nullptr)
1140
0
    {
1141
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1142
0
                 "TABINDNode::Search(): Node has not been initialized yet!");
1143
0
        return -1;
1144
0
    }
1145
1146
    /*-----------------------------------------------------------------
1147
     * Unless something has been broken, this method will be called by our
1148
     * parent node after it has established that we are the best candidate
1149
     * to contain the first instance of the key value.  So there is no
1150
     * need to look in the previous or next nodes in the chain... if the
1151
     * value is not found in the current node block then it is not present
1152
     * in the index at all.
1153
     *
1154
     * m_nCurIndexEntry will be used to keep track of the search pointer
1155
     * when FindNext() will be used.
1156
     *----------------------------------------------------------------*/
1157
13.0k
    m_nCurIndexEntry = 0;
1158
1159
13.0k
    if (m_nSubTreeDepth == 1)
1160
4.48k
    {
1161
        /*-------------------------------------------------------------
1162
         * Leaf node level... we look for an exact match
1163
         *------------------------------------------------------------*/
1164
79.1k
        while (m_nCurIndexEntry < m_numEntriesInNode)
1165
77.0k
        {
1166
77.0k
            int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry);
1167
77.0k
            if (nCmpStatus > 0)
1168
74.6k
            {
1169
                /* Not there yet... (pKey > IndexEntry) */
1170
74.6k
                m_nCurIndexEntry++;
1171
74.6k
            }
1172
2.33k
            else if (nCmpStatus == 0)
1173
1.59k
            {
1174
                /* Found it!  Return the record number */
1175
1.59k
                return ReadIndexEntry(m_nCurIndexEntry, nullptr);
1176
1.59k
            }
1177
743
            else
1178
743
            {
1179
                /* Item does not exist... return 0 */
1180
743
                return 0;
1181
743
            }
1182
77.0k
        }
1183
4.48k
    }
1184
8.53k
    else
1185
8.53k
    {
1186
        /*-------------------------------------------------------------
1187
         * Index Node: Find the child node that is the best candidate to
1188
         * contain the value
1189
         *
1190
         * In the index tree at the node level, for each node entry inside
1191
         * the parent node, the key value (in the parent) corresponds to
1192
         * the value of the first key that you will find when you access
1193
         * the corresponding child node.
1194
         *
1195
         * This means that to find the child that contains the searched
1196
         * key, we look for the first index key >= pKeyValue and the child
1197
         * node that we are looking for is the one that precedes it.
1198
         *
1199
         * If the first key in the list is >= pKeyValue then this means
1200
         * that the pKeyValue does not exist in our children and we just
1201
         * return 0.  We do not bother searching the previous node at the
1202
         * same level since this is the responsibility of our parent.
1203
         *
1204
         * The same way if the last indexkey in this node is < pKeyValue
1205
         * we won't bother searching the next node since this should also
1206
         * be taken care of by our parent.
1207
         *------------------------------------------------------------*/
1208
189k
        while (m_nCurIndexEntry < m_numEntriesInNode)
1209
186k
        {
1210
186k
            int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry);
1211
1212
186k
            if (nCmpStatus > 0 && m_nCurIndexEntry + 1 < m_numEntriesInNode)
1213
181k
            {
1214
                /* Not there yet... (pKey > IndexEntry) */
1215
181k
                m_nCurIndexEntry++;
1216
181k
            }
1217
5.41k
            else
1218
5.41k
            {
1219
                /*-----------------------------------------------------
1220
                 * We either found an indexkey >= pKeyValue or reached
1221
                 * the last entry in this node... still have to decide
1222
                 * what we're going to do...
1223
                 *----------------------------------------------------*/
1224
5.41k
                if (nCmpStatus < 0 && m_nCurIndexEntry == 0)
1225
304
                {
1226
                    /*-------------------------------------------------
1227
                     * First indexkey in block is > pKeyValue...
1228
                     * the key definitely does not exist in our children.
1229
                     * However, we still want to drill down the rest of the
1230
                     * tree because this function is also used when looking
1231
                     * for a node to insert a new value.
1232
                     *-------------------------------------------------*/
1233
                    // Nothing special to do... just continue processing.
1234
304
                }
1235
1236
                /*-----------------------------------------------------
1237
                 * If we found an node for which pKeyValue < indexkey
1238
                 * (or pKeyValue <= indexkey for non-unique indexes) then
1239
                 * we access the preceding child node.
1240
                 *
1241
                 * Note that for indexkey == pKeyValue in non-unique indexes
1242
                 * we also check in the preceding node because when keys
1243
                 * are not unique then there are chances that the requested
1244
                 * key could also be found at the end of the preceding node.
1245
                 * In this case, if we don't find the key in the preceding
1246
                 * node then we'll do a second search in the current node.
1247
                 *----------------------------------------------------*/
1248
5.41k
                int numChildrenToVisit = 1;
1249
5.41k
                if (m_nCurIndexEntry > 0 &&
1250
5.41k
                    (nCmpStatus < 0 || (nCmpStatus == 0 && !m_bUnique)))
1251
4.71k
                {
1252
4.71k
                    m_nCurIndexEntry--;
1253
4.71k
                    if (nCmpStatus == 0)
1254
1.57k
                        numChildrenToVisit = 2;
1255
4.71k
                }
1256
1257
                /*-----------------------------------------------------
1258
                 * OK, now it is time to load/access the candidate child nodes.
1259
                 *----------------------------------------------------*/
1260
5.41k
                int nRetValue = 0;
1261
5.41k
                for (int iChild = 0;
1262
9.85k
                     nRetValue == 0 && iChild < numChildrenToVisit; iChild++)
1263
6.21k
                {
1264
                    // If we're doing a second pass then jump to next entry
1265
6.21k
                    if (iChild > 0)
1266
799
                        m_nCurIndexEntry++;
1267
1268
6.21k
                    int nChildNodePtr =
1269
6.21k
                        ReadIndexEntry(m_nCurIndexEntry, nullptr);
1270
6.21k
                    if (nChildNodePtr <= 0)
1271
1.14k
                    {
1272
                        /* Invalid child node??? */
1273
1.14k
                        nRetValue = 0;
1274
1.14k
                        continue;
1275
1.14k
                    }
1276
5.06k
                    else if (oSetVisitedNodePtr.find(nChildNodePtr) !=
1277
5.06k
                             oSetVisitedNodePtr.end())
1278
276
                    {
1279
276
                        CPLError(CE_Failure, CPLE_AppDefined,
1280
276
                                 "Invalid child node pointer structure");
1281
276
                        return -1;
1282
276
                    }
1283
4.79k
                    else if ((nChildNodePtr % 512) != 0)
1284
511
                    {
1285
511
                        CPLError(CE_Failure, CPLE_AppDefined,
1286
511
                                 "Invalid child node pointer");
1287
511
                        return -1;
1288
511
                    }
1289
4.27k
                    else if (m_poCurChildNode == nullptr)
1290
78
                    {
1291
                        /* Child node has never been initialized...do it now!*/
1292
1293
78
                        m_poCurChildNode = new TABINDNode(m_eAccessMode);
1294
78
                        if (m_poCurChildNode->InitNode(
1295
78
                                m_fp, nChildNodePtr, m_nKeyLength,
1296
78
                                m_nSubTreeDepth - 1, m_bUnique,
1297
78
                                m_poBlockManagerRef, this) != 0 ||
1298
78
                            m_poCurChildNode->SetFieldType(m_eFieldType) != 0)
1299
46
                        {
1300
                            // An error happened... and was already reported
1301
46
                            return -1;
1302
46
                        }
1303
78
                    }
1304
1305
4.23k
                    if (m_poCurChildNode->GotoNodePtr(nChildNodePtr) != 0)
1306
946
                    {
1307
                        // An error happened and has already been reported
1308
946
                        return -1;
1309
946
                    }
1310
1311
3.28k
                    oSetVisitedNodePtr.insert(nChildNodePtr);
1312
3.28k
                    nRetValue = m_poCurChildNode->FindFirst(pKeyValue,
1313
3.28k
                                                            oSetVisitedNodePtr);
1314
3.28k
                } /*for iChild*/
1315
1316
3.63k
                return nRetValue;
1317
5.41k
            }  // else
1318
186k
        }      // while numEntries
1319
1320
        // No node was found that contains the key value.
1321
        // We should never get here... only leaf nodes should return 0
1322
3.11k
        CPLAssert(false);
1323
3.11k
        return 0;
1324
8.53k
    }
1325
1326
2.14k
    return 0;  // Not found
1327
13.0k
}
1328
1329
/**********************************************************************
1330
 *                   TABINDNode::FindNext()
1331
 *
1332
 * Continue the search previously started by FindFirst() in this node
1333
 * and its children for a key value.
1334
 *
1335
 * Return value:
1336
 *  - the key's corresponding record number in the .DAT file (greater than 0)
1337
 *  - 0 if the key was not found
1338
 *  - or -1 if an error happened
1339
 **********************************************************************/
1340
GInt32 TABINDNode::FindNext(GByte *pKeyValue)
1341
0
{
1342
0
    if (m_poDataBlock == nullptr)
1343
0
    {
1344
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1345
0
                 "TABINDNode::Search(): Node has not been initialized yet!");
1346
0
        return -1;
1347
0
    }
1348
1349
    /*-----------------------------------------------------------------
1350
     * m_nCurIndexEntry is the index of the last item that has been
1351
     * returned by FindFirst()/FindNext().
1352
     *----------------------------------------------------------------*/
1353
1354
0
    if (m_nSubTreeDepth == 1)
1355
0
    {
1356
        /*-------------------------------------------------------------
1357
         * Leaf node level... check if the next entry is an exact match
1358
         *------------------------------------------------------------*/
1359
0
        m_nCurIndexEntry++;
1360
0
        if (m_nCurIndexEntry >= m_numEntriesInNode && m_nNextNodePtr > 0)
1361
0
        {
1362
            // We're at the end of a node ... continue with next node
1363
0
            GotoNodePtr(m_nNextNodePtr);
1364
0
            m_nCurIndexEntry = 0;
1365
0
        }
1366
1367
0
        if (m_nCurIndexEntry < m_numEntriesInNode &&
1368
0
            IndexKeyCmp(pKeyValue, m_nCurIndexEntry) == 0)
1369
0
        {
1370
            /* Found it!  Return the record number */
1371
0
            return ReadIndexEntry(m_nCurIndexEntry, nullptr);
1372
0
        }
1373
0
        else
1374
0
        {
1375
            /* No more items with that key... return 0 */
1376
0
            return 0;
1377
0
        }
1378
0
    }
1379
0
    else
1380
0
    {
1381
        /*-------------------------------------------------------------
1382
         * Index Node: just pass the search to this child node.
1383
         *------------------------------------------------------------*/
1384
0
        while (m_nCurIndexEntry < m_numEntriesInNode)
1385
0
        {
1386
0
            if (m_poCurChildNode != nullptr)
1387
0
                return m_poCurChildNode->FindNext(pKeyValue);
1388
0
        }
1389
0
    }
1390
1391
    // No more nodes were found that contain the key value.
1392
0
    return 0;
1393
0
}
1394
1395
/**********************************************************************
1396
 *                   TABINDNode::CommitToFile()
1397
 *
1398
 * For write access, write current block and its children to file.
1399
 *
1400
 * note: TABRawBinBlock::CommitToFile() does nothing unless the block has
1401
 *       been modified.  (it has an internal bModified flag)
1402
 *
1403
 * Returns 0 on success, -1 on error.
1404
 **********************************************************************/
1405
int TABINDNode::CommitToFile()
1406
0
{
1407
0
    if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
1408
0
        m_poDataBlock == nullptr)
1409
0
        return -1;
1410
1411
0
    if (m_poCurChildNode)
1412
0
    {
1413
0
        if (m_poCurChildNode->CommitToFile() != 0)
1414
0
            return -1;
1415
1416
0
        m_nSubTreeDepth = m_poCurChildNode->GetSubTreeDepth() + 1;
1417
0
    }
1418
1419
0
    return m_poDataBlock->CommitToFile();
1420
0
}
1421
1422
/**********************************************************************
1423
 *                   TABINDNode::AddEntry()
1424
 *
1425
 * Add an .DAT record entry for pKeyValue in this index
1426
 *
1427
 * nRecordNo is the .DAT record number, record numbers start at 1.
1428
 *
1429
 * In order to insert a new value, the root node first does a FindFirst()
1430
 * that will load the whole tree branch up to the insertion point.
1431
 * Then AddEntry() is recursively called up to the leaf node level for
1432
 * the insertion of the actual value.
1433
 * If the leaf node is full then it will be split and if necessary the
1434
 * split will propagate up in the tree through the pointer that each node
1435
 * has on its parent.
1436
 *
1437
 * If bAddInThisNodeOnly=TRUE, then the entry is added only locally and
1438
 * we do not try to update the child node.  This is used when the parent
1439
 * of a node that is being split has to be updated.
1440
 *
1441
 * bInsertAfterCurChild forces the insertion to happen immediately after
1442
 * the m_nCurIndexEntry.  This works only when bAddInThisNodeOnly=TRUE.
1443
 * The default is to search the node for a an insertion point.
1444
 *
1445
 * Returns 0 on success, -1 on error
1446
 **********************************************************************/
1447
int TABINDNode::AddEntry(GByte *pKeyValue, GInt32 nRecordNo,
1448
                         GBool bAddInThisNodeOnly /*=FALSE*/,
1449
                         GBool bInsertAfterCurChild /*=FALSE*/,
1450
                         GBool bMakeNewEntryCurChild /*=FALSE*/)
1451
0
{
1452
0
    if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
1453
0
        m_poDataBlock == nullptr)
1454
0
        return -1;
1455
1456
    /*-----------------------------------------------------------------
1457
     * If I'm the root node, then do a FindFirst() to init all the nodes
1458
     * and to make all of them point to the insertion point.
1459
     *----------------------------------------------------------------*/
1460
0
    if (m_poParentNodeRef == nullptr && !bAddInThisNodeOnly)
1461
0
    {
1462
0
        if (FindFirst(pKeyValue) < 0)
1463
0
            return -1;  // Error happened and has already been reported.
1464
0
    }
1465
1466
0
    if (m_poCurChildNode && !bAddInThisNodeOnly)
1467
0
    {
1468
0
        CPLAssert(m_nSubTreeDepth > 1);
1469
        /*-------------------------------------------------------------
1470
         * Propagate the call down to our children
1471
         * Note: this recursive call could result in new levels of nodes
1472
         * being added under our feet by SplitRootnode() so it is very
1473
         * important to return right after this call or we might not be
1474
         * able to recognize this node at the end of the call!
1475
         *------------------------------------------------------------*/
1476
0
        return m_poCurChildNode->AddEntry(pKeyValue, nRecordNo);
1477
0
    }
1478
0
    else
1479
0
    {
1480
        /*-------------------------------------------------------------
1481
         * OK, we're a leaf node... this is where the real work happens!!!
1482
         *------------------------------------------------------------*/
1483
0
        CPLAssert(m_nSubTreeDepth == 1 || bAddInThisNodeOnly);
1484
1485
        /*-------------------------------------------------------------
1486
         * First thing to do is make sure that there is room for a new
1487
         * entry in this node, and to split it if necessary.
1488
         *------------------------------------------------------------*/
1489
0
        if (GetNumEntries() == GetMaxNumEntries())
1490
0
        {
1491
0
            if (m_poParentNodeRef == nullptr)
1492
0
            {
1493
                /*-----------------------------------------------------
1494
                 * Splitting the root node adds one level to the tree, so
1495
                 * after splitting we just redirect the call to our child.
1496
                 *----------------------------------------------------*/
1497
0
                if (SplitRootNode() != 0)
1498
0
                    return -1;  // Error happened and has already been reported
1499
1500
0
                CPLAssert(m_poCurChildNode);
1501
0
                CPLAssert(m_nSubTreeDepth > 1);
1502
0
                return m_poCurChildNode->AddEntry(
1503
0
                    pKeyValue, nRecordNo, bAddInThisNodeOnly,
1504
0
                    bInsertAfterCurChild, bMakeNewEntryCurChild);
1505
0
            }
1506
0
            else
1507
0
            {
1508
                /*-----------------------------------------------------
1509
                 * Splitting a regular node will leave it 50% full.
1510
                 *----------------------------------------------------*/
1511
0
                if (SplitNode() != 0)
1512
0
                    return -1;
1513
0
            }
1514
0
        }
1515
1516
        /*-------------------------------------------------------------
1517
         * Insert new key/value at the right position in node.
1518
         *------------------------------------------------------------*/
1519
0
        if (InsertEntry(pKeyValue, nRecordNo, bInsertAfterCurChild,
1520
0
                        bMakeNewEntryCurChild) != 0)
1521
0
            return -1;
1522
0
    }
1523
1524
0
    return 0;
1525
0
}
1526
1527
/**********************************************************************
1528
 *                   TABINDNode::InsertEntry()
1529
 *
1530
 * (private method)
1531
 *
1532
 * Insert a key/value pair in the current node buffer.
1533
 *
1534
 * Returns 0 on success, -1 on error
1535
 **********************************************************************/
1536
int TABINDNode::InsertEntry(GByte *pKeyValue, GInt32 nRecordNo,
1537
                            GBool bInsertAfterCurChild /*=FALSE*/,
1538
                            GBool bMakeNewEntryCurChild /*=FALSE*/)
1539
0
{
1540
0
    int iInsertAt = 0;
1541
1542
0
    if (GetNumEntries() >= GetMaxNumEntries())
1543
0
    {
1544
0
        CPLError(CE_Failure, CPLE_AssertionFailed,
1545
0
                 "Node is full!  Cannot insert key!");
1546
0
        return -1;
1547
0
    }
1548
1549
    /*-----------------------------------------------------------------
1550
     * Find the spot where the key belongs
1551
     *----------------------------------------------------------------*/
1552
0
    if (bInsertAfterCurChild)
1553
0
    {
1554
0
        iInsertAt = m_nCurIndexEntry + 1;
1555
0
    }
1556
0
    else
1557
0
    {
1558
0
        while (iInsertAt < m_numEntriesInNode)
1559
0
        {
1560
0
            int nCmpStatus = IndexKeyCmp(pKeyValue, iInsertAt);
1561
0
            if (nCmpStatus <= 0)
1562
0
            {
1563
0
                break;
1564
0
            }
1565
0
            iInsertAt++;
1566
0
        }
1567
0
    }
1568
1569
0
    m_poDataBlock->GotoByteInBlock(12 + iInsertAt * (m_nKeyLength + 4));
1570
1571
    /*-----------------------------------------------------------------
1572
     * Shift all entries that follow in the array
1573
     *----------------------------------------------------------------*/
1574
0
    if (iInsertAt < m_numEntriesInNode)
1575
0
    {
1576
        // Since we use memmove() directly, we need to inform
1577
        // m_poDataBlock that the upper limit of the buffer will move
1578
0
        m_poDataBlock->GotoByteInBlock(12 + (m_numEntriesInNode + 1) *
1579
0
                                                (m_nKeyLength + 4));
1580
0
        m_poDataBlock->GotoByteInBlock(12 + iInsertAt * (m_nKeyLength + 4));
1581
1582
0
        memmove(m_poDataBlock->GetCurDataPtr() + (m_nKeyLength + 4),
1583
0
                m_poDataBlock->GetCurDataPtr(),
1584
0
                static_cast<size_t>(m_numEntriesInNode - iInsertAt) *
1585
0
                    (m_nKeyLength + 4));
1586
0
    }
1587
1588
    /*-----------------------------------------------------------------
1589
     * Write new entry
1590
     *----------------------------------------------------------------*/
1591
0
    m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue);
1592
0
    m_poDataBlock->WriteInt32(nRecordNo);
1593
1594
0
    m_numEntriesInNode++;
1595
0
    m_poDataBlock->GotoByteInBlock(0);
1596
0
    m_poDataBlock->WriteInt32(m_numEntriesInNode);
1597
1598
0
    if (bMakeNewEntryCurChild)
1599
0
        m_nCurIndexEntry = iInsertAt;
1600
0
    else if (m_nCurIndexEntry >= iInsertAt)
1601
0
        m_nCurIndexEntry++;
1602
1603
    /*-----------------------------------------------------------------
1604
     * If we replaced the first entry in the node, then this node's key
1605
     * changes and we have to update the reference in the parent node.
1606
     *----------------------------------------------------------------*/
1607
0
    if (iInsertAt == 0 && m_poParentNodeRef)
1608
0
    {
1609
0
        if (m_poParentNodeRef->UpdateCurChildEntry(GetNodeKey(),
1610
0
                                                   GetNodeBlockPtr()) != 0)
1611
0
            return -1;
1612
0
    }
1613
1614
0
    return 0;
1615
0
}
1616
1617
/**********************************************************************
1618
 *                   TABINDNode::UpdateCurChildEntry()
1619
 *
1620
 * Update the key for the current child node.  This method is called by
1621
 * the child when its first entry (defining its node key) is changed.
1622
 *
1623
 * Returns 0 on success, -1 on error
1624
 **********************************************************************/
1625
int TABINDNode::UpdateCurChildEntry(GByte *pKeyValue, GInt32 nRecordNo)
1626
0
{
1627
1628
    /*-----------------------------------------------------------------
1629
     * Update current child entry with the info for the first node.
1630
     *
1631
     * For some reason, the key for first entry of the first node of each
1632
     * level has to be set to 0 except for the leaf level.
1633
     *----------------------------------------------------------------*/
1634
0
    m_poDataBlock->GotoByteInBlock(12 + m_nCurIndexEntry * (m_nKeyLength + 4));
1635
1636
0
    int ret;
1637
0
    if (m_nCurIndexEntry == 0 && m_nSubTreeDepth > 1 && m_nPrevNodePtr == 0)
1638
0
    {
1639
0
        ret = m_poDataBlock->WriteZeros(m_nKeyLength);
1640
0
    }
1641
0
    else
1642
0
    {
1643
0
        ret = m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue);
1644
0
    }
1645
0
    if (ret == 0)
1646
0
        ret = m_poDataBlock->WriteInt32(nRecordNo);
1647
1648
0
    return ret;
1649
0
}
1650
1651
/**********************************************************************
1652
 *                   TABINDNode::UpdateSplitChild()
1653
 *
1654
 * Update the key and/or record ptr information corresponding to the
1655
 * current child node.
1656
 *
1657
 * Returns 0 on success, -1 on error
1658
 **********************************************************************/
1659
int TABINDNode::UpdateSplitChild(GByte *pKeyValue1, GInt32 nRecordNo1,
1660
                                 GByte *pKeyValue2, GInt32 nRecordNo2,
1661
                                 int nNewCurChildNo /* 1 or 2 */)
1662
0
{
1663
1664
    /*-----------------------------------------------------------------
1665
     * Update current child entry with the info for the first node.
1666
     *
1667
     * For some reason, the key for first entry of the first node of each
1668
     * level has to be set to 0 except for the leaf level.
1669
     *----------------------------------------------------------------*/
1670
0
    m_poDataBlock->GotoByteInBlock(12 + m_nCurIndexEntry * (m_nKeyLength + 4));
1671
1672
0
    if (m_nCurIndexEntry == 0 && m_nSubTreeDepth > 1 && m_nPrevNodePtr == 0)
1673
0
    {
1674
0
        m_poDataBlock->WriteZeros(m_nKeyLength);
1675
0
    }
1676
0
    else
1677
0
    {
1678
0
        m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue1);
1679
0
    }
1680
0
    m_poDataBlock->WriteInt32(nRecordNo1);
1681
1682
    /*-----------------------------------------------------------------
1683
     * Add an entry for the second node after the current one and ask
1684
     * AddEntry() to update m_nCurIndexEntry if the new node should
1685
     * become the new current child.
1686
     *----------------------------------------------------------------*/
1687
0
    if (AddEntry(pKeyValue2, nRecordNo2, TRUE, /* bInThisNodeOnly */
1688
0
                 TRUE,                         /* bInsertAfterCurChild */
1689
0
                 (nNewCurChildNo == 2)) != 0)
1690
0
    {
1691
0
        return -1;
1692
0
    }
1693
1694
0
    return 0;
1695
0
}
1696
1697
/**********************************************************************
1698
 *                   TABINDNode::SplitNode()
1699
 *
1700
 * (private method)
1701
 *
1702
 * Split a node, update the references in the parent node, etc.
1703
 * Note that Root Nodes cannot be split using this method... SplitRootNode()
1704
 * should be used instead.
1705
 *
1706
 * The node is split in a way that the current child stays inside this
1707
 * node object, and a new node is created for the other half of the
1708
 * entries.  This way, the object references in this node's parent and in its
1709
 * current child all remain valid.  The new node is not kept in memory,
1710
 * it is written to disk right away.
1711
 *
1712
 * Returns 0 on success, -1 on error
1713
 **********************************************************************/
1714
int TABINDNode::SplitNode()
1715
0
{
1716
0
    CPLAssert(m_numEntriesInNode >= 2);
1717
0
    CPLAssert(m_poParentNodeRef);  // This func. does not work for root nodes
1718
1719
    /*-----------------------------------------------------------------
1720
     * Prepare new node
1721
     *----------------------------------------------------------------*/
1722
0
    int numInNode1 = (m_numEntriesInNode + 1) / 2;
1723
0
    int numInNode2 = m_numEntriesInNode - numInNode1;
1724
1725
0
    TABINDNode *poNewNode = new TABINDNode(m_eAccessMode);
1726
1727
0
    if (m_nCurIndexEntry < numInNode1)
1728
0
    {
1729
        /*-------------------------------------------------------------
1730
         * We will move the second half of the array to a new node.
1731
         *------------------------------------------------------------*/
1732
0
        if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth,
1733
0
                                m_bUnique, m_poBlockManagerRef,
1734
0
                                m_poParentNodeRef, GetNodeBlockPtr(),
1735
0
                                m_nNextNodePtr) != 0 ||
1736
0
            poNewNode->SetFieldType(m_eFieldType) != 0)
1737
0
        {
1738
0
            delete poNewNode;
1739
0
            return -1;
1740
0
        }
1741
1742
        // We have to update m_nPrevNodePtr in the node that used to follow
1743
        // the current node and will now follow the new node.
1744
0
        if (m_nNextNodePtr)
1745
0
        {
1746
0
            TABINDNode *poTmpNode = new TABINDNode(m_eAccessMode);
1747
0
            if (poTmpNode->InitNode(
1748
0
                    m_fp, m_nNextNodePtr, m_nKeyLength, m_nSubTreeDepth,
1749
0
                    m_bUnique, m_poBlockManagerRef, m_poParentNodeRef) != 0 ||
1750
0
                poTmpNode->SetPrevNodePtr(poNewNode->GetNodeBlockPtr()) != 0 ||
1751
0
                poTmpNode->CommitToFile() != 0)
1752
0
            {
1753
0
                delete poTmpNode;
1754
0
                delete poNewNode;
1755
0
                return -1;
1756
0
            }
1757
0
            delete poTmpNode;
1758
0
        }
1759
1760
0
        m_nNextNodePtr = poNewNode->GetNodeBlockPtr();
1761
1762
        // Move half the entries to the new block
1763
0
        m_poDataBlock->GotoByteInBlock(12 + numInNode1 * (m_nKeyLength + 4));
1764
1765
0
        if (poNewNode->SetNodeBufferDirectly(
1766
0
                numInNode2, m_poDataBlock->GetCurDataPtr()) != 0)
1767
0
        {
1768
0
            delete poNewNode;
1769
0
            return -1;
1770
0
        }
1771
1772
#ifdef DEBUG
1773
        // Just in case, reset space previously used by moved entries
1774
        memset(m_poDataBlock->GetCurDataPtr(), 0,
1775
               static_cast<size_t>(numInNode2) * (m_nKeyLength + 4));
1776
#endif
1777
        // And update current node members
1778
0
        m_numEntriesInNode = numInNode1;
1779
1780
        // Update parent node with new children info
1781
0
        if (m_poParentNodeRef)
1782
0
        {
1783
0
            if (m_poParentNodeRef->UpdateSplitChild(
1784
0
                    GetNodeKey(), GetNodeBlockPtr(), poNewNode->GetNodeKey(),
1785
0
                    poNewNode->GetNodeBlockPtr(), 1) != 0)
1786
0
            {
1787
0
                delete poNewNode;
1788
0
                return -1;
1789
0
            }
1790
0
        }
1791
0
    }
1792
0
    else
1793
0
    {
1794
        /*-------------------------------------------------------------
1795
         * We will move the first half of the array to a new node.
1796
         *------------------------------------------------------------*/
1797
0
        if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth,
1798
0
                                m_bUnique, m_poBlockManagerRef,
1799
0
                                m_poParentNodeRef, m_nPrevNodePtr,
1800
0
                                GetNodeBlockPtr()) != 0 ||
1801
0
            poNewNode->SetFieldType(m_eFieldType) != 0)
1802
0
        {
1803
0
            delete poNewNode;
1804
0
            return -1;
1805
0
        }
1806
1807
        // We have to update m_nNextNodePtr in the node that used to precede
1808
        // the current node and will now precede the new node.
1809
0
        if (m_nPrevNodePtr)
1810
0
        {
1811
0
            TABINDNode *poTmpNode = new TABINDNode(m_eAccessMode);
1812
0
            if (poTmpNode->InitNode(
1813
0
                    m_fp, m_nPrevNodePtr, m_nKeyLength, m_nSubTreeDepth,
1814
0
                    m_bUnique, m_poBlockManagerRef, m_poParentNodeRef) != 0 ||
1815
0
                poTmpNode->SetNextNodePtr(poNewNode->GetNodeBlockPtr()) != 0 ||
1816
0
                poTmpNode->CommitToFile() != 0)
1817
0
            {
1818
0
                delete poTmpNode;
1819
0
                delete poNewNode;
1820
0
                return -1;
1821
0
            }
1822
0
            delete poTmpNode;
1823
0
        }
1824
1825
0
        m_nPrevNodePtr = poNewNode->GetNodeBlockPtr();
1826
1827
        // Move half the entries to the new block
1828
0
        m_poDataBlock->GotoByteInBlock(12 + 0);
1829
1830
0
        if (poNewNode->SetNodeBufferDirectly(
1831
0
                numInNode1, m_poDataBlock->GetCurDataPtr()) != 0)
1832
0
        {
1833
0
            delete poNewNode;
1834
0
            return -1;
1835
0
        }
1836
1837
        // Shift the second half of the entries to beginning of buffer
1838
0
        memmove(m_poDataBlock->GetCurDataPtr(),
1839
0
                m_poDataBlock->GetCurDataPtr() +
1840
0
                    numInNode1 * (m_nKeyLength + 4),
1841
0
                static_cast<size_t>(numInNode2) * (m_nKeyLength + 4));
1842
1843
#ifdef DEBUG
1844
        // Just in case, reset space previously used by moved entries
1845
        memset(m_poDataBlock->GetCurDataPtr() +
1846
                   static_cast<size_t>(numInNode2) * (m_nKeyLength + 4),
1847
               0, static_cast<size_t>(numInNode1) * (m_nKeyLength + 4));
1848
#endif
1849
1850
        // And update current node members
1851
0
        m_numEntriesInNode = numInNode2;
1852
0
        m_nCurIndexEntry -= numInNode1;
1853
1854
        // Update parent node with new children info
1855
0
        if (m_poParentNodeRef)
1856
0
        {
1857
0
            if (m_poParentNodeRef->UpdateSplitChild(
1858
0
                    poNewNode->GetNodeKey(), poNewNode->GetNodeBlockPtr(),
1859
0
                    GetNodeKey(), GetNodeBlockPtr(), 2) != 0)
1860
0
            {
1861
0
                delete poNewNode;
1862
0
                return -1;
1863
0
            }
1864
0
        }
1865
0
    }
1866
1867
    /*-----------------------------------------------------------------
1868
     * Update current node header
1869
     *----------------------------------------------------------------*/
1870
0
    m_poDataBlock->GotoByteInBlock(0);
1871
0
    m_poDataBlock->WriteInt32(m_numEntriesInNode);
1872
0
    m_poDataBlock->WriteInt32(m_nPrevNodePtr);
1873
0
    m_poDataBlock->WriteInt32(m_nNextNodePtr);
1874
1875
    /*-----------------------------------------------------------------
1876
     * Flush and destroy temporary node
1877
     *----------------------------------------------------------------*/
1878
0
    if (poNewNode->CommitToFile() != 0)
1879
0
    {
1880
0
        delete poNewNode;
1881
0
        return -1;
1882
0
    }
1883
1884
0
    delete poNewNode;
1885
1886
0
    return 0;
1887
0
}
1888
1889
/**********************************************************************
1890
 *                   TABINDNode::SplitRootNode()
1891
 *
1892
 * (private method)
1893
 *
1894
 * Split a Root Node.
1895
 * First, a level of nodes must be added to the tree, then the contents
1896
 * of what used to be the root node is moved 1 level down and then that
1897
 * node is split like a regular node.
1898
 *
1899
 * Returns 0 on success, -1 on error
1900
 **********************************************************************/
1901
int TABINDNode::SplitRootNode()
1902
0
{
1903
    /*-----------------------------------------------------------------
1904
     * Since a root note cannot be split, we add a level of nodes
1905
     * under it and we'll do the split at that level.
1906
     *----------------------------------------------------------------*/
1907
0
    TABINDNode *poNewNode = new TABINDNode(m_eAccessMode);
1908
1909
0
    if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth, m_bUnique,
1910
0
                            m_poBlockManagerRef, this, 0, 0) != 0 ||
1911
0
        poNewNode->SetFieldType(m_eFieldType) != 0)
1912
0
    {
1913
0
        delete poNewNode;
1914
0
        return -1;
1915
0
    }
1916
1917
    // Move all entries to the new child
1918
0
    m_poDataBlock->GotoByteInBlock(12 + 0);
1919
0
    if (poNewNode->SetNodeBufferDirectly(
1920
0
            m_numEntriesInNode, m_poDataBlock->GetCurDataPtr(),
1921
0
            m_nCurIndexEntry, m_poCurChildNode) != 0)
1922
0
    {
1923
0
        delete poNewNode;
1924
0
        return -1;
1925
0
    }
1926
1927
#ifdef DEBUG
1928
    // Just in case, reset space previously used by moved entries
1929
    memset(m_poDataBlock->GetCurDataPtr(), 0,
1930
           static_cast<size_t>(m_numEntriesInNode) * (m_nKeyLength + 4));
1931
#endif
1932
1933
    /*-----------------------------------------------------------------
1934
     * Rewrite current node. (the new root node)
1935
     *----------------------------------------------------------------*/
1936
0
    m_numEntriesInNode = 0;
1937
0
    m_nSubTreeDepth++;
1938
1939
0
    m_poDataBlock->GotoByteInBlock(0);
1940
0
    m_poDataBlock->WriteInt32(m_numEntriesInNode);
1941
1942
0
    InsertEntry(poNewNode->GetNodeKey(), poNewNode->GetNodeBlockPtr());
1943
1944
    /*-----------------------------------------------------------------
1945
     * Keep a reference to the new child
1946
     *----------------------------------------------------------------*/
1947
0
    m_poCurChildNode = poNewNode;
1948
0
    m_nCurIndexEntry = 0;
1949
1950
    /*-----------------------------------------------------------------
1951
     * And finally force the child to split itself
1952
     *----------------------------------------------------------------*/
1953
0
    return m_poCurChildNode->SplitNode();
1954
0
}
1955
1956
/**********************************************************************
1957
 *                   TABINDNode::SetNodeBufferDirectly()
1958
 *
1959
 * (private method)
1960
 *
1961
 * Set the key/value part of the nodes buffer and the pointers to the
1962
 * current child directly.  This is used when copying info to a new node
1963
 * in SplitNode() and SplitRootNode()
1964
 *
1965
 * Returns 0 on success, -1 on error
1966
 **********************************************************************/
1967
int TABINDNode::SetNodeBufferDirectly(int numEntries, GByte *pBuf,
1968
                                      int nCurIndexEntry /*=0*/,
1969
                                      TABINDNode *poCurChild /*=NULL*/)
1970
0
{
1971
0
    m_poDataBlock->GotoByteInBlock(0);
1972
0
    m_poDataBlock->WriteInt32(numEntries);
1973
1974
0
    m_numEntriesInNode = numEntries;
1975
1976
0
    m_poDataBlock->GotoByteInBlock(12);
1977
0
    if (m_poDataBlock->WriteBytes(numEntries * (m_nKeyLength + 4), pBuf) != 0)
1978
0
    {
1979
0
        return -1;  // An error msg should have been reported already
1980
0
    }
1981
1982
0
    m_nCurIndexEntry = nCurIndexEntry;
1983
0
    m_poCurChildNode = poCurChild;
1984
0
    if (m_poCurChildNode)
1985
0
        m_poCurChildNode->m_poParentNodeRef = this;
1986
1987
0
    return 0;
1988
0
}
1989
1990
/**********************************************************************
1991
 *                   TABINDNode::GetNodeKey()
1992
 *
1993
 * Returns a reference to the key for the first entry in the node, which
1994
 * is also the key for this node at the level above it in the tree.
1995
 *
1996
 * Returns NULL if node is empty.
1997
 **********************************************************************/
1998
GByte *TABINDNode::GetNodeKey()
1999
0
{
2000
0
    if (m_poDataBlock == nullptr || m_numEntriesInNode == 0)
2001
0
        return nullptr;
2002
2003
0
    m_poDataBlock->GotoByteInBlock(12);
2004
2005
0
    return m_poDataBlock->GetCurDataPtr();
2006
0
}
2007
2008
/**********************************************************************
2009
 *                   TABINDNode::SetPrevNodePtr()
2010
 *
2011
 * Update the m_nPrevNodePtr member.
2012
 *
2013
 * Returns 0 on success, -1 on error.
2014
 **********************************************************************/
2015
int TABINDNode::SetPrevNodePtr(GInt32 nPrevNodePtr)
2016
0
{
2017
0
    if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
2018
0
        m_poDataBlock == nullptr)
2019
0
        return -1;
2020
2021
0
    if (m_nPrevNodePtr == nPrevNodePtr)
2022
0
        return 0;  // Nothing to do.
2023
2024
0
    m_poDataBlock->GotoByteInBlock(4);
2025
0
    return m_poDataBlock->WriteInt32(nPrevNodePtr);
2026
0
}
2027
2028
/**********************************************************************
2029
 *                   TABINDNode::SetNextNodePtr()
2030
 *
2031
 * Update the m_nNextNodePtr member.
2032
 *
2033
 * Returns 0 on success, -1 on error.
2034
 **********************************************************************/
2035
int TABINDNode::SetNextNodePtr(GInt32 nNextNodePtr)
2036
0
{
2037
0
    if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
2038
0
        m_poDataBlock == nullptr)
2039
0
        return -1;
2040
2041
0
    if (m_nNextNodePtr == nNextNodePtr)
2042
0
        return 0;  // Nothing to do.
2043
2044
0
    m_poDataBlock->GotoByteInBlock(8);
2045
0
    return m_poDataBlock->WriteInt32(nNextNodePtr);
2046
0
}
2047
2048
/**********************************************************************
2049
 *                   TABINDNode::Dump()
2050
 *
2051
 * Dump block contents... available only in DEBUG mode.
2052
 **********************************************************************/
2053
#ifdef DEBUG
2054
2055
void TABINDNode::Dump(FILE *fpOut /*=NULL*/)
2056
{
2057
    if (fpOut == nullptr)
2058
        fpOut = stdout;
2059
2060
    fprintf(fpOut, "----- TABINDNode::Dump() -----\n");
2061
2062
    if (m_fp == nullptr)
2063
    {
2064
        fprintf(fpOut, "Node is not initialized.\n");
2065
    }
2066
    else
2067
    {
2068
        fprintf(fpOut, "   m_numEntriesInNode   = %d\n", m_numEntriesInNode);
2069
        fprintf(fpOut, "   m_nCurDataBlockPtr   = %d\n", m_nCurDataBlockPtr);
2070
        fprintf(fpOut, "   m_nPrevNodePtr       = %d\n", m_nPrevNodePtr);
2071
        fprintf(fpOut, "   m_nNextNodePtr       = %d\n", m_nNextNodePtr);
2072
        fprintf(fpOut, "   m_nSubTreeDepth      = %d\n", m_nSubTreeDepth);
2073
        fprintf(fpOut, "   m_nKeyLength         = %d\n", m_nKeyLength);
2074
        fprintf(fpOut, "   m_eFieldtype         = %s\n",
2075
                TABFIELDTYPE_2_STRING(m_eFieldType));
2076
        if (m_nSubTreeDepth > 0)
2077
        {
2078
            GByte aKeyValBuf[255];
2079
            GInt32 nRecordPtr, nValue;
2080
            TABINDNode oChildNode;
2081
2082
            if (m_nKeyLength > 254)
2083
            {
2084
                CPLError(CE_Failure, CPLE_NotSupported,
2085
                         "Dump() cannot handle keys longer than 254 chars.");
2086
                return;
2087
            }
2088
2089
            fprintf(fpOut, "\n");
2090
            for (int i = 0; i < m_numEntriesInNode; i++)
2091
            {
2092
                if (m_nSubTreeDepth > 1)
2093
                {
2094
                    fprintf(fpOut, "   >>>> Child %d of %d <<<<<\n", i,
2095
                            m_numEntriesInNode);
2096
                }
2097
                else
2098
                {
2099
                    fprintf(fpOut, "   >>>> Record (leaf) %d of %d <<<<<\n", i,
2100
                            m_numEntriesInNode);
2101
                }
2102
2103
                if (m_eFieldType == TABFChar)
2104
                {
2105
                    nRecordPtr = ReadIndexEntry(i, aKeyValBuf);
2106
                    fprintf(fpOut, "   nRecordPtr = %d\n", nRecordPtr);
2107
                    fprintf(fpOut, "   Char Val= \"%s\"\n",
2108
                            reinterpret_cast<char *>(aKeyValBuf));
2109
                }
2110
                else if (m_nKeyLength != 4)
2111
                {
2112
                    nRecordPtr = ReadIndexEntry(i, aKeyValBuf);
2113
                    GInt32 nInt32 = 0;
2114
                    memcpy(&nInt32, aKeyValBuf, 4);
2115
                    GInt16 nInt16 = 0;
2116
                    memcpy(&nInt16, aKeyValBuf + 2, 2);
2117
                    GUInt32 nUInt32 = 0;
2118
                    memcpy(&nUInt32, aKeyValBuf, 4);
2119
                    fprintf(fpOut, "   nRecordPtr = %d\n", nRecordPtr);
2120
                    fprintf(fpOut, "   Int Value = %d\n", nInt32);
2121
                    fprintf(fpOut, "   Int16 Val= %d\n", nInt16);
2122
                    fprintf(fpOut, "   Hex Val= 0x%8.8x\n", nUInt32);
2123
                }
2124
                else
2125
                {
2126
                    nRecordPtr =
2127
                        ReadIndexEntry(i, reinterpret_cast<GByte *>(&nValue));
2128
                    fprintf(fpOut, "   nRecordPtr = %d\n", nRecordPtr);
2129
                    fprintf(fpOut, "   Int Value = %d\n", nValue);
2130
                    fprintf(fpOut, "   Hex Value = 0x%8.8x\n", nValue);
2131
                }
2132
2133
                if (m_nSubTreeDepth > 1)
2134
                {
2135
                    CPL_IGNORE_RET_VAL(
2136
                        oChildNode.InitNode(m_fp, nRecordPtr, m_nKeyLength,
2137
                                            m_nSubTreeDepth - 1, FALSE));
2138
                    oChildNode.SetFieldType(m_eFieldType);
2139
                    oChildNode.Dump(fpOut);
2140
                }
2141
            }
2142
        }
2143
    }
2144
2145
    fflush(fpOut);
2146
}
2147
2148
#endif  // DEBUG