Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogr_xerces.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GML Reader
4
 * Purpose:  Convenience functions for parsing with Xerces-C library
5
 *           Functions for translating back and forth between XMLCh and char.
6
 *           We assume that XMLCh is a simple numeric type that we can
7
 *           correspond 1:1 with char values, but that it likely is larger
8
 *           than a char.
9
 * Author:   Frank Warmerdam, warmerdam@pobox.com
10
 * Author:   Even Rouault, <even.rouault at spatialys.com>
11
 *
12
 ******************************************************************************
13
 * Copyright (c) 2002, Frank Warmerdam
14
 * Copyright (c) 2016, Even Rouault <even.rouault at spatialys.com>
15
 *
16
 * SPDX-License-Identifier: MIT
17
 ****************************************************************************/
18
19
#include "ogr_xerces.h"
20
21
#include "cpl_port.h"
22
#include "cpl_error.h"
23
#include "cpl_multiproc.h"
24
#include "cpl_string.h"
25
26
#include <algorithm>
27
#include <limits>
28
#include <map>
29
30
#ifdef HAVE_XERCES
31
32
class OGRXercesStandardMemoryManager;
33
class OGRXercesInstrumentedMemoryManager;
34
35
/************************************************************************/
36
/*                        CPLGettimeofday()                             */
37
/************************************************************************/
38
39
#if defined(_WIN32) && !defined(__CYGWIN__)
40
#include <sys/timeb.h>
41
42
namespace
43
{
44
struct CPLTimeVal
45
{
46
    time_t tv_sec; /* seconds */
47
    long tv_usec;  /* and microseconds */
48
};
49
}  // namespace
50
51
static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/)
52
{
53
    struct _timeb theTime;
54
55
    _ftime(&theTime);
56
    tp->tv_sec = static_cast<time_t>(theTime.time);
57
    tp->tv_usec = theTime.millitm * 1000;
58
    return 0;
59
}
60
#else
61
#include <sys/time.h> /* for gettimeofday() */
62
18.1k
#define CPLTimeVal timeval
63
18.1k
#define CPLGettimeofday(t, u) gettimeofday(t, u)
64
#endif
65
66
namespace
67
{
68
struct LimitationStruct
69
{
70
    size_t maxMemAlloc = 0;
71
    std::string osMsgMaxMemAlloc{};
72
    double timeOut = 0;
73
    std::string osMsgTimeout{};
74
75
    CPLTimeVal initTV{0, 0};
76
    CPLTimeVal lastTV{0, 0};
77
    size_t totalAllocSize = 0;
78
    size_t allocCount = 0;
79
};
80
}  // namespace
81
82
static CPLMutex *hOGRXercesMutex = nullptr;
83
static int nCounter = 0;
84
static bool bXercesWasAlreadyInitializedBeforeUs = false;
85
static OGRXercesStandardMemoryManager *gpExceptionMemoryManager = nullptr;
86
static OGRXercesInstrumentedMemoryManager *gpMemoryManager = nullptr;
87
static std::map<GIntBig, LimitationStruct> *gpoMapThreadTimeout = nullptr;
88
89
/************************************************************************/
90
/*                    OGRXercesStandardMemoryManager                    */
91
/************************************************************************/
92
93
class OGRXercesStandardMemoryManager final : public MemoryManager
94
{
95
  public:
96
4.31k
    OGRXercesStandardMemoryManager() = default;
97
98
    MemoryManager *getExceptionMemoryManager() override
99
0
    {
100
0
        return this;
101
0
    }
102
103
    void *allocate(XMLSize_t size) override;
104
105
    void deallocate(void *p) override;
106
};
107
108
void *OGRXercesStandardMemoryManager::allocate(XMLSize_t size)
109
1.49M
{
110
1.49M
    void *memptr = VSIMalloc(size);
111
1.49M
    if (memptr == nullptr && size != 0)
112
0
        throw OutOfMemoryException();
113
1.49M
    return memptr;
114
1.49M
}
115
116
void OGRXercesStandardMemoryManager::deallocate(void *p)
117
1.63M
{
118
1.63M
    if (p)
119
1.49M
        VSIFree(p);
120
1.63M
}
121
122
/************************************************************************/
123
/*               OGRXercesInstrumentedMemoryManager                     */
124
/************************************************************************/
125
126
class OGRXercesInstrumentedMemoryManager final : public MemoryManager
127
{
128
  public:
129
4.31k
    OGRXercesInstrumentedMemoryManager() = default;
130
131
    MemoryManager *getExceptionMemoryManager() override
132
548k
    {
133
548k
        return gpExceptionMemoryManager;
134
548k
    }
135
136
    void *allocate(XMLSize_t size) override;
137
138
    void deallocate(void *p) override;
139
};
140
141
void *OGRXercesInstrumentedMemoryManager::allocate(XMLSize_t size)
142
56.3M
{
143
56.3M
    if (size > std::numeric_limits<size_t>::max() - 8U)
144
0
        throw OutOfMemoryException();
145
56.3M
    void *memptr = VSIMalloc(size + 8);
146
56.3M
    if (memptr == nullptr)
147
0
        throw OutOfMemoryException();
148
56.3M
    memcpy(memptr, &size, sizeof(XMLSize_t));
149
150
56.3M
    LimitationStruct *pLimitation = nullptr;
151
56.3M
    {
152
56.3M
        CPLMutexHolderD(&hOGRXercesMutex);
153
154
56.3M
        if (gpoMapThreadTimeout)
155
21.8M
        {
156
21.8M
            auto iter = gpoMapThreadTimeout->find(CPLGetPID());
157
21.8M
            if (iter != gpoMapThreadTimeout->end())
158
21.8M
            {
159
21.8M
                pLimitation = &(iter->second);
160
21.8M
            }
161
21.8M
        }
162
56.3M
    }
163
164
    // Big memory allocation can happen in cases like
165
    // https://issues.apache.org/jira/browse/XERCESC-1051
166
56.3M
    if (pLimitation && pLimitation->maxMemAlloc > 0)
167
21.8M
    {
168
21.8M
        if (pLimitation->totalAllocSize + size > pLimitation->maxMemAlloc)
169
2
        {
170
2
            pLimitation->maxMemAlloc = 0;
171
2
            VSIFree(memptr);
172
2
            if (!pLimitation->osMsgMaxMemAlloc.empty())
173
2
            {
174
2
                CPLError(CE_Failure, CPLE_AppDefined, "%s",
175
2
                         pLimitation->osMsgMaxMemAlloc.c_str());
176
2
            }
177
2
            throw OutOfMemoryException();
178
2
        }
179
21.8M
    }
180
181
    // Quite a hack, but some pathologic schema can cause excessive
182
    // processing time. As memory allocations are regularly done, we
183
    // measure the time of those consecutive allocations and check it
184
    // does not exceed a threshold set by OGRStartXercesTimeoutForThisThread()
185
    // Can happen in cases like
186
    // https://issues.apache.org/jira/browse/XERCESC-1051
187
56.3M
    if (pLimitation && pLimitation->timeOut > 0)
188
21.8M
    {
189
21.8M
        ++pLimitation->allocCount;
190
21.8M
        if (pLimitation->allocCount == 1000)
191
18.1k
        {
192
18.1k
            pLimitation->allocCount = 0;
193
194
18.1k
            CPLTimeVal tv;
195
18.1k
            CPLGettimeofday(&tv, nullptr);
196
18.1k
            if (pLimitation->initTV.tv_sec == 0 ||
197
                // Reset the counter if the delay between the last 1000 memory
198
                // allocations is too large. This enables being tolerant to
199
                // network requests.
200
16.4k
                tv.tv_sec + tv.tv_usec * 1e-6 -
201
16.4k
                        (pLimitation->lastTV.tv_sec +
202
16.4k
                         pLimitation->lastTV.tv_usec * 1e-6) >
203
16.4k
                    std::min(0.1, pLimitation->timeOut / 10))
204
1.92k
            {
205
1.92k
                pLimitation->initTV = tv;
206
1.92k
            }
207
16.2k
            else if (tv.tv_sec + tv.tv_usec * 1e-6 -
208
16.2k
                         (pLimitation->initTV.tv_sec +
209
16.2k
                          pLimitation->initTV.tv_usec * 1e-6) >
210
16.2k
                     pLimitation->timeOut)
211
3
            {
212
3
                pLimitation->timeOut = 0;
213
3
                VSIFree(memptr);
214
3
                if (!pLimitation->osMsgTimeout.empty())
215
3
                {
216
3
                    CPLError(CE_Failure, CPLE_AppDefined, "%s",
217
3
                             pLimitation->osMsgTimeout.c_str());
218
3
                }
219
3
                throw OutOfMemoryException();
220
3
            }
221
18.1k
            pLimitation->lastTV = tv;
222
18.1k
        }
223
21.8M
    }
224
225
56.3M
    if (pLimitation && pLimitation->maxMemAlloc > 0)
226
21.8M
    {
227
21.8M
        pLimitation->totalAllocSize += size;
228
21.8M
    }
229
230
56.3M
    return static_cast<char *>(memptr) + 8;
231
56.3M
}
232
233
void OGRXercesInstrumentedMemoryManager::deallocate(void *p)
234
62.3M
{
235
62.3M
    if (p)
236
56.3M
    {
237
56.3M
        void *rawptr =
238
56.3M
            reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(p) - 8);
239
56.3M
        XMLSize_t size;
240
56.3M
        memcpy(&size, rawptr, sizeof(XMLSize_t));
241
56.3M
        VSIFree(rawptr);
242
243
56.3M
        LimitationStruct *pLimitation = nullptr;
244
56.3M
        {
245
56.3M
            CPLMutexHolderD(&hOGRXercesMutex);
246
247
56.3M
            if (gpoMapThreadTimeout)
248
19.1M
            {
249
19.1M
                auto iter = gpoMapThreadTimeout->find(CPLGetPID());
250
19.1M
                if (iter != gpoMapThreadTimeout->end())
251
19.1M
                {
252
19.1M
                    pLimitation = &(iter->second);
253
19.1M
                }
254
19.1M
            }
255
56.3M
        }
256
56.3M
        if (pLimitation && pLimitation->maxMemAlloc > 0)
257
13.3M
        {
258
            // Memory allocations aren't necessarily paired within
259
            // a OGRStartXercesLimitsForThisThread() /
260
            // OGRStopXercesLimitsForThisThread() session. Probably due to
261
            // some caching with Xerces. So handle this gracefully to avoid
262
            // unsigned integer underflow.
263
13.3M
            if (pLimitation->totalAllocSize >= size)
264
13.3M
                pLimitation->totalAllocSize -= size;
265
40.5k
            else
266
40.5k
                pLimitation->totalAllocSize = 0;
267
13.3M
        }
268
56.3M
    }
269
62.3M
}
270
271
/************************************************************************/
272
/*                  OGRStartXercesLimitsForThisThread()                 */
273
/************************************************************************/
274
275
void OGRStartXercesLimitsForThisThread(size_t nMaxMemAlloc,
276
                                       const char *pszMsgMaxMemAlloc,
277
                                       double dfTimeoutSecond,
278
                                       const char *pszMsgTimeout)
279
7.40k
{
280
7.40k
    CPLMutexHolderD(&hOGRXercesMutex);
281
7.40k
    if (gpoMapThreadTimeout == nullptr)
282
7.40k
    {
283
7.40k
        gpoMapThreadTimeout = new std::map<GIntBig, LimitationStruct>();
284
7.40k
    }
285
7.40k
    LimitationStruct limitation;
286
7.40k
    limitation.maxMemAlloc = nMaxMemAlloc;
287
7.40k
    if (pszMsgMaxMemAlloc)
288
7.40k
        limitation.osMsgMaxMemAlloc = pszMsgMaxMemAlloc;
289
7.40k
    limitation.timeOut = dfTimeoutSecond;
290
7.40k
    if (pszMsgTimeout)
291
7.40k
        limitation.osMsgTimeout = pszMsgTimeout;
292
7.40k
    (*gpoMapThreadTimeout)[CPLGetPID()] = std::move(limitation);
293
7.40k
}
294
295
/************************************************************************/
296
/*                  OGRStopXercesLimitsForThisThread()                  */
297
/************************************************************************/
298
299
void OGRStopXercesLimitsForThisThread()
300
7.40k
{
301
7.40k
    CPLMutexHolderD(&hOGRXercesMutex);
302
7.40k
    (*gpoMapThreadTimeout).erase(CPLGetPID());
303
7.40k
    if (gpoMapThreadTimeout->empty())
304
7.40k
    {
305
7.40k
        delete gpoMapThreadTimeout;
306
7.40k
        gpoMapThreadTimeout = nullptr;
307
7.40k
    }
308
7.40k
}
309
310
/************************************************************************/
311
/*                      OGRXercesBinInputStream                         */
312
/************************************************************************/
313
314
class OGRXercesBinInputStream final : public BinInputStream
315
{
316
    CPL_DISALLOW_COPY_ASSIGN(OGRXercesBinInputStream)
317
318
    VSILFILE *fp = nullptr;
319
    bool bOwnFP = false;
320
    XMLCh emptyString = 0;
321
322
  public:
323
    explicit OGRXercesBinInputStream(VSILFILE *fpIn, bool bOwnFPIn);
324
    ~OGRXercesBinInputStream() override;
325
326
    XMLFilePos curPos() const override;
327
    XMLSize_t readBytes(XMLByte *const toFill,
328
                        const XMLSize_t maxToRead) override;
329
330
    const XMLCh *getContentType() const override
331
0
    {
332
0
        return &emptyString;
333
0
    }
334
};
335
336
/************************************************************************/
337
/*                      OGRXercesNetAccessor                            */
338
/************************************************************************/
339
340
class OGRXercesNetAccessor final : public XMLNetAccessor
341
{
342
  public:
343
4.31k
    OGRXercesNetAccessor() = default;
344
345
    BinInputStream *makeNew(const XMLURL &urlSource,
346
                            const XMLNetHTTPInfo *httpInfo) override;
347
348
    const XMLCh *getId() const override
349
0
    {
350
0
        return fgMyName;
351
0
    }
352
353
  private:
354
    static const XMLCh fgMyName[];
355
356
    OGRXercesNetAccessor(const OGRXercesNetAccessor &);
357
    OGRXercesNetAccessor &operator=(const OGRXercesNetAccessor &);
358
};
359
360
const XMLCh OGRXercesNetAccessor::fgMyName[] = {
361
    chLatin_O, chLatin_G, chLatin_R, chLatin_X, chLatin_e, chLatin_r, chLatin_c,
362
    chLatin_e, chLatin_s, chLatin_N, chLatin_e, chLatin_t, chLatin_A, chLatin_c,
363
    chLatin_c, chLatin_e, chLatin_s, chLatin_s, chLatin_o, chLatin_r, chNull};
364
365
BinInputStream *
366
OGRXercesNetAccessor::makeNew(const XMLURL &urlSource,
367
                              const XMLNetHTTPInfo * /*httpInfo*/)
368
0
{
369
0
    const std::string osURL =
370
0
        "/vsicurl_streaming/" + transcode(urlSource.getURLText());
371
0
    VSILFILE *fp = VSIFOpenL(osURL.c_str(), "rb");
372
0
    if (!fp)
373
0
        return nullptr;
374
0
    return new OGRXercesBinInputStream(fp, true);
375
0
}
376
377
/************************************************************************/
378
/*                        OGRInitializeXerces()                         */
379
/************************************************************************/
380
381
bool OGRInitializeXerces()
382
4.31k
{
383
4.31k
    CPLMutexHolderD(&hOGRXercesMutex);
384
385
4.31k
    if (nCounter > 0)
386
0
    {
387
0
        nCounter++;
388
0
        return true;
389
0
    }
390
391
4.31k
    if (XMLPlatformUtils::fgMemoryManager != nullptr)
392
0
    {
393
0
        CPLDebug("OGR", "Xerces-C already initialized before GDAL");
394
0
        bXercesWasAlreadyInitializedBeforeUs = true;
395
0
        nCounter = 1;
396
0
        return true;
397
0
    }
398
4.31k
    else
399
4.31k
    {
400
4.31k
        gpExceptionMemoryManager = new OGRXercesStandardMemoryManager();
401
4.31k
        gpMemoryManager = new OGRXercesInstrumentedMemoryManager();
402
403
4.31k
        try
404
4.31k
        {
405
4.31k
            CPLDebug("OGR", "XMLPlatformUtils::Initialize()");
406
4.31k
            XMLPlatformUtils::Initialize(XMLUni::fgXercescDefaultLocale,
407
4.31k
                                         nullptr, /* nlsHome */
408
4.31k
                                         nullptr, /* panicHandler */
409
4.31k
                                         gpMemoryManager);
410
411
            // Install our own network accessor instead of the default Xerces-C
412
            // one This enables us in particular to honour GDAL_HTTP_TIMEOUT
413
4.31k
            if (CPLTestBool(CPLGetConfigOption(
414
4.31k
                    "OGR_XERCES_USE_OGR_NET_ACCESSOR", "YES")))
415
4.31k
            {
416
4.31k
                auto oldNetAccessor = XMLPlatformUtils::fgNetAccessor;
417
4.31k
                XMLPlatformUtils::fgNetAccessor = new OGRXercesNetAccessor();
418
4.31k
                delete oldNetAccessor;
419
4.31k
            }
420
421
4.31k
            nCounter = 1;
422
4.31k
            return true;
423
4.31k
        }
424
4.31k
        catch (const XMLException &toCatch)
425
4.31k
        {
426
0
            CPLError(CE_Failure, CPLE_AppDefined,
427
0
                     "Exception initializing Xerces: %s",
428
0
                     transcode(toCatch.getMessage()).c_str());
429
0
            return false;
430
0
        }
431
4.31k
    }
432
4.31k
}
433
434
/************************************************************************/
435
/*                       OGRDeinitializeXerces()                        */
436
/************************************************************************/
437
438
void OGRDeinitializeXerces()
439
4.31k
{
440
4.31k
    CPLMutexHolderD(&hOGRXercesMutex);
441
4.31k
    if (nCounter == 0)
442
0
    {
443
0
        CPLError(CE_Failure, CPLE_AppDefined,
444
0
                 "Unpaired OGRInitializeXerces / OGRDeinitializeXerces calls");
445
0
        return;
446
0
    }
447
4.31k
    nCounter--;
448
4.31k
    if (nCounter == 0)
449
4.31k
    {
450
4.31k
        if (!bXercesWasAlreadyInitializedBeforeUs &&
451
4.31k
            CPLTestBool(CPLGetConfigOption("OGR_XERCES_TERMINATE", "YES")))
452
4.31k
        {
453
4.31k
            CPLDebug("OGR", "XMLPlatformUtils::Terminate()");
454
4.31k
            XMLPlatformUtils::Terminate();
455
456
4.31k
            delete gpMemoryManager;
457
4.31k
            gpMemoryManager = nullptr;
458
4.31k
            delete gpExceptionMemoryManager;
459
4.31k
            gpExceptionMemoryManager = nullptr;
460
4.31k
        }
461
4.31k
    }
462
4.31k
}
463
464
/************************************************************************/
465
/*                       OGRCleanupXercesMutex()                        */
466
/************************************************************************/
467
468
void OGRCleanupXercesMutex()
469
0
{
470
0
    if (hOGRXercesMutex != nullptr)
471
0
        CPLDestroyMutex(hOGRXercesMutex);
472
0
    hOGRXercesMutex = nullptr;
473
0
}
474
475
namespace OGR
476
{
477
478
/************************************************************************/
479
/*                            transcode()                               */
480
/************************************************************************/
481
482
CPLString transcode(const XMLCh *panXMLString, int nLimitingChars)
483
5.97M
{
484
5.97M
    CPLString osRet;
485
5.97M
    transcode(panXMLString, osRet, nLimitingChars);
486
5.97M
    return osRet;
487
5.97M
}
488
489
CPLString &transcode(const XMLCh *panXMLString, CPLString &osRet,
490
                     int nLimitingChars)
491
12.5M
{
492
12.5M
    if (panXMLString == nullptr)
493
0
    {
494
0
        osRet = "(null)";
495
0
        return osRet;
496
0
    }
497
498
12.5M
    osRet.clear();
499
12.5M
    if (nLimitingChars > 0)
500
1.42M
        osRet.reserve(nLimitingChars);
501
502
12.5M
    bool bSimpleASCII = true;
503
12.5M
    int nChars = 0;
504
12.5M
    for (int i = 0;
505
1.26G
         panXMLString[i] != 0 && (nLimitingChars < 0 || i < nLimitingChars);
506
1.25G
         i++)
507
1.25G
    {
508
1.25G
        if (panXMLString[i] > 127)
509
423k
        {
510
423k
            bSimpleASCII = false;
511
423k
        }
512
1.25G
        osRet += static_cast<char>(panXMLString[i]);
513
1.25G
        nChars++;
514
1.25G
    }
515
516
12.5M
    if (bSimpleASCII)
517
12.3M
        return osRet;
518
519
    /* -------------------------------------------------------------------- */
520
    /*      The simple translation was wrong, because the source is not     */
521
    /*      all simple ASCII characters.  Redo using the more expensive     */
522
    /*      recoding API.                                                   */
523
    /* -------------------------------------------------------------------- */
524
175k
    wchar_t *pwszSource =
525
175k
        static_cast<wchar_t *>(CPLMalloc(sizeof(wchar_t) * (nChars + 1)));
526
340M
    for (int i = 0; i < nChars; i++)
527
340M
        pwszSource[i] = panXMLString[i];
528
175k
    pwszSource[nChars] = 0;
529
530
175k
    char *pszResult = CPLRecodeFromWChar(pwszSource, "WCHAR_T", CPL_ENC_UTF8);
531
532
175k
    osRet = pszResult;
533
534
175k
    CPLFree(pwszSource);
535
175k
    CPLFree(pszResult);
536
537
175k
    return osRet;
538
12.5M
}
539
540
}  // namespace OGR
541
542
/************************************************************************/
543
/*                       OGRXercesInputSource                           */
544
/************************************************************************/
545
546
class OGRXercesInputSource : public InputSource
547
{
548
    OGRXercesBinInputStream *pBinInputStream;
549
550
    CPL_DISALLOW_COPY_ASSIGN(OGRXercesInputSource)
551
552
  public:
553
    explicit OGRXercesInputSource(
554
        VSILFILE *fp,
555
        MemoryManager *const manager = XMLPlatformUtils::fgMemoryManager);
556
    ~OGRXercesInputSource() override;
557
558
    BinInputStream *makeStream() const override
559
4.61k
    {
560
4.61k
        return pBinInputStream;
561
4.61k
    }
562
};
563
564
/************************************************************************/
565
/*                      OGRXercesBinInputStream()                       */
566
/************************************************************************/
567
568
OGRXercesBinInputStream::OGRXercesBinInputStream(VSILFILE *fpIn, bool bOwnFPIn)
569
4.61k
    : fp(fpIn), bOwnFP(bOwnFPIn)
570
4.61k
{
571
4.61k
}
572
573
/************************************************************************/
574
/*                     ~OGRXercesBinInputStream()                       */
575
/************************************************************************/
576
577
OGRXercesBinInputStream::~OGRXercesBinInputStream()
578
4.61k
{
579
4.61k
    if (bOwnFP)
580
0
        VSIFCloseL(fp);
581
4.61k
}
582
583
/************************************************************************/
584
/*                              curPos()                                */
585
/************************************************************************/
586
587
XMLFilePos OGRXercesBinInputStream::curPos() const
588
0
{
589
0
    return static_cast<XMLFilePos>(VSIFTellL(fp));
590
0
}
591
592
/************************************************************************/
593
/*                            readBytes()                               */
594
/************************************************************************/
595
596
XMLSize_t OGRXercesBinInputStream::readBytes(XMLByte *const toFill,
597
                                             const XMLSize_t maxToRead)
598
6.01k
{
599
6.01k
    return static_cast<XMLSize_t>(VSIFReadL(toFill, 1, maxToRead, fp));
600
6.01k
}
601
602
/************************************************************************/
603
/*                       OGRXercesInputSource()                         */
604
/************************************************************************/
605
606
OGRXercesInputSource::OGRXercesInputSource(VSILFILE *fp,
607
                                           MemoryManager *const manager)
608
4.61k
    : InputSource(manager),
609
4.61k
      pBinInputStream(new OGRXercesBinInputStream(fp, false))
610
4.61k
{
611
4.61k
}
612
613
/************************************************************************/
614
/*                      ~OGRXercesInputSource()                         */
615
/************************************************************************/
616
617
4.61k
OGRXercesInputSource::~OGRXercesInputSource() = default;
618
619
/************************************************************************/
620
/*                     OGRCreateXercesInputSource()                     */
621
/************************************************************************/
622
623
InputSource *OGRCreateXercesInputSource(VSILFILE *fp)
624
4.61k
{
625
4.61k
    return new OGRXercesInputSource(fp);
626
4.61k
}
627
628
/************************************************************************/
629
/*                     OGRDestroyXercesInputSource()                    */
630
/************************************************************************/
631
632
void OGRDestroyXercesInputSource(InputSource *is)
633
7.88k
{
634
7.88k
    delete is;
635
7.88k
}
636
637
#endif  // HAVE_XERCES