Coverage Report

Created: 2026-08-11 08:26

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