/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 |