Coverage Report

Created: 2025-06-09 07:02

/src/gdal/frmts/hfa/hfaentry.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  Erdas Imagine (.img) Translator
4
 * Purpose:  Implementation of the HFAEntry class for reading and relating
5
 *           one node in the HFA object tree structure.
6
 * Author:   Frank Warmerdam, warmerdam@pobox.com
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 1999, Intergraph Corporation
10
 * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ******************************************************************************
14
 *
15
 * hfaentry.cpp
16
 *
17
 * Implementation of the HFAEntry class.
18
 *
19
 */
20
21
#include "cpl_port.h"
22
#include "hfa_p.h"
23
24
#include <cerrno>
25
#include <climits>
26
#include <cstddef>
27
#include <cstdio>
28
#include <cstring>
29
#if HAVE_FCNTL_H
30
#include <fcntl.h>
31
#endif
32
#include <vector>
33
34
#include "cpl_conv.h"
35
#include "cpl_error.h"
36
#include "cpl_string.h"
37
#include "cpl_vsi.h"
38
39
/************************************************************************/
40
/*                              HFAEntry()                              */
41
/************************************************************************/
42
43
HFAEntry::HFAEntry()
44
156k
    : bDirty(false), nFilePos(0), psHFA(nullptr), poParent(nullptr),
45
156k
      poPrev(nullptr), nNextPos(0), poNext(nullptr), nChildPos(0),
46
156k
      poChild(nullptr), poType(nullptr), nDataPos(0), nDataSize(0),
47
156k
      pabyData(nullptr), bIsMIFObject(false)
48
156k
{
49
156k
    szName[0] = '\0';
50
156k
    szType[0] = '\0';
51
156k
}
52
53
/************************************************************************/
54
/*                              HFAEntry()                              */
55
/*                                                                      */
56
/*      Construct an HFAEntry from the source file.                     */
57
/************************************************************************/
58
59
HFAEntry *HFAEntry::New(HFAInfo_t *psHFAIn, GUInt32 nPos, HFAEntry *poParentIn,
60
                        HFAEntry *poPrevIn)
61
62
156k
{
63
156k
    HFAEntry *poEntry = new HFAEntry;
64
156k
    poEntry->psHFA = psHFAIn;
65
66
156k
    poEntry->nFilePos = nPos;
67
156k
    poEntry->poParent = poParentIn;
68
156k
    poEntry->poPrev = poPrevIn;
69
70
    // Read the entry information from the file.
71
156k
    GInt32 anEntryNums[6] = {};
72
73
156k
    if (VSIFSeekL(poEntry->psHFA->fp, poEntry->nFilePos, SEEK_SET) == -1 ||
74
156k
        VSIFReadL(anEntryNums, sizeof(GInt32) * 6, 1, poEntry->psHFA->fp) < 1)
75
445
    {
76
445
        CPLError(CE_Failure, CPLE_FileIO,
77
445
                 "VSIFReadL(%p,6*4) @ %u failed in HFAEntry().\n%s",
78
445
                 poEntry->psHFA->fp, poEntry->nFilePos, VSIStrerror(errno));
79
445
        delete poEntry;
80
445
        return nullptr;
81
445
    }
82
83
1.09M
    for (int i = 0; i < 6; i++)
84
938k
        HFAStandard(4, anEntryNums + i);
85
86
156k
    poEntry->nNextPos = anEntryNums[0];
87
156k
    poEntry->nChildPos = anEntryNums[3];
88
156k
    poEntry->nDataPos = anEntryNums[4];
89
156k
    poEntry->nDataSize = anEntryNums[5];
90
91
    // Read the name, and type.
92
156k
    if (VSIFReadL(poEntry->szName, 64, 1, poEntry->psHFA->fp) < 1 ||
93
156k
        VSIFReadL(poEntry->szType, 32, 1, poEntry->psHFA->fp) < 1)
94
23
    {
95
23
        poEntry->szName[sizeof(poEntry->szName) - 1] = '\0';
96
23
        poEntry->szType[sizeof(poEntry->szType) - 1] = '\0';
97
23
        CPLError(CE_Failure, CPLE_FileIO, "VSIFReadL() failed in HFAEntry().");
98
23
        delete poEntry;
99
23
        return nullptr;
100
23
    }
101
156k
    poEntry->szName[sizeof(poEntry->szName) - 1] = '\0';
102
156k
    poEntry->szType[sizeof(poEntry->szType) - 1] = '\0';
103
156k
    return poEntry;
104
156k
}
105
106
/************************************************************************/
107
/*                              HFAEntry()                              */
108
/*                                                                      */
109
/*      Construct an HFAEntry in memory, with the intention that it     */
110
/*      would be written to disk later.                                 */
111
/************************************************************************/
112
113
HFAEntry::HFAEntry(HFAInfo_t *psHFAIn, const char *pszNodeName,
114
                   const char *pszTypeName, HFAEntry *poParentIn)
115
606k
    : nFilePos(0), psHFA(psHFAIn), poParent(poParentIn), poPrev(nullptr),
116
606k
      nNextPos(0), poNext(nullptr), nChildPos(0), poChild(nullptr),
117
606k
      poType(nullptr), nDataPos(0), nDataSize(0), pabyData(nullptr),
118
606k
      bIsMIFObject(false)
119
606k
{
120
    // Initialize Entry.
121
606k
    SetName(pszNodeName);
122
606k
    memset(szType, 0, sizeof(szType));
123
606k
    snprintf(szType, sizeof(szType), "%s", pszTypeName);
124
125
    // Update the previous or parent node to refer to this one.
126
606k
    if (poParent == nullptr)
127
1.19k
    {
128
        // Do nothing.
129
1.19k
    }
130
605k
    else if (poParent->poChild == nullptr)
131
132k
    {
132
132k
        poParent->poChild = this;
133
132k
        poParent->MarkDirty();
134
132k
    }
135
472k
    else
136
472k
    {
137
472k
        poPrev = poParent->poChild;
138
20.8M
        while (poPrev->poNext != nullptr)
139
20.3M
            poPrev = poPrev->poNext;
140
141
472k
        poPrev->poNext = this;
142
472k
        poPrev->MarkDirty();
143
472k
    }
144
145
606k
    MarkDirty();
146
606k
}
147
148
/************************************************************************/
149
/*                              New()                                   */
150
/*                                                                      */
151
/*      Construct an HFAEntry in memory, with the intention that it     */
152
/*      would be written to disk later.                                 */
153
/************************************************************************/
154
155
HFAEntry *HFAEntry::New(HFAInfo_t *psHFAIn, const char *pszNodeName,
156
                        const char *pszTypeName, HFAEntry *poParentIn)
157
605k
{
158
605k
    CPLAssert(poParentIn != nullptr);
159
605k
    return new HFAEntry(psHFAIn, pszNodeName, pszTypeName, poParentIn);
160
605k
}
161
162
/************************************************************************/
163
/*                      BuildEntryFromMIFObject()                       */
164
/*                                                                      */
165
/*      Create a pseudo-HFAEntry wrapping a MIFObject.                  */
166
/************************************************************************/
167
168
HFAEntry *HFAEntry::BuildEntryFromMIFObject(HFAEntry *poContainer,
169
                                            const char *pszMIFObjectPath)
170
0
{
171
0
    CPLString osFieldName;
172
173
0
    osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFDictionary");
174
0
    const char *pszField = poContainer->GetStringField(osFieldName.c_str());
175
0
    if (pszField == nullptr)
176
0
    {
177
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry",
178
0
                 osFieldName.c_str());
179
0
        return nullptr;
180
0
    }
181
0
    CPLString osDictionary = pszField;
182
183
0
    osFieldName.Printf("%s.%s", pszMIFObjectPath, "type.string");
184
0
    pszField = poContainer->GetStringField(osFieldName.c_str());
185
0
    if (pszField == nullptr)
186
0
    {
187
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry",
188
0
                 osFieldName.c_str());
189
0
        return nullptr;
190
0
    }
191
0
    CPLString osType = pszField;
192
193
0
    osFieldName.Printf("%s.%s", pszMIFObjectPath, "MIFObject");
194
0
    int nRemainingDataSize = 0;
195
0
    pszField = poContainer->GetStringField(osFieldName.c_str(), nullptr,
196
0
                                           &nRemainingDataSize);
197
0
    if (pszField == nullptr)
198
0
    {
199
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s entry",
200
0
                 osFieldName.c_str());
201
0
        return nullptr;
202
0
    }
203
204
0
    GInt32 nMIFObjectSize = 0;
205
    // We rudely look before the field data to get at the pointer/size info.
206
0
    memcpy(&nMIFObjectSize, pszField - 8, 4);
207
0
    HFAStandard(4, &nMIFObjectSize);
208
0
    if (nMIFObjectSize <= 0)
209
0
    {
210
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid MIF object size (%d)",
211
0
                 nMIFObjectSize);
212
0
        return nullptr;
213
0
    }
214
215
    // Check that we won't copy more bytes than available in the buffer.
216
0
    if (nMIFObjectSize > nRemainingDataSize)
217
0
    {
218
0
        CPLError(CE_Failure, CPLE_AppDefined,
219
0
                 "Invalid MIF object size (%d > %d)", nMIFObjectSize,
220
0
                 nRemainingDataSize);
221
0
        return nullptr;
222
0
    }
223
224
0
    GByte *l_pabyData = static_cast<GByte *>(VSIMalloc(nMIFObjectSize));
225
0
    if (l_pabyData == nullptr)
226
0
        return nullptr;
227
228
0
    memcpy(l_pabyData, pszField, nMIFObjectSize);
229
230
0
    return new HFAEntry(osDictionary, osType, nMIFObjectSize, l_pabyData);
231
0
}
232
233
/************************************************************************/
234
/*                              HFAEntry()                              */
235
/*                                                                      */
236
/*      Create a pseudo-HFAEntry wrapping a MIFObject.                  */
237
/************************************************************************/
238
239
HFAEntry::HFAEntry(const char *pszDictionary, const char *pszTypeName,
240
                   int nDataSizeIn, GByte *pabyDataIn)
241
0
    : bDirty(false), nFilePos(0), poParent(nullptr), poPrev(nullptr),
242
0
      nNextPos(0), poNext(nullptr), nChildPos(0), poChild(nullptr), nDataPos(0),
243
0
      nDataSize(0), bIsMIFObject(true)
244
0
{
245
    // Initialize Entry
246
0
    memset(szName, 0, sizeof(szName));
247
248
    // Create a dummy HFAInfo_t.
249
0
    psHFA = static_cast<HFAInfo_t *>(CPLCalloc(sizeof(HFAInfo_t), 1));
250
251
0
    psHFA->eAccess = HFA_ReadOnly;
252
0
    psHFA->bTreeDirty = false;
253
0
    psHFA->poRoot = this;
254
255
0
    psHFA->poDictionary = new HFADictionary(pszDictionary);
256
257
    // Work out the type for this MIFObject.
258
0
    memset(szType, 0, sizeof(szType));
259
0
    snprintf(szType, sizeof(szType), "%s", pszTypeName);
260
261
0
    poType = psHFA->poDictionary->FindType(szType);
262
263
0
    nDataSize = nDataSizeIn;
264
0
    pabyData = pabyDataIn;
265
0
}
266
267
/************************************************************************/
268
/*                             ~HFAEntry()                              */
269
/*                                                                      */
270
/*      Ensure that children are cleaned up when this node is           */
271
/*      cleaned up.                                                     */
272
/************************************************************************/
273
274
HFAEntry::~HFAEntry()
275
276
763k
{
277
763k
    CPLFree(pabyData);
278
279
763k
    if (poNext != nullptr)
280
574k
        delete poNext;
281
282
763k
    if (poChild != nullptr)
283
185k
        delete poChild;
284
285
763k
    if (bIsMIFObject)
286
0
    {
287
0
        delete psHFA->poDictionary;
288
0
        CPLFree(psHFA);
289
0
    }
290
763k
}
291
292
/************************************************************************/
293
/*                          RemoveAndDestroy()                          */
294
/*                                                                      */
295
/*      Removes this entry, and its children from the current           */
296
/*      tree.  The parent and/or siblings are appropriately updated     */
297
/*      so that they will be flushed back to disk without the           */
298
/*      reference to this node.                                         */
299
/************************************************************************/
300
301
CPLErr HFAEntry::RemoveAndDestroy()
302
303
0
{
304
0
    if (poPrev != nullptr)
305
0
    {
306
0
        poPrev->poNext = poNext;
307
0
        if (poNext != nullptr)
308
0
            poPrev->nNextPos = poNext->nFilePos;
309
0
        else
310
0
            poPrev->nNextPos = 0;
311
0
        poPrev->MarkDirty();
312
0
    }
313
0
    if (poParent != nullptr && poParent->poChild == this)
314
0
    {
315
0
        poParent->poChild = poNext;
316
0
        if (poNext)
317
0
            poParent->nChildPos = poNext->nFilePos;
318
0
        else
319
0
            poParent->nChildPos = 0;
320
0
        poParent->MarkDirty();
321
0
    }
322
323
0
    if (poNext != nullptr)
324
0
    {
325
0
        poNext->poPrev = poPrev;
326
0
    }
327
328
0
    poNext = nullptr;
329
0
    poPrev = nullptr;
330
0
    poParent = nullptr;
331
332
0
    delete this;
333
334
0
    return CE_None;
335
0
}
336
337
/************************************************************************/
338
/*                              SetName()                               */
339
/*                                                                      */
340
/*    Changes the name assigned to this node                            */
341
/************************************************************************/
342
343
void HFAEntry::SetName(const char *pszNodeName)
344
608k
{
345
608k
    memset(szName, 0, sizeof(szName));
346
608k
    snprintf(szName, sizeof(szName), "%s", pszNodeName);
347
348
608k
    MarkDirty();
349
608k
}
350
351
/************************************************************************/
352
/*                              GetChild()                              */
353
/************************************************************************/
354
355
HFAEntry *HFAEntry::GetChild()
356
357
2.11M
{
358
    // Do we need to create the child node?
359
2.11M
    if (poChild == nullptr && nChildPos != 0)
360
52.8k
    {
361
52.8k
        poChild = HFAEntry::New(psHFA, nChildPos, this, nullptr);
362
52.8k
        if (poChild == nullptr)
363
415
            nChildPos = 0;
364
52.8k
    }
365
366
2.11M
    return poChild;
367
2.11M
}
368
369
/************************************************************************/
370
/*                              GetNext()                               */
371
/************************************************************************/
372
373
HFAEntry *HFAEntry::GetNext()
374
375
4.54M
{
376
    // Do we need to create the next node?
377
4.54M
    if (poNext == nullptr && nNextPos != 0)
378
102k
    {
379
        // Check if we have a loop on the next node in this sibling chain.
380
102k
        HFAEntry *poPast;
381
382
19.5M
        for (poPast = this; poPast != nullptr && poPast->nFilePos != nNextPos;
383
19.4M
             poPast = poPast->poPrev)
384
19.4M
        {
385
19.4M
        }
386
387
102k
        if (poPast != nullptr)
388
0
        {
389
0
            CPLError(CE_Warning, CPLE_AppDefined,
390
0
                     "Corrupt (looping) entry in %s, "
391
0
                     "ignoring some entries after %s.",
392
0
                     psHFA->pszFilename, szName);
393
0
            nNextPos = 0;
394
0
            return nullptr;
395
0
        }
396
397
102k
        poNext = HFAEntry::New(psHFA, nNextPos, poParent, this);
398
102k
        if (poNext == nullptr)
399
3
            nNextPos = 0;
400
102k
    }
401
402
4.54M
    return poNext;
403
4.54M
}
404
405
/************************************************************************/
406
/*                              LoadData()                              */
407
/*                                                                      */
408
/*      Load the data for this entry, and build up the field            */
409
/*      information for it.                                             */
410
/************************************************************************/
411
412
void HFAEntry::LoadData()
413
414
7.44M
{
415
7.44M
    if (pabyData != nullptr || nDataSize == 0)
416
7.34M
        return;
417
102k
    if (nDataSize > INT_MAX - 1)
418
0
    {
419
0
        CPLError(CE_Failure, CPLE_AppDefined,
420
0
                 "Invalid value for nDataSize = %u", nDataSize);
421
0
        return;
422
0
    }
423
424
    // Allocate buffer, and read data.
425
102k
    pabyData = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nDataSize + 1));
426
102k
    if (pabyData == nullptr)
427
0
    {
428
0
        return;
429
0
    }
430
431
102k
    if (VSIFSeekL(psHFA->fp, nDataPos, SEEK_SET) < 0)
432
0
    {
433
0
        CPLError(CE_Failure, CPLE_FileIO,
434
0
                 "VSIFSeekL() failed in HFAEntry::LoadData().");
435
0
        return;
436
0
    }
437
438
102k
    if (VSIFReadL(pabyData, nDataSize, 1, psHFA->fp) < 1)
439
0
    {
440
0
        CPLError(CE_Failure, CPLE_FileIO,
441
0
                 "VSIFReadL() failed in HFAEntry::LoadData().");
442
0
        return;
443
0
    }
444
445
    // Make sure the buffer is always null terminated to avoid
446
    // issues when extracting strings from a corrupted file.
447
102k
    pabyData[nDataSize] = '\0';
448
449
    // Get the type corresponding to this entry.
450
102k
    poType = psHFA->poDictionary->FindType(szType);
451
102k
    if (poType == nullptr)
452
0
        return;
453
102k
}
454
455
/************************************************************************/
456
/*                           GetTypeObject()                            */
457
/************************************************************************/
458
459
HFAType *HFAEntry::GetTypeObject()
460
461
9.09k
{
462
9.09k
    if (poType == nullptr)
463
9.09k
        poType = psHFA->poDictionary->FindType(szType);
464
465
9.09k
    return poType;
466
9.09k
}
467
468
/************************************************************************/
469
/*                              MakeData()                              */
470
/*                                                                      */
471
/*      Create a data block on the this HFAEntry in memory.  By         */
472
/*      default it will create the data the correct size for fixed      */
473
/*      sized types, or do nothing for variable length types.           */
474
/*      However, the caller can supply a desired size for variable      */
475
/*      sized fields.                                                   */
476
/************************************************************************/
477
478
GByte *HFAEntry::MakeData(int nSize)
479
480
3.23M
{
481
3.23M
    if (poType == nullptr)
482
596k
    {
483
596k
        poType = psHFA->poDictionary->FindType(szType);
484
596k
        if (poType == nullptr)
485
3
            return nullptr;
486
596k
    }
487
488
3.23M
    if (nSize == 0 && poType->nBytes > 0)
489
1.34M
        nSize = poType->nBytes;
490
491
    // nDataSize is a GUInt32.
492
3.23M
    if (static_cast<int>(nDataSize) < nSize && nSize > 0)
493
605k
    {
494
605k
        pabyData = static_cast<GByte *>(CPLRealloc(pabyData, nSize));
495
605k
        memset(pabyData + nDataSize, 0, nSize - nDataSize);
496
605k
        nDataSize = nSize;
497
498
605k
        MarkDirty();
499
500
        // If the data already had a file position, we now need to
501
        // clear that, forcing it to be rewritten at the end of the
502
        // file.  Referencing nodes will need to be marked dirty so
503
        // they are rewritten.
504
605k
        if (nFilePos != 0)
505
0
        {
506
0
            nFilePos = 0;
507
0
            nDataPos = 0;
508
0
            if (poPrev != nullptr)
509
0
                poPrev->MarkDirty();
510
0
            if (poNext != nullptr)
511
0
                poNext->MarkDirty();
512
0
            if (poChild != nullptr)
513
0
                poChild->MarkDirty();
514
0
            if (poParent != nullptr)
515
0
                poParent->MarkDirty();
516
0
        }
517
605k
    }
518
2.62M
    else
519
2.62M
    {
520
2.62M
        LoadData();  // Make sure the data is loaded before we return pointer.
521
2.62M
    }
522
523
3.23M
    return pabyData;
524
3.23M
}
525
526
/************************************************************************/
527
/*                          DumpFieldValues()                           */
528
/************************************************************************/
529
530
void HFAEntry::DumpFieldValues(FILE *fp, const char *pszPrefix)
531
532
0
{
533
0
    if (pszPrefix == nullptr)
534
0
        pszPrefix = "";
535
536
0
    LoadData();
537
538
0
    if (pabyData == nullptr || poType == nullptr)
539
0
        return;
540
541
0
    poType->DumpInstValue(fp, pabyData, nDataPos, nDataSize, pszPrefix);
542
0
}
543
544
/************************************************************************/
545
/*                            FindChildren()                            */
546
/*                                                                      */
547
/*      Find all the children of the current node that match the        */
548
/*      name and type provided.  Either may be NULL if it is not a      */
549
/*      factor.  The pszName should be just the node name, not a        */
550
/*      path.                                                           */
551
/************************************************************************/
552
553
std::vector<HFAEntry *> HFAEntry::FindChildren(const char *pszName,
554
                                               const char *pszType,
555
                                               int nRecLevel,
556
                                               int *pbErrorDetected)
557
558
0
{
559
0
    std::vector<HFAEntry *> apoChildren;
560
561
0
    if (*pbErrorDetected)
562
0
        return apoChildren;
563
0
    if (nRecLevel == 50)
564
0
    {
565
0
        CPLError(CE_Failure, CPLE_AppDefined,
566
0
                 "Bad entry structure: recursion detected !");
567
0
        *pbErrorDetected = TRUE;
568
0
        return apoChildren;
569
0
    }
570
571
0
    for (HFAEntry *poEntry = GetChild(); poEntry != nullptr;
572
0
         poEntry = poEntry->GetNext())
573
0
    {
574
0
        std::vector<HFAEntry *> apoEntryChildren;
575
576
0
        if ((pszName == nullptr || EQUAL(poEntry->GetName(), pszName)) &&
577
0
            (pszType == nullptr || EQUAL(poEntry->GetType(), pszType)))
578
0
            apoChildren.push_back(poEntry);
579
580
0
        apoEntryChildren = poEntry->FindChildren(
581
0
            pszName, pszType, nRecLevel + 1, pbErrorDetected);
582
0
        if (*pbErrorDetected)
583
0
            return apoChildren;
584
585
0
        for (size_t i = 0; i < apoEntryChildren.size(); i++)
586
0
            apoChildren.push_back(apoEntryChildren[i]);
587
0
    }
588
589
0
    return apoChildren;
590
0
}
591
592
std::vector<HFAEntry *> HFAEntry::FindChildren(const char *pszName,
593
                                               const char *pszType)
594
595
0
{
596
0
    int bErrorDetected = FALSE;
597
0
    return FindChildren(pszName, pszType, 0, &bErrorDetected);
598
0
}
599
600
/************************************************************************/
601
/*                           GetNamedChild()                            */
602
/************************************************************************/
603
604
HFAEntry *HFAEntry::GetNamedChild(const char *pszName)
605
606
2.05M
{
607
    // Establish how much of this name path is for the next child.
608
    // Up to the '.' or end of the string.
609
2.05M
    int nNameLen = 0;
610
30.8M
    for (; pszName[nNameLen] != '.' && pszName[nNameLen] != '\0' &&
611
30.8M
           pszName[nNameLen] != ':';
612
28.7M
         nNameLen++)
613
28.7M
    {
614
28.7M
    }
615
616
    // Scan children looking for this name.
617
6.32M
    for (HFAEntry *poEntry = GetChild(); poEntry != nullptr;
618
4.26M
         poEntry = poEntry->GetNext())
619
4.89M
    {
620
4.89M
        if (EQUALN(poEntry->GetName(), pszName, nNameLen) &&
621
4.89M
            static_cast<int>(strlen(poEntry->GetName())) == nNameLen)
622
623k
        {
623
623k
            if (pszName[nNameLen] == '.')
624
0
            {
625
0
                HFAEntry *poResult;
626
627
0
                poResult = poEntry->GetNamedChild(pszName + nNameLen + 1);
628
0
                if (poResult != nullptr)
629
0
                    return poResult;
630
0
            }
631
623k
            else
632
623k
                return poEntry;
633
623k
        }
634
4.89M
    }
635
636
1.42M
    return nullptr;
637
2.05M
}
638
639
/************************************************************************/
640
/*                           GetFieldValue()                            */
641
/************************************************************************/
642
643
bool HFAEntry::GetFieldValue(const char *pszFieldPath, char chReqType,
644
                             void *pReqReturn, int *pnRemainingDataSize)
645
646
1.87M
{
647
    // Is there a node path in this string?
648
1.87M
    if (strchr(pszFieldPath, ':') != nullptr)
649
0
    {
650
0
        HFAEntry *poEntry = GetNamedChild(pszFieldPath);
651
0
        if (poEntry == nullptr)
652
0
            return false;
653
654
0
        pszFieldPath = strchr(pszFieldPath, ':') + 1;
655
0
    }
656
657
    // Do we have the data and type for this node?
658
1.87M
    LoadData();
659
660
1.87M
    if (pabyData == nullptr)
661
0
        return false;
662
663
1.87M
    if (poType == nullptr)
664
0
        return false;
665
666
    // Extract the instance information.
667
1.87M
    return poType->ExtractInstValue(pszFieldPath, pabyData, nDataPos, nDataSize,
668
1.87M
                                    chReqType, pReqReturn, pnRemainingDataSize);
669
1.87M
}
670
671
/************************************************************************/
672
/*                           GetFieldCount()                            */
673
/************************************************************************/
674
675
int HFAEntry::GetFieldCount(const char *pszFieldPath, CPLErr * /* peErr */)
676
0
{
677
    // Is there a node path in this string?
678
0
    if (strchr(pszFieldPath, ':') != nullptr)
679
0
    {
680
0
        HFAEntry *poEntry = GetNamedChild(pszFieldPath);
681
0
        if (poEntry == nullptr)
682
0
            return -1;
683
684
0
        pszFieldPath = strchr(pszFieldPath, ':') + 1;
685
0
    }
686
687
    // Do we have the data and type for this node?
688
0
    LoadData();
689
690
0
    if (pabyData == nullptr)
691
0
        return -1;
692
693
0
    if (poType == nullptr)
694
0
        return -1;
695
696
    // Extract the instance information.
697
698
0
    return poType->GetInstCount(pszFieldPath, pabyData, nDataPos, nDataSize);
699
0
}
700
701
/************************************************************************/
702
/*                            GetIntField()                             */
703
/************************************************************************/
704
705
GInt32 HFAEntry::GetIntField(const char *pszFieldPath, CPLErr *peErr)
706
707
1.72M
{
708
1.72M
    GInt32 nIntValue = 0;
709
710
1.72M
    if (!GetFieldValue(pszFieldPath, 'i', &nIntValue, nullptr))
711
0
    {
712
0
        if (peErr != nullptr)
713
0
            *peErr = CE_Failure;
714
715
0
        return 0;
716
0
    }
717
718
1.72M
    if (peErr != nullptr)
719
611k
        *peErr = CE_None;
720
721
1.72M
    return nIntValue;
722
1.72M
}
723
724
/************************************************************************/
725
/*                           GetBigIntField()                           */
726
/*                                                                      */
727
/*      This is just a helper method that reads two ULONG array         */
728
/*      entries as a GIntBig.  The passed name should be the name of    */
729
/*      the array with no array index.  Array indexes 0 and 1 will      */
730
/*      be concatenated.                                                */
731
/************************************************************************/
732
733
GIntBig HFAEntry::GetBigIntField(const char *pszFieldPath, CPLErr *peErr)
734
735
0
{
736
0
    char szFullFieldPath[1024];
737
738
0
    snprintf(szFullFieldPath, sizeof(szFullFieldPath), "%s[0]", pszFieldPath);
739
0
    const GUInt32 nLower = GetIntField(szFullFieldPath, peErr);
740
0
    if (peErr != nullptr && *peErr != CE_None)
741
0
        return 0;
742
743
0
    snprintf(szFullFieldPath, sizeof(szFullFieldPath), "%s[1]", pszFieldPath);
744
0
    const GUInt32 nUpper = GetIntField(szFullFieldPath, peErr);
745
0
    if (peErr != nullptr && *peErr != CE_None)
746
0
        return 0;
747
748
0
    return nLower + (static_cast<GIntBig>(nUpper) << 32);
749
0
}
750
751
/************************************************************************/
752
/*                           GetDoubleField()                           */
753
/************************************************************************/
754
755
double HFAEntry::GetDoubleField(const char *pszFieldPath, CPLErr *peErr)
756
757
61.7k
{
758
61.7k
    double dfDoubleValue = 0;
759
760
61.7k
    if (!GetFieldValue(pszFieldPath, 'd', &dfDoubleValue, nullptr))
761
0
    {
762
0
        if (peErr != nullptr)
763
0
            *peErr = CE_Failure;
764
765
0
        return 0.0;
766
0
    }
767
768
61.7k
    if (peErr != nullptr)
769
0
        *peErr = CE_None;
770
771
61.7k
    return dfDoubleValue;
772
61.7k
}
773
774
/************************************************************************/
775
/*                           GetStringField()                           */
776
/************************************************************************/
777
778
const char *HFAEntry::GetStringField(const char *pszFieldPath, CPLErr *peErr,
779
                                     int *pnRemainingDataSize)
780
781
87.1k
{
782
87.1k
    char *pszResult = nullptr;
783
784
87.1k
    if (!GetFieldValue(pszFieldPath, 's', &pszResult, pnRemainingDataSize))
785
0
    {
786
0
        if (peErr != nullptr)
787
0
            *peErr = CE_Failure;
788
789
0
        return nullptr;
790
0
    }
791
792
87.1k
    if (peErr != nullptr)
793
51.2k
        *peErr = CE_None;
794
795
87.1k
    return pszResult;
796
87.1k
}
797
798
/************************************************************************/
799
/*                           SetFieldValue()                            */
800
/************************************************************************/
801
802
CPLErr HFAEntry::SetFieldValue(const char *pszFieldPath, char chReqType,
803
                               void *pValue)
804
805
2.91M
{
806
    // Is there a node path in this string?
807
2.91M
    if (strchr(pszFieldPath, ':') != nullptr)
808
0
    {
809
0
        HFAEntry *poEntry = GetNamedChild(pszFieldPath);
810
0
        if (poEntry == nullptr)
811
0
            return CE_Failure;
812
813
0
        pszFieldPath = strchr(pszFieldPath, ':') + 1;
814
0
    }
815
816
    // Do we have the data and type for this node?  Try loading
817
    // from a file, or instantiating a new node.
818
2.91M
    LoadData();
819
2.91M
    if (MakeData() == nullptr || pabyData == nullptr || poType == nullptr)
820
3
    {
821
3
        return CE_Failure;
822
3
    }
823
824
    // Extract the instance information.
825
2.91M
    MarkDirty();
826
827
2.91M
    return poType->SetInstValue(pszFieldPath, pabyData, nDataPos, nDataSize,
828
2.91M
                                chReqType, pValue);
829
2.91M
}
830
831
/************************************************************************/
832
/*                           SetStringField()                           */
833
/************************************************************************/
834
835
CPLErr HFAEntry::SetStringField(const char *pszFieldPath, const char *pszValue)
836
837
693k
{
838
693k
    return SetFieldValue(pszFieldPath, 's', (void *)pszValue);
839
693k
}
840
841
/************************************************************************/
842
/*                            SetIntField()                             */
843
/************************************************************************/
844
845
CPLErr HFAEntry::SetIntField(const char *pszFieldPath, int nValue)
846
847
1.21M
{
848
1.21M
    return SetFieldValue(pszFieldPath, 'i', &nValue);
849
1.21M
}
850
851
/************************************************************************/
852
/*                           SetDoubleField()                           */
853
/************************************************************************/
854
855
CPLErr HFAEntry::SetDoubleField(const char *pszFieldPath, double dfValue)
856
857
1.00M
{
858
1.00M
    return SetFieldValue(pszFieldPath, 'd', &dfValue);
859
1.00M
}
860
861
/************************************************************************/
862
/*                            SetPosition()                             */
863
/*                                                                      */
864
/*      Set the disk position for this entry, and recursively apply     */
865
/*      to any children of this node.  The parent will take care of     */
866
/*      our siblings.                                                   */
867
/************************************************************************/
868
869
void HFAEntry::SetPosition()
870
871
942k
{
872
    // Establish the location of this entry, and its data.
873
942k
    if (nFilePos == 0)
874
606k
    {
875
606k
        nFilePos =
876
606k
            HFAAllocateSpace(psHFA, psHFA->nEntryHeaderLength + nDataSize);
877
878
606k
        if (nDataSize > 0)
879
605k
            nDataPos = nFilePos + psHFA->nEntryHeaderLength;
880
606k
    }
881
882
    // Force all children to set their position.
883
1.70M
    for (HFAEntry *poThisChild = poChild; poThisChild != nullptr;
884
942k
         poThisChild = poThisChild->poNext)
885
759k
    {
886
759k
        poThisChild->SetPosition();
887
759k
    }
888
942k
}
889
890
/************************************************************************/
891
/*                            FlushToDisk()                             */
892
/*                                                                      */
893
/*      Write this entry, and its data to disk if the entries           */
894
/*      information is dirty.  Also force children to do the same.      */
895
/************************************************************************/
896
897
CPLErr HFAEntry::FlushToDisk()
898
899
762k
{
900
    // If we are the root node, call SetPosition() on the whole
901
    // tree to ensure that all entries have an allocated position.
902
762k
    if (poParent == nullptr)
903
2.35k
        SetPosition();
904
905
    // Only write this node out if it is dirty.
906
762k
    if (bDirty)
907
707k
    {
908
        // Ensure we know where the relative entries are located.
909
707k
        if (poNext != nullptr)
910
523k
            nNextPos = poNext->nFilePos;
911
912
707k
        if (poChild != nullptr)
913
138k
            nChildPos = poChild->nFilePos;
914
915
        // Write the Ehfa_Entry fields.
916
917
        // VSIFFlushL(psHFA->fp);
918
707k
        if (VSIFSeekL(psHFA->fp, nFilePos, SEEK_SET) != 0)
919
0
        {
920
0
            CPLError(CE_Failure, CPLE_FileIO,
921
0
                     "Failed to seek to %d for writing, out of disk space?",
922
0
                     nFilePos);
923
0
            return CE_Failure;
924
0
        }
925
926
707k
        GUInt32 nLong = nNextPos;
927
707k
        HFAStandard(4, &nLong);
928
707k
        bool bOK = VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
929
930
707k
        if (poPrev != nullptr)
931
527k
            nLong = poPrev->nFilePos;
932
179k
        else
933
179k
            nLong = 0;
934
707k
        HFAStandard(4, &nLong);
935
707k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
936
937
707k
        if (poParent != nullptr)
938
706k
            nLong = poParent->nFilePos;
939
1.19k
        else
940
1.19k
            nLong = 0;
941
707k
        HFAStandard(4, &nLong);
942
707k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
943
944
707k
        nLong = nChildPos;
945
707k
        HFAStandard(4, &nLong);
946
707k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
947
948
707k
        nLong = nDataPos;
949
707k
        HFAStandard(4, &nLong);
950
707k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
951
952
707k
        nLong = nDataSize;
953
707k
        HFAStandard(4, &nLong);
954
707k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
955
956
707k
        bOK &= VSIFWriteL(szName, 1, 64, psHFA->fp) > 0;
957
707k
        bOK &= VSIFWriteL(szType, 1, 32, psHFA->fp) > 0;
958
959
707k
        nLong = 0;  // Should we keep the time, or set it more reasonably?
960
707k
        bOK &= VSIFWriteL(&nLong, 4, 1, psHFA->fp) > 0;
961
707k
        if (!bOK)
962
0
        {
963
0
            CPLError(CE_Failure, CPLE_FileIO,
964
0
                     "Failed to write HFAEntry %s(%s), out of disk space?",
965
0
                     szName, szType);
966
0
            return CE_Failure;
967
0
        }
968
969
        // Write out the data.
970
        // VSIFFlushL(psHFA->fp);
971
707k
        if (nDataSize > 0 && pabyData != nullptr)
972
656k
        {
973
656k
            if (VSIFSeekL(psHFA->fp, nDataPos, SEEK_SET) != 0 ||
974
656k
                VSIFWriteL(pabyData, nDataSize, 1, psHFA->fp) != 1)
975
0
            {
976
0
                CPLError(CE_Failure, CPLE_FileIO,
977
0
                         "Failed to write %d bytes HFAEntry %s(%s) data, "
978
0
                         "out of disk space?",
979
0
                         nDataSize, szName, szType);
980
0
                return CE_Failure;
981
0
            }
982
656k
        }
983
984
        // VSIFFlushL(psHFA->fp);
985
707k
    }
986
987
    // Process all the children of this node.
988
1.52M
    for (HFAEntry *poThisChild = poChild; poThisChild != nullptr;
989
762k
         poThisChild = poThisChild->poNext)
990
759k
    {
991
759k
        CPLErr eErr = poThisChild->FlushToDisk();
992
759k
        if (eErr != CE_None)
993
0
            return eErr;
994
759k
    }
995
996
762k
    bDirty = false;
997
998
762k
    return CE_None;
999
762k
}
1000
1001
/************************************************************************/
1002
/*                             MarkDirty()                              */
1003
/*                                                                      */
1004
/*      Mark this node as dirty (in need of writing to disk), and       */
1005
/*      also mark the tree as a whole as being dirty.                   */
1006
/************************************************************************/
1007
1008
void HFAEntry::MarkDirty()
1009
1010
5.39M
{
1011
5.39M
    bDirty = true;
1012
5.39M
    psHFA->bTreeDirty = true;
1013
5.39M
}