Coverage Report

Created: 2025-12-31 08:30

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
17.8k
#define CPLTimeVal timeval
63
17.8k
#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.02k
    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.46M
{
110
1.46M
    void *memptr = VSIMalloc(size);
111
1.46M
    if (memptr == nullptr && size != 0)
112
0
        throw OutOfMemoryException();
113
1.46M
    return memptr;
114
1.46M
}
115
116
void OGRXercesStandardMemoryManager::deallocate(void *p)
117
1.64M
{
118
1.64M
    if (p)
119
1.46M
        VSIFree(p);
120
1.64M
}
121
122
/************************************************************************/
123
/*               OGRXercesInstrumentedMemoryManager                     */
124
/************************************************************************/
125
126
class OGRXercesInstrumentedMemoryManager final : public MemoryManager
127
{
128
  public:
129
4.02k
    OGRXercesInstrumentedMemoryManager() = default;
130
131
    MemoryManager *getExceptionMemoryManager() override
132
549k
    {
133
549k
        return gpExceptionMemoryManager;
134
549k
    }
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
52.2M
{
143
52.2M
    if (size > std::numeric_limits<size_t>::max() - 8U)
144
0
        throw OutOfMemoryException();
145
52.2M
    void *memptr = VSIMalloc(size + 8);
146
52.2M
    if (memptr == nullptr)
147
0
        throw OutOfMemoryException();
148
52.2M
    memcpy(memptr, &size, sizeof(XMLSize_t));
149
150
52.2M
    LimitationStruct *pLimitation = nullptr;
151
52.2M
    {
152
52.2M
        CPLMutexHolderD(&hOGRXercesMutex);
153
154
52.2M
        if (gpoMapThreadTimeout)
155
20.3M
        {
156
20.3M
            auto iter = gpoMapThreadTimeout->find(CPLGetPID());
157
20.3M
            if (iter != gpoMapThreadTimeout->end())
158
20.3M
            {
159
20.3M
                pLimitation = &(iter->second);
160
20.3M
            }
161
20.3M
        }
162
52.2M
    }
163
164
    // Big memory allocation can happen in cases like
165
    // https://issues.apache.org/jira/browse/XERCESC-1051
166
52.2M
    if (pLimitation && pLimitation->maxMemAlloc > 0)
167
20.3M
    {
168
20.3M
        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
20.3M
    }
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
52.2M
    if (pLimitation && pLimitation->timeOut > 0)
188
20.3M
    {
189
20.3M
        ++pLimitation->allocCount;
190
20.3M
        if (pLimitation->allocCount == 1000)
191
17.8k
        {
192
17.8k
            pLimitation->allocCount = 0;
193
194
17.8k
            CPLTimeVal tv;
195
17.8k
            CPLGettimeofday(&tv, nullptr);
196
17.8k
            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.2k
                tv.tv_sec + tv.tv_usec * 1e-6 -
201
16.2k
                        (pLimitation->lastTV.tv_sec +
202
16.2k
                         pLimitation->lastTV.tv_usec * 1e-6) >
203
16.2k
                    std::min(0.1, pLimitation->timeOut / 10))
204
1.86k
            {
205
1.86k
                pLimitation->initTV = tv;
206
1.86k
            }
207
15.9k
            else if (tv.tv_sec + tv.tv_usec * 1e-6 -
208
15.9k
                         (pLimitation->initTV.tv_sec +
209
15.9k
                          pLimitation->initTV.tv_usec * 1e-6) >
210
15.9k
                     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
17.7k
            pLimitation->lastTV = tv;
222
17.7k
        }
223
20.3M
    }
224
225
52.2M
    if (pLimitation && pLimitation->maxMemAlloc > 0)
226
20.3M
    {
227
20.3M
        pLimitation->totalAllocSize += size;
228
20.3M
    }
229
230
52.2M
    return static_cast<char *>(memptr) + 8;
231
52.2M
}
232
233
void OGRXercesInstrumentedMemoryManager::deallocate(void *p)
234
57.5M
{
235
57.5M
    if (p)
236
52.2M
    {
237
52.2M
        void *rawptr =
238
52.2M
            reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(p) - 8);
239
52.2M
        XMLSize_t size;
240
52.2M
        memcpy(&size, rawptr, sizeof(XMLSize_t));
241
52.2M
        VSIFree(rawptr);
242
243
52.2M
        LimitationStruct *pLimitation = nullptr;
244
52.2M
        {
245
52.2M
            CPLMutexHolderD(&hOGRXercesMutex);
246
247
52.2M
            if (gpoMapThreadTimeout)
248
18.7M
            {
249
18.7M
                auto iter = gpoMapThreadTimeout->find(CPLGetPID());
250
18.7M
                if (iter != gpoMapThreadTimeout->end())
251
18.7M
                {
252
18.7M
                    pLimitation = &(iter->second);
253
18.7M
                }
254
18.7M
            }
255
52.2M
        }
256
52.2M
        if (pLimitation && pLimitation->maxMemAlloc > 0)
257
12.9M
        {
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
12.9M
            if (pLimitation->totalAllocSize >= size)
264
12.8M
                pLimitation->totalAllocSize -= size;
265
46.2k
            else
266
46.2k
                pLimitation->totalAllocSize = 0;
267
12.9M
        }
268
52.2M
    }
269
57.5M
}
270
271
/************************************************************************/
272
/*                  OGRStartXercesLimitsForThisThread()                 */
273
/************************************************************************/
274
275
void OGRStartXercesLimitsForThisThread(size_t nMaxMemAlloc,
276
                                       const char *pszMsgMaxMemAlloc,
277
                                       double dfTimeoutSecond,
278
                                       const char *pszMsgTimeout)
279
5.13k
{
280
5.13k
    CPLMutexHolderD(&hOGRXercesMutex);
281
5.13k
    if (gpoMapThreadTimeout == nullptr)
282
5.13k
    {
283
5.13k
        gpoMapThreadTimeout = new std::map<GIntBig, LimitationStruct>();
284
5.13k
    }
285
5.13k
    LimitationStruct limitation;
286
5.13k
    limitation.maxMemAlloc = nMaxMemAlloc;
287
5.13k
    if (pszMsgMaxMemAlloc)
288
5.13k
        limitation.osMsgMaxMemAlloc = pszMsgMaxMemAlloc;
289
5.13k
    limitation.timeOut = dfTimeoutSecond;
290
5.13k
    if (pszMsgTimeout)
291
5.13k
        limitation.osMsgTimeout = pszMsgTimeout;
292
5.13k
    (*gpoMapThreadTimeout)[CPLGetPID()] = std::move(limitation);
293
5.13k
}
294
295
/************************************************************************/
296
/*                  OGRStopXercesLimitsForThisThread()                  */
297
/************************************************************************/
298
299
void OGRStopXercesLimitsForThisThread()
300
5.13k
{
301
5.13k
    CPLMutexHolderD(&hOGRXercesMutex);
302
5.13k
    (*gpoMapThreadTimeout).erase(CPLGetPID());
303
5.13k
    if (gpoMapThreadTimeout->empty())
304
5.13k
    {
305
5.13k
        delete gpoMapThreadTimeout;
306
5.13k
        gpoMapThreadTimeout = nullptr;
307
5.13k
    }
308
5.13k
}
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.02k
    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.02k
{
383
4.02k
    CPLMutexHolderD(&hOGRXercesMutex);
384
385
4.02k
    if (nCounter > 0)
386
0
    {
387
0
        nCounter++;
388
0
        return true;
389
0
    }
390
391
4.02k
    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.02k
    else
399
4.02k
    {
400
4.02k
        gpExceptionMemoryManager = new OGRXercesStandardMemoryManager();
401
4.02k
        gpMemoryManager = new OGRXercesInstrumentedMemoryManager();
402
403
4.02k
        try
404
4.02k
        {
405
4.02k
            CPLDebug("OGR", "XMLPlatformUtils::Initialize()");
406
4.02k
            XMLPlatformUtils::Initialize(XMLUni::fgXercescDefaultLocale,
407
4.02k
                                         nullptr, /* nlsHome */
408
4.02k
                                         nullptr, /* panicHandler */
409
4.02k
                                         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.02k
            if (CPLTestBool(CPLGetConfigOption(
414
4.02k
                    "OGR_XERCES_USE_OGR_NET_ACCESSOR", "YES")))
415
4.02k
            {
416
4.02k
                auto oldNetAccessor = XMLPlatformUtils::fgNetAccessor;
417
4.02k
                XMLPlatformUtils::fgNetAccessor = new OGRXercesNetAccessor();
418
4.02k
                delete oldNetAccessor;
419
4.02k
            }
420
421
4.02k
            nCounter = 1;
422
4.02k
            return true;
423
4.02k
        }
424
4.02k
        catch (const XMLException &toCatch)
425
4.02k
        {
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.02k
    }
432
4.02k
}
433
434
/************************************************************************/
435
/*                       OGRDeinitializeXerces()                        */
436
/************************************************************************/
437
438
void OGRDeinitializeXerces()
439
4.02k
{
440
4.02k
    CPLMutexHolderD(&hOGRXercesMutex);
441
4.02k
    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.02k
    nCounter--;
448
4.02k
    if (nCounter == 0)
449
4.02k
    {
450
4.02k
        if (!bXercesWasAlreadyInitializedBeforeUs &&
451
4.02k
            CPLTestBool(CPLGetConfigOption("OGR_XERCES_TERMINATE", "YES")))
452
4.02k
        {
453
4.02k
            CPLDebug("OGR", "XMLPlatformUtils::Terminate()");
454
4.02k
            XMLPlatformUtils::Terminate();
455
456
4.02k
            delete gpMemoryManager;
457
4.02k
            gpMemoryManager = nullptr;
458
4.02k
            delete gpExceptionMemoryManager;
459
4.02k
            gpExceptionMemoryManager = nullptr;
460
4.02k
        }
461
4.02k
    }
462
4.02k
}
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.82M
{
484
5.82M
    CPLString osRet;
485
5.82M
    transcode(panXMLString, osRet, nLimitingChars);
486
5.82M
    return osRet;
487
5.82M
}
488
489
CPLString &transcode(const XMLCh *panXMLString, CPLString &osRet,
490
                     int nLimitingChars)
491
12.6M
{
492
12.6M
    if (panXMLString == nullptr)
493
0
    {
494
0
        osRet = "(null)";
495
0
        return osRet;
496
0
    }
497
498
12.6M
    osRet.clear();
499
12.6M
    if (nLimitingChars > 0)
500
1.37M
        osRet.reserve(nLimitingChars);
501
502
12.6M
    bool bSimpleASCII = true;
503
12.6M
    int nChars = 0;
504
12.6M
    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
502k
        {
510
502k
            bSimpleASCII = false;
511
502k
        }
512
1.25G
        osRet += static_cast<char>(panXMLString[i]);
513
1.25G
        nChars++;
514
1.25G
    }
515
516
12.6M
    if (bSimpleASCII)
517
12.4M
        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
181k
    wchar_t *pwszSource =
525
181k
        static_cast<wchar_t *>(CPLMalloc(sizeof(wchar_t) * (nChars + 1)));
526
390M
    for (int i = 0; i < nChars; i++)
527
390M
        pwszSource[i] = panXMLString[i];
528
181k
    pwszSource[nChars] = 0;
529
530
181k
    char *pszResult = CPLRecodeFromWChar(pwszSource, "WCHAR_T", CPL_ENC_UTF8);
531
532
181k
    osRet = pszResult;
533
534
181k
    CPLFree(pwszSource);
535
181k
    CPLFree(pszResult);
536
537
181k
    return osRet;
538
12.6M
}
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
3.86k
    {
560
3.86k
        return pBinInputStream;
561
3.86k
    }
562
};
563
564
/************************************************************************/
565
/*                      OGRXercesBinInputStream()                       */
566
/************************************************************************/
567
568
OGRXercesBinInputStream::OGRXercesBinInputStream(VSILFILE *fpIn, bool bOwnFPIn)
569
3.86k
    : fp(fpIn), bOwnFP(bOwnFPIn)
570
3.86k
{
571
3.86k
}
572
573
/************************************************************************/
574
/*                     ~OGRXercesBinInputStream()                       */
575
/************************************************************************/
576
577
OGRXercesBinInputStream::~OGRXercesBinInputStream()
578
3.86k
{
579
3.86k
    if (bOwnFP)
580
0
        VSIFCloseL(fp);
581
3.86k
}
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
5.12k
{
599
5.12k
    return static_cast<XMLSize_t>(VSIFReadL(toFill, 1, maxToRead, fp));
600
5.12k
}
601
602
/************************************************************************/
603
/*                       OGRXercesInputSource()                         */
604
/************************************************************************/
605
606
OGRXercesInputSource::OGRXercesInputSource(VSILFILE *fp,
607
                                           MemoryManager *const manager)
608
3.86k
    : InputSource(manager),
609
3.86k
      pBinInputStream(new OGRXercesBinInputStream(fp, false))
610
3.86k
{
611
3.86k
}
612
613
/************************************************************************/
614
/*                      ~OGRXercesInputSource()                         */
615
/************************************************************************/
616
617
3.86k
OGRXercesInputSource::~OGRXercesInputSource() = default;
618
619
/************************************************************************/
620
/*                     OGRCreateXercesInputSource()                     */
621
/************************************************************************/
622
623
InputSource *OGRCreateXercesInputSource(VSILFILE *fp)
624
3.86k
{
625
3.86k
    return new OGRXercesInputSource(fp);
626
3.86k
}
627
628
/************************************************************************/
629
/*                     OGRDestroyXercesInputSource()                    */
630
/************************************************************************/
631
632
void OGRDestroyXercesInputSource(InputSource *is)
633
7.07k
{
634
7.07k
    delete is;
635
7.07k
}
636
637
#endif  // HAVE_XERCES