/src/pugixml/src/pugixml.cpp
Line | Count | Source |
1 | | /** |
2 | | * pugixml parser - version 1.15 |
3 | | * -------------------------------------------------------- |
4 | | * Report bugs and download new versions at https://pugixml.org/ |
5 | | * |
6 | | * SPDX-FileCopyrightText: Copyright (C) 2006-2025, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) |
7 | | * SPDX-License-Identifier: MIT |
8 | | * |
9 | | * See LICENSE.md or notice at the end of this file. |
10 | | */ |
11 | | |
12 | | #ifndef SOURCE_PUGIXML_CPP |
13 | | #define SOURCE_PUGIXML_CPP |
14 | | |
15 | | #include "pugixml.hpp" |
16 | | |
17 | | #include <stdlib.h> |
18 | | #include <stdio.h> |
19 | | #include <string.h> |
20 | | #include <assert.h> |
21 | | #include <limits.h> |
22 | | |
23 | | #ifdef PUGIXML_WCHAR_MODE |
24 | | # include <wchar.h> |
25 | | #endif |
26 | | |
27 | | #ifndef PUGIXML_NO_XPATH |
28 | | # include <math.h> |
29 | | # include <float.h> |
30 | | #endif |
31 | | |
32 | | #ifndef PUGIXML_NO_STL |
33 | | # include <istream> |
34 | | # include <ostream> |
35 | | # include <string> |
36 | | #endif |
37 | | |
38 | | // For placement new |
39 | | #include <new> |
40 | | |
41 | | // For load_file |
42 | | #if defined(__linux__) || defined(__APPLE__) |
43 | | #include <sys/stat.h> |
44 | | #endif |
45 | | |
46 | | #ifdef _MSC_VER |
47 | | # pragma warning(push) |
48 | | # pragma warning(disable: 4127) // conditional expression is constant |
49 | | # pragma warning(disable: 4324) // structure was padded due to __declspec(align()) |
50 | | # pragma warning(disable: 4702) // unreachable code |
51 | | # pragma warning(disable: 4996) // this function or variable may be unsafe |
52 | | #endif |
53 | | |
54 | | #if defined(__clang__) |
55 | | # pragma clang diagnostic push |
56 | | # pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // NULL as null pointer constant |
57 | | #endif |
58 | | |
59 | | #if defined(_MSC_VER) && defined(__c2__) |
60 | | # pragma clang diagnostic push |
61 | | # pragma clang diagnostic ignored "-Wdeprecated" // this function or variable may be unsafe |
62 | | #endif |
63 | | |
64 | | #ifdef __INTEL_COMPILER |
65 | | # pragma warning(disable: 177) // function was declared but never referenced |
66 | | # pragma warning(disable: 279) // controlling expression is constant |
67 | | # pragma warning(disable: 1478 1786) // function was declared "deprecated" |
68 | | # pragma warning(disable: 1684) // conversion from pointer to same-sized integral type |
69 | | #endif |
70 | | |
71 | | #if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) |
72 | | # pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away |
73 | | #endif |
74 | | |
75 | | #ifdef __BORLANDC__ |
76 | | # pragma option push |
77 | | # pragma warn -8008 // condition is always false |
78 | | # pragma warn -8066 // unreachable code |
79 | | #endif |
80 | | |
81 | | #ifdef __SNC__ |
82 | | // Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug |
83 | | # pragma diag_suppress=178 // function was declared but never referenced |
84 | | # pragma diag_suppress=237 // controlling expression is constant |
85 | | #endif |
86 | | |
87 | | #ifdef __TI_COMPILER_VERSION__ |
88 | | # pragma diag_suppress 179 // function was declared but never referenced |
89 | | #endif |
90 | | |
91 | | // Inlining controls |
92 | | #if defined(_MSC_VER) && _MSC_VER >= 1300 |
93 | | # define PUGI_IMPL_NO_INLINE __declspec(noinline) |
94 | | #elif defined(__GNUC__) |
95 | | # define PUGI_IMPL_NO_INLINE __attribute__((noinline)) |
96 | | #else |
97 | | # define PUGI_IMPL_NO_INLINE |
98 | | #endif |
99 | | |
100 | | // Branch weight controls |
101 | | #if defined(__GNUC__) && !defined(__c2__) |
102 | 9.58M | # define PUGI_IMPL_UNLIKELY(cond) __builtin_expect(cond, 0) |
103 | | #else |
104 | | # define PUGI_IMPL_UNLIKELY(cond) (cond) |
105 | | #endif |
106 | | |
107 | | // Simple static assertion |
108 | 38.1k | #define PUGI_IMPL_STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } |
109 | | |
110 | | // Digital Mars C++ bug workaround for passing char loaded from memory via stack |
111 | | #ifdef __DMC__ |
112 | | # define PUGI_IMPL_DMC_VOLATILE volatile |
113 | | #else |
114 | | # define PUGI_IMPL_DMC_VOLATILE |
115 | | #endif |
116 | | |
117 | | // Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces "attribute directive ignored" warnings |
118 | | #if defined(__clang__) && defined(__has_attribute) |
119 | | # if __has_attribute(no_sanitize) |
120 | | # define PUGI_IMPL_UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow"))) |
121 | | # else |
122 | | # define PUGI_IMPL_UNSIGNED_OVERFLOW |
123 | | # endif |
124 | | #else |
125 | | # define PUGI_IMPL_UNSIGNED_OVERFLOW |
126 | | #endif |
127 | | |
128 | | // Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) |
129 | | #if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) |
130 | | using std::memcpy; |
131 | | using std::memmove; |
132 | | using std::memset; |
133 | | #endif |
134 | | |
135 | | // Old versions of GCC do not define ::malloc and ::free depending on header include order |
136 | | #if defined(__GNUC__) && (__GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)) |
137 | | using std::malloc; |
138 | | using std::free; |
139 | | #endif |
140 | | |
141 | | // Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations |
142 | | #if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX) |
143 | | # define LLONG_MIN (-LLONG_MAX - 1LL) |
144 | | # define LLONG_MAX __LONG_LONG_MAX__ |
145 | | # define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) |
146 | | #endif |
147 | | |
148 | | // In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features |
149 | | #if defined(_MSC_VER) && !defined(__S3E__) && !defined(_WIN32_WCE) |
150 | | # define PUGI_IMPL_MSVC_CRT_VERSION _MSC_VER |
151 | | #elif defined(_WIN32_WCE) |
152 | | # define PUGI_IMPL_MSVC_CRT_VERSION 1310 // MSVC7.1 |
153 | | #endif |
154 | | |
155 | | // Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size. |
156 | | #if __cplusplus >= 201103 |
157 | 0 | # define PUGI_IMPL_SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) |
158 | | #elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 |
159 | | # define PUGI_IMPL_SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__) |
160 | | #elif defined(__APPLE__) && __clang_major__ >= 14 // Xcode 14 marks sprintf as deprecated while still using C++98 by default |
161 | | # define PUGI_IMPL_SNPRINTF(buf, fmt, arg1, arg2) snprintf(buf, sizeof(buf), fmt, arg1, arg2) |
162 | | #else |
163 | | # define PUGI_IMPL_SNPRINTF sprintf |
164 | | #endif |
165 | | |
166 | | // We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat. |
167 | | #ifdef PUGIXML_HEADER_ONLY |
168 | | # define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { |
169 | | # define PUGI_IMPL_NS_END } } |
170 | | # define PUGI_IMPL_FN inline |
171 | | # define PUGI_IMPL_FN_NO_INLINE inline |
172 | | #else |
173 | | # if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces |
174 | | # define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { |
175 | | # define PUGI_IMPL_NS_END } } |
176 | | # else |
177 | | # define PUGI_IMPL_NS_BEGIN namespace pugi { namespace impl { namespace { |
178 | | # define PUGI_IMPL_NS_END } } } |
179 | | # endif |
180 | | # define PUGI_IMPL_FN |
181 | | # define PUGI_IMPL_FN_NO_INLINE PUGI_IMPL_NO_INLINE |
182 | | #endif |
183 | | |
184 | | // uintptr_t |
185 | | #if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561) |
186 | | namespace pugi |
187 | | { |
188 | | # ifndef _UINTPTR_T_DEFINED |
189 | | typedef size_t uintptr_t; |
190 | | # endif |
191 | | |
192 | | typedef unsigned __int8 uint8_t; |
193 | | typedef unsigned __int16 uint16_t; |
194 | | typedef unsigned __int32 uint32_t; |
195 | | } |
196 | | #else |
197 | | # include <stdint.h> |
198 | | #endif |
199 | | |
200 | | // Memory allocation |
201 | | PUGI_IMPL_NS_BEGIN |
202 | | PUGI_IMPL_FN void* default_allocate(size_t size) |
203 | 19.1k | { |
204 | 19.1k | return malloc(size); |
205 | 19.1k | } |
206 | | |
207 | | PUGI_IMPL_FN void default_deallocate(void* ptr) |
208 | 19.1k | { |
209 | 19.1k | free(ptr); |
210 | 19.1k | } |
211 | | |
212 | | template <typename T> |
213 | | struct xml_memory_management_function_storage |
214 | | { |
215 | | static allocation_function allocate; |
216 | | static deallocation_function deallocate; |
217 | | }; |
218 | | |
219 | | // Global allocation functions are stored in class statics so that in header mode linker deduplicates them |
220 | | // Without a template<> we'll get multiple definitions of the same static |
221 | | template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate; |
222 | | template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate; |
223 | | |
224 | | typedef xml_memory_management_function_storage<int> xml_memory; |
225 | | PUGI_IMPL_NS_END |
226 | | |
227 | | // String utilities |
228 | | PUGI_IMPL_NS_BEGIN |
229 | | // Get string length |
230 | | PUGI_IMPL_FN size_t strlength(const char_t* s) |
231 | 0 | { |
232 | 0 | assert(s); |
233 | | |
234 | | #ifdef PUGIXML_WCHAR_MODE |
235 | | return wcslen(s); |
236 | | #else |
237 | 0 | return strlen(s); |
238 | 0 | #endif |
239 | 0 | } |
240 | | |
241 | | // Compare two strings |
242 | | PUGI_IMPL_FN bool strequal(const char_t* src, const char_t* dst) |
243 | 0 | { |
244 | 0 | assert(src && dst); |
245 | | |
246 | | #ifdef PUGIXML_WCHAR_MODE |
247 | | return wcscmp(src, dst) == 0; |
248 | | #else |
249 | 0 | return strcmp(src, dst) == 0; |
250 | 0 | #endif |
251 | 0 | } |
252 | | |
253 | | #ifdef PUGIXML_HAS_STRING_VIEW |
254 | | // Check if the null-terminated dst string is equal to the entire contents of srcview |
255 | | PUGI_IMPL_FN bool stringview_equal(string_view_t srcview, const char_t* dst) |
256 | 0 | { |
257 | | // std::basic_string_view::compare(const char*) has the right behavior, but it performs an |
258 | | // extra traversal of dst to compute its length. |
259 | 0 | assert(dst); |
260 | 0 | const char_t* src = srcview.data(); |
261 | 0 | size_t srclen = srcview.size(); |
262 | |
|
263 | 0 | while (srclen && *dst && *src == *dst) |
264 | 0 | { |
265 | 0 | --srclen; ++dst; ++src; |
266 | 0 | } |
267 | 0 | return srclen == 0 && *dst == 0; |
268 | 0 | } |
269 | | #endif |
270 | | |
271 | | // Compare lhs with [rhs_begin, rhs_end) |
272 | | PUGI_IMPL_FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) |
273 | 0 | { |
274 | 0 | for (size_t i = 0; i < count; ++i) |
275 | 0 | if (lhs[i] != rhs[i]) |
276 | 0 | return false; |
277 | | |
278 | 0 | return lhs[count] == 0; |
279 | 0 | } |
280 | | |
281 | | // Get length of wide string, even if CRT lacks wide character support |
282 | | PUGI_IMPL_FN size_t strlength_wide(const wchar_t* s) |
283 | 0 | { |
284 | 0 | assert(s); |
285 | | |
286 | | #ifdef PUGIXML_WCHAR_MODE |
287 | | return wcslen(s); |
288 | | #else |
289 | 0 | const wchar_t* end = s; |
290 | 0 | while (*end) end++; |
291 | 0 | return static_cast<size_t>(end - s); |
292 | 0 | #endif |
293 | 0 | } |
294 | | PUGI_IMPL_NS_END |
295 | | |
296 | | // auto_ptr-like object for exception recovery |
297 | | PUGI_IMPL_NS_BEGIN |
298 | | template <typename T> struct auto_deleter |
299 | | { |
300 | | typedef void (*D)(T*); |
301 | | |
302 | | T* data; |
303 | | D deleter; |
304 | | |
305 | 11.4k | auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_) |
306 | 11.4k | { |
307 | 11.4k | } pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<void>::auto_deleter(void*, void (*)(void*)) Line | Count | Source | 305 | 11.4k | auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_) | 306 | 11.4k | { | 307 | 11.4k | } |
Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<pugi::impl::(anonymous namespace)::xml_stream_chunk<char> >::auto_deleter(pugi::impl::(anonymous namespace)::xml_stream_chunk<char>*, void (*)(pugi::impl::(anonymous namespace)::xml_stream_chunk<char>*)) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<pugi::impl::(anonymous namespace)::xml_stream_chunk<wchar_t> >::auto_deleter(pugi::impl::(anonymous namespace)::xml_stream_chunk<wchar_t>*, void (*)(pugi::impl::(anonymous namespace)::xml_stream_chunk<wchar_t>*)) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<_IO_FILE>::auto_deleter(_IO_FILE*, void (*)(_IO_FILE*)) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<pugi::impl::(anonymous namespace)::xpath_query_impl>::auto_deleter(pugi::impl::(anonymous namespace)::xpath_query_impl*, void (*)(pugi::impl::(anonymous namespace)::xpath_query_impl*)) |
308 | | |
309 | | ~auto_deleter() |
310 | 11.4k | { |
311 | 11.4k | if (data) deleter(data); |
312 | 11.4k | } pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<void>::~auto_deleter() Line | Count | Source | 310 | 11.4k | { | 311 | 11.4k | if (data) deleter(data); | 312 | 11.4k | } |
Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<pugi::impl::(anonymous namespace)::xml_stream_chunk<char> >::~auto_deleter() Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<pugi::impl::(anonymous namespace)::xml_stream_chunk<wchar_t> >::~auto_deleter() Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<_IO_FILE>::~auto_deleter() Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<pugi::impl::(anonymous namespace)::xpath_query_impl>::~auto_deleter() |
313 | | |
314 | | T* release() |
315 | 11.4k | { |
316 | 11.4k | T* result = data; |
317 | 11.4k | data = NULL; |
318 | 11.4k | return result; |
319 | 11.4k | } pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<void>::release() Line | Count | Source | 315 | 11.4k | { | 316 | 11.4k | T* result = data; | 317 | | data = NULL; | 318 | 11.4k | return result; | 319 | 11.4k | } |
Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<_IO_FILE>::release() Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::auto_deleter<pugi::impl::(anonymous namespace)::xpath_query_impl>::release() |
320 | | }; |
321 | | PUGI_IMPL_NS_END |
322 | | |
323 | | #ifdef PUGIXML_COMPACT |
324 | | PUGI_IMPL_NS_BEGIN |
325 | | class compact_hash_table |
326 | | { |
327 | | public: |
328 | | compact_hash_table(): _items(NULL), _capacity(0), _count(0) |
329 | | { |
330 | | } |
331 | | |
332 | | void clear() |
333 | | { |
334 | | if (_items) |
335 | | { |
336 | | xml_memory::deallocate(_items); |
337 | | _items = NULL; |
338 | | _capacity = 0; |
339 | | _count = 0; |
340 | | } |
341 | | } |
342 | | |
343 | | void* find(const void* key) |
344 | | { |
345 | | if (_capacity == 0) return NULL; |
346 | | |
347 | | item_t* item = get_item(key); |
348 | | assert(item); |
349 | | assert(item->key == key || (item->key == NULL && item->value == NULL)); |
350 | | |
351 | | return item->value; |
352 | | } |
353 | | |
354 | | void insert(const void* key, void* value) |
355 | | { |
356 | | assert(_capacity != 0 && _count < _capacity - _capacity / 4); |
357 | | |
358 | | item_t* item = get_item(key); |
359 | | assert(item); |
360 | | |
361 | | if (item->key == NULL) |
362 | | { |
363 | | _count++; |
364 | | item->key = key; |
365 | | } |
366 | | |
367 | | item->value = value; |
368 | | } |
369 | | |
370 | | bool reserve(size_t extra = 16) |
371 | | { |
372 | | if (_count + extra >= _capacity - _capacity / 4) |
373 | | return rehash(_count + extra); |
374 | | |
375 | | return true; |
376 | | } |
377 | | |
378 | | private: |
379 | | struct item_t |
380 | | { |
381 | | const void* key; |
382 | | void* value; |
383 | | }; |
384 | | |
385 | | item_t* _items; |
386 | | size_t _capacity; |
387 | | |
388 | | size_t _count; |
389 | | |
390 | | bool rehash(size_t count); |
391 | | |
392 | | item_t* get_item(const void* key) |
393 | | { |
394 | | assert(key); |
395 | | assert(_capacity > 0); |
396 | | |
397 | | size_t hashmod = _capacity - 1; |
398 | | size_t bucket = hash(key) & hashmod; |
399 | | |
400 | | for (size_t probe = 0; probe <= hashmod; ++probe) |
401 | | { |
402 | | item_t& probe_item = _items[bucket]; |
403 | | |
404 | | if (probe_item.key == key || probe_item.key == NULL) |
405 | | return &probe_item; |
406 | | |
407 | | // hash collision, quadratic probing |
408 | | bucket = (bucket + probe + 1) & hashmod; |
409 | | } |
410 | | |
411 | | assert(false && "Hash table is full"); // unreachable |
412 | | return NULL; |
413 | | } |
414 | | |
415 | | static PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash(const void* key) |
416 | | { |
417 | | unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key) & 0xffffffff); |
418 | | |
419 | | // MurmurHash3 32-bit finalizer |
420 | | h ^= h >> 16; |
421 | | h *= 0x85ebca6bu; |
422 | | h ^= h >> 13; |
423 | | h *= 0xc2b2ae35u; |
424 | | h ^= h >> 16; |
425 | | |
426 | | return h; |
427 | | } |
428 | | }; |
429 | | |
430 | | PUGI_IMPL_FN_NO_INLINE bool compact_hash_table::rehash(size_t count) |
431 | | { |
432 | | size_t capacity = 32; |
433 | | while (count >= capacity - capacity / 4) |
434 | | capacity *= 2; |
435 | | |
436 | | compact_hash_table rt; |
437 | | rt._capacity = capacity; |
438 | | rt._items = static_cast<item_t*>(xml_memory::allocate(sizeof(item_t) * capacity)); |
439 | | |
440 | | if (!rt._items) |
441 | | return false; |
442 | | |
443 | | memset(rt._items, 0, sizeof(item_t) * capacity); |
444 | | |
445 | | for (size_t i = 0; i < _capacity; ++i) |
446 | | if (_items[i].key) |
447 | | rt.insert(_items[i].key, _items[i].value); |
448 | | |
449 | | if (_items) |
450 | | xml_memory::deallocate(_items); |
451 | | |
452 | | _capacity = capacity; |
453 | | _items = rt._items; |
454 | | |
455 | | assert(_count == rt._count); |
456 | | |
457 | | return true; |
458 | | } |
459 | | |
460 | | PUGI_IMPL_NS_END |
461 | | #endif |
462 | | |
463 | | PUGI_IMPL_NS_BEGIN |
464 | | #ifdef PUGIXML_COMPACT |
465 | | static const uintptr_t xml_memory_block_alignment = 4; |
466 | | #else |
467 | | static const uintptr_t xml_memory_block_alignment = sizeof(void*); |
468 | | #endif |
469 | | |
470 | | // extra metadata bits |
471 | | static const uintptr_t xml_memory_page_contents_shared_mask = 64; |
472 | | static const uintptr_t xml_memory_page_name_allocated_mask = 32; |
473 | | static const uintptr_t xml_memory_page_value_allocated_mask = 16; |
474 | | static const uintptr_t xml_memory_page_type_mask = 15; |
475 | | |
476 | | // combined masks for string uniqueness |
477 | | static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; |
478 | | static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; |
479 | | |
480 | | #ifdef PUGIXML_COMPACT |
481 | | #define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) // unused |
482 | | #define PUGI_IMPL_GETPAGE_IMPL(header) (header).get_page() |
483 | | #else |
484 | 882k | #define PUGI_IMPL_GETHEADER_IMPL(object, page, flags) (((reinterpret_cast<char*>(object) - reinterpret_cast<char*>(page)) << 8) | (flags)) |
485 | | // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings |
486 | 15.2k | #define PUGI_IMPL_GETPAGE_IMPL(header) static_cast<impl::xml_memory_page*>(const_cast<void*>(static_cast<const void*>(reinterpret_cast<const char*>(&header) - (header >> 8)))) |
487 | | #endif |
488 | | |
489 | 15.2k | #define PUGI_IMPL_GETPAGE(n) PUGI_IMPL_GETPAGE_IMPL((n)->header) |
490 | 8.98k | #define PUGI_IMPL_NODETYPE(n) static_cast<xml_node_type>((n)->header & impl::xml_memory_page_type_mask) |
491 | | |
492 | | struct xml_allocator; |
493 | | |
494 | | struct xml_memory_page |
495 | | { |
496 | | static xml_memory_page* construct(void* memory) |
497 | 22.9k | { |
498 | 22.9k | xml_memory_page* result = static_cast<xml_memory_page*>(memory); |
499 | | |
500 | 22.9k | result->allocator = NULL; |
501 | 22.9k | result->prev = NULL; |
502 | 22.9k | result->next = NULL; |
503 | 22.9k | result->busy_size = 0; |
504 | 22.9k | result->freed_size = 0; |
505 | | |
506 | | #ifdef PUGIXML_COMPACT |
507 | | result->compact_string_base = NULL; |
508 | | result->compact_shared_parent = NULL; |
509 | | result->compact_page_marker = NULL; |
510 | | #endif |
511 | | |
512 | 22.9k | return result; |
513 | 22.9k | } |
514 | | |
515 | | xml_allocator* allocator; |
516 | | |
517 | | xml_memory_page* prev; |
518 | | xml_memory_page* next; |
519 | | |
520 | | size_t busy_size; |
521 | | size_t freed_size; |
522 | | |
523 | | #ifdef PUGIXML_COMPACT |
524 | | char_t* compact_string_base; |
525 | | void* compact_shared_parent; |
526 | | uint32_t* compact_page_marker; |
527 | | #endif |
528 | | }; |
529 | | |
530 | | static const size_t xml_memory_page_size = |
531 | | #ifdef PUGIXML_MEMORY_PAGE_SIZE |
532 | | (PUGIXML_MEMORY_PAGE_SIZE) |
533 | | #else |
534 | | 32768 |
535 | | #endif |
536 | | - sizeof(xml_memory_page); |
537 | | |
538 | | struct xml_memory_string_header |
539 | | { |
540 | | uint16_t page_offset; // offset from page->data |
541 | | uint16_t full_size; // 0 if string occupies whole page |
542 | | }; |
543 | | |
544 | | struct xml_allocator |
545 | | { |
546 | 15.2k | xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) |
547 | 15.2k | { |
548 | | #ifdef PUGIXML_COMPACT |
549 | | _hash = NULL; |
550 | | #endif |
551 | 15.2k | } |
552 | | |
553 | | xml_memory_page* allocate_page(size_t data_size) |
554 | 7.70k | { |
555 | 7.70k | size_t size = sizeof(xml_memory_page) + data_size; |
556 | | |
557 | | // allocate block with some alignment, leaving memory for worst-case padding |
558 | 7.70k | void* memory = xml_memory::allocate(size); |
559 | 7.70k | if (!memory) return NULL; |
560 | | |
561 | | // prepare page structure |
562 | 7.70k | xml_memory_page* page = xml_memory_page::construct(memory); |
563 | 7.70k | assert(page); |
564 | | |
565 | 7.70k | assert(this == _root->allocator); |
566 | 7.70k | page->allocator = this; |
567 | | |
568 | 7.70k | return page; |
569 | 7.70k | } |
570 | | |
571 | | static void deallocate_page(xml_memory_page* page) |
572 | 7.70k | { |
573 | 7.70k | xml_memory::deallocate(page); |
574 | 7.70k | } |
575 | | |
576 | | void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); |
577 | | |
578 | | void* allocate_memory(size_t size, xml_memory_page*& out_page) |
579 | 867k | { |
580 | 867k | if (PUGI_IMPL_UNLIKELY(_busy_size + size > xml_memory_page_size)) |
581 | 7.70k | return allocate_memory_oob(size, out_page); |
582 | | |
583 | 859k | void* buf = reinterpret_cast<char*>(_root) + sizeof(xml_memory_page) + _busy_size; |
584 | | |
585 | 859k | _busy_size += size; |
586 | | |
587 | 859k | out_page = _root; |
588 | | |
589 | 859k | return buf; |
590 | 867k | } |
591 | | |
592 | | #ifdef PUGIXML_COMPACT |
593 | | void* allocate_object(size_t size, xml_memory_page*& out_page) |
594 | | { |
595 | | void* result = allocate_memory(size + sizeof(uint32_t), out_page); |
596 | | if (!result) return NULL; |
597 | | |
598 | | // adjust for marker |
599 | | ptrdiff_t offset = static_cast<char*>(result) - reinterpret_cast<char*>(out_page->compact_page_marker); |
600 | | |
601 | | if (PUGI_IMPL_UNLIKELY(static_cast<uintptr_t>(offset) >= 256 * xml_memory_block_alignment)) |
602 | | { |
603 | | // insert new marker |
604 | | uint32_t* marker = static_cast<uint32_t*>(result); |
605 | | |
606 | | *marker = static_cast<uint32_t>(reinterpret_cast<char*>(marker) - reinterpret_cast<char*>(out_page)); |
607 | | out_page->compact_page_marker = marker; |
608 | | |
609 | | // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block |
610 | | // this will make sure deallocate_memory correctly tracks the size |
611 | | out_page->freed_size += sizeof(uint32_t); |
612 | | |
613 | | return marker + 1; |
614 | | } |
615 | | else |
616 | | { |
617 | | // roll back uint32_t part |
618 | | _busy_size -= sizeof(uint32_t); |
619 | | |
620 | | return result; |
621 | | } |
622 | | } |
623 | | #else |
624 | | void* allocate_object(size_t size, xml_memory_page*& out_page) |
625 | 867k | { |
626 | 867k | return allocate_memory(size, out_page); |
627 | 867k | } |
628 | | #endif |
629 | | |
630 | | void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) |
631 | 0 | { |
632 | 0 | if (page == _root) page->busy_size = _busy_size; |
633 | |
|
634 | 0 | assert(ptr >= reinterpret_cast<char*>(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast<char*>(page) + sizeof(xml_memory_page) + page->busy_size); |
635 | 0 | (void)!ptr; |
636 | |
|
637 | 0 | page->freed_size += size; |
638 | 0 | assert(page->freed_size <= page->busy_size); |
639 | | |
640 | 0 | if (page->freed_size == page->busy_size) |
641 | 0 | { |
642 | 0 | if (page->next == NULL) |
643 | 0 | { |
644 | 0 | assert(_root == page); |
645 | | |
646 | | // top page freed, just reset sizes |
647 | 0 | page->busy_size = 0; |
648 | 0 | page->freed_size = 0; |
649 | |
|
650 | | #ifdef PUGIXML_COMPACT |
651 | | // reset compact state to maximize efficiency |
652 | | page->compact_string_base = NULL; |
653 | | page->compact_shared_parent = NULL; |
654 | | page->compact_page_marker = NULL; |
655 | | #endif |
656 | |
|
657 | 0 | _busy_size = 0; |
658 | 0 | } |
659 | 0 | else |
660 | 0 | { |
661 | 0 | assert(_root != page); |
662 | 0 | assert(page->prev); |
663 | | |
664 | | // remove from the list |
665 | 0 | page->prev->next = page->next; |
666 | 0 | page->next->prev = page->prev; |
667 | | |
668 | | // deallocate |
669 | 0 | deallocate_page(page); |
670 | 0 | } |
671 | 0 | } |
672 | 0 | } |
673 | | |
674 | | char_t* allocate_string(size_t length) |
675 | 0 | { |
676 | 0 | static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment; |
677 | |
|
678 | 0 | PUGI_IMPL_STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); |
679 | | |
680 | | // allocate memory for string and header block |
681 | 0 | size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); |
682 | | |
683 | | // round size up to block alignment boundary |
684 | 0 | size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1); |
685 | |
|
686 | 0 | xml_memory_page* page; |
687 | 0 | xml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page)); |
688 | |
|
689 | 0 | if (!header) return NULL; |
690 | | |
691 | | // setup header |
692 | 0 | ptrdiff_t page_offset = reinterpret_cast<char*>(header) - reinterpret_cast<char*>(page) - sizeof(xml_memory_page); |
693 | |
|
694 | 0 | assert(page_offset % xml_memory_block_alignment == 0); |
695 | 0 | assert(page_offset >= 0 && static_cast<size_t>(page_offset) < max_encoded_offset); |
696 | 0 | header->page_offset = static_cast<uint16_t>(static_cast<size_t>(page_offset) / xml_memory_block_alignment); |
697 | | |
698 | | // full_size == 0 for large strings that occupy the whole page |
699 | 0 | assert(full_size % xml_memory_block_alignment == 0); |
700 | 0 | assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0)); |
701 | 0 | header->full_size = static_cast<uint16_t>(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0); |
702 | | |
703 | | // round-trip through void* to avoid 'cast increases required alignment of target type' warning |
704 | | // header is guaranteed a pointer-sized alignment, which should be enough for char_t |
705 | 0 | return static_cast<char_t*>(static_cast<void*>(header + 1)); |
706 | 0 | } |
707 | | |
708 | | void deallocate_string(char_t* string) |
709 | 0 | { |
710 | | // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings |
711 | | // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string |
712 | | |
713 | | // get header |
714 | 0 | xml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1; |
715 | 0 | assert(header); |
716 | | |
717 | | // deallocate |
718 | 0 | size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment; |
719 | 0 | xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset)); |
720 | | |
721 | | // if full_size == 0 then this string occupies the whole page |
722 | 0 | size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment; |
723 | |
|
724 | 0 | deallocate_memory(header, full_size, page); |
725 | 0 | } |
726 | | |
727 | | bool reserve() |
728 | 867k | { |
729 | | #ifdef PUGIXML_COMPACT |
730 | | return _hash->reserve(); |
731 | | #else |
732 | 867k | return true; |
733 | 867k | #endif |
734 | 867k | } |
735 | | |
736 | | xml_memory_page* _root; |
737 | | size_t _busy_size; |
738 | | |
739 | | #ifdef PUGIXML_COMPACT |
740 | | compact_hash_table* _hash; |
741 | | #endif |
742 | | }; |
743 | | |
744 | | PUGI_IMPL_FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) |
745 | 7.70k | { |
746 | 7.70k | const size_t large_allocation_threshold = xml_memory_page_size / 4; |
747 | | |
748 | 7.70k | xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); |
749 | 7.70k | out_page = page; |
750 | | |
751 | 7.70k | if (!page) return NULL; |
752 | | |
753 | 7.70k | if (size <= large_allocation_threshold) |
754 | 7.70k | { |
755 | 7.70k | _root->busy_size = _busy_size; |
756 | | |
757 | | // insert page at the end of linked list |
758 | 7.70k | page->prev = _root; |
759 | 7.70k | _root->next = page; |
760 | 7.70k | _root = page; |
761 | | |
762 | 7.70k | _busy_size = size; |
763 | 7.70k | } |
764 | 0 | else |
765 | 0 | { |
766 | | // insert page before the end of linked list, so that it is deleted as soon as possible |
767 | | // the last page is not deleted even if it's empty (see deallocate_memory) |
768 | 0 | assert(_root->prev); |
769 | | |
770 | 0 | page->prev = _root->prev; |
771 | 0 | page->next = _root; |
772 | |
|
773 | 0 | _root->prev->next = page; |
774 | 0 | _root->prev = page; |
775 | |
|
776 | 0 | page->busy_size = size; |
777 | 0 | } |
778 | | |
779 | 7.70k | return reinterpret_cast<char*>(page) + sizeof(xml_memory_page); |
780 | 7.70k | } |
781 | | PUGI_IMPL_NS_END |
782 | | |
783 | | #ifdef PUGIXML_COMPACT |
784 | | PUGI_IMPL_NS_BEGIN |
785 | | static const uintptr_t compact_alignment_log2 = 2; |
786 | | static const uintptr_t compact_alignment = 1 << compact_alignment_log2; |
787 | | |
788 | | class compact_header |
789 | | { |
790 | | public: |
791 | | compact_header(xml_memory_page* page, unsigned int flags) |
792 | | { |
793 | | PUGI_IMPL_STATIC_ASSERT(xml_memory_block_alignment == compact_alignment); |
794 | | |
795 | | ptrdiff_t offset = (reinterpret_cast<char*>(this) - reinterpret_cast<char*>(page->compact_page_marker)); |
796 | | assert(offset % compact_alignment == 0 && static_cast<uintptr_t>(offset) < 256 * compact_alignment); |
797 | | |
798 | | _page = static_cast<unsigned char>(offset >> compact_alignment_log2); |
799 | | _flags = static_cast<unsigned char>(flags); |
800 | | } |
801 | | |
802 | | void operator&=(uintptr_t mod) |
803 | | { |
804 | | _flags &= static_cast<unsigned char>(mod); |
805 | | } |
806 | | |
807 | | void operator|=(uintptr_t mod) |
808 | | { |
809 | | _flags |= static_cast<unsigned char>(mod); |
810 | | } |
811 | | |
812 | | uintptr_t operator&(uintptr_t mod) const |
813 | | { |
814 | | return _flags & mod; |
815 | | } |
816 | | |
817 | | xml_memory_page* get_page() const |
818 | | { |
819 | | // round-trip through void* to silence 'cast increases required alignment of target type' warnings |
820 | | const char* page_marker = reinterpret_cast<const char*>(this) - (_page << compact_alignment_log2); |
821 | | const char* page = page_marker - *reinterpret_cast<const uint32_t*>(static_cast<const void*>(page_marker)); |
822 | | |
823 | | return const_cast<xml_memory_page*>(reinterpret_cast<const xml_memory_page*>(static_cast<const void*>(page))); |
824 | | } |
825 | | |
826 | | private: |
827 | | unsigned char _page; |
828 | | unsigned char _flags; |
829 | | }; |
830 | | |
831 | | PUGI_IMPL_FN xml_memory_page* compact_get_page(const void* object, int header_offset) |
832 | | { |
833 | | const compact_header* header = reinterpret_cast<const compact_header*>(static_cast<const char*>(object) - header_offset); |
834 | | |
835 | | return header->get_page(); |
836 | | } |
837 | | |
838 | | template <int header_offset, typename T> PUGI_IMPL_FN_NO_INLINE T* compact_get_value(const void* object) |
839 | | { |
840 | | return static_cast<T*>(compact_get_page(object, header_offset)->allocator->_hash->find(object)); |
841 | | } |
842 | | |
843 | | template <int header_offset, typename T> PUGI_IMPL_FN_NO_INLINE void compact_set_value(const void* object, T* value) |
844 | | { |
845 | | compact_get_page(object, header_offset)->allocator->_hash->insert(object, value); |
846 | | } |
847 | | |
848 | | template <typename T, int header_offset, int start = -126> class compact_pointer |
849 | | { |
850 | | public: |
851 | | compact_pointer(): _data(0) |
852 | | { |
853 | | } |
854 | | |
855 | | void operator=(const compact_pointer& rhs) |
856 | | { |
857 | | *this = rhs + 0; |
858 | | } |
859 | | |
860 | | void operator=(T* value) |
861 | | { |
862 | | if (value) |
863 | | { |
864 | | // value is guaranteed to be compact-aligned; 'this' is not |
865 | | // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) |
866 | | // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to |
867 | | // compensate for arithmetic shift rounding for negative values |
868 | | ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this); |
869 | | ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start; |
870 | | |
871 | | if (static_cast<uintptr_t>(offset) <= 253) |
872 | | _data = static_cast<unsigned char>(offset + 1); |
873 | | else |
874 | | { |
875 | | compact_set_value<header_offset>(this, value); |
876 | | |
877 | | _data = 255; |
878 | | } |
879 | | } |
880 | | else |
881 | | _data = 0; |
882 | | } |
883 | | |
884 | | operator T*() const |
885 | | { |
886 | | if (_data) |
887 | | { |
888 | | if (_data < 255) |
889 | | { |
890 | | uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1); |
891 | | |
892 | | return reinterpret_cast<T*>(base + (_data - 1 + start) * compact_alignment); |
893 | | } |
894 | | else |
895 | | return compact_get_value<header_offset, T>(this); |
896 | | } |
897 | | else |
898 | | return NULL; |
899 | | } |
900 | | |
901 | | T* operator->() const |
902 | | { |
903 | | return *this; |
904 | | } |
905 | | |
906 | | private: |
907 | | unsigned char _data; |
908 | | }; |
909 | | |
910 | | template <typename T, int header_offset> class compact_pointer_parent |
911 | | { |
912 | | public: |
913 | | compact_pointer_parent(): _data(0) |
914 | | { |
915 | | } |
916 | | |
917 | | void operator=(const compact_pointer_parent& rhs) |
918 | | { |
919 | | *this = rhs + 0; |
920 | | } |
921 | | |
922 | | void operator=(T* value) |
923 | | { |
924 | | if (value) |
925 | | { |
926 | | // value is guaranteed to be compact-aligned; 'this' is not |
927 | | // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) |
928 | | // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to |
929 | | // compensate for arithmetic shift behavior for negative values |
930 | | ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this); |
931 | | ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533; |
932 | | |
933 | | if (static_cast<uintptr_t>(offset) <= 65533) |
934 | | { |
935 | | _data = static_cast<unsigned short>(offset + 1); |
936 | | } |
937 | | else |
938 | | { |
939 | | xml_memory_page* page = compact_get_page(this, header_offset); |
940 | | |
941 | | if (PUGI_IMPL_UNLIKELY(page->compact_shared_parent == NULL)) |
942 | | page->compact_shared_parent = value; |
943 | | |
944 | | if (page->compact_shared_parent == value) |
945 | | { |
946 | | _data = 65534; |
947 | | } |
948 | | else |
949 | | { |
950 | | compact_set_value<header_offset>(this, value); |
951 | | |
952 | | _data = 65535; |
953 | | } |
954 | | } |
955 | | } |
956 | | else |
957 | | { |
958 | | _data = 0; |
959 | | } |
960 | | } |
961 | | |
962 | | operator T*() const |
963 | | { |
964 | | if (_data) |
965 | | { |
966 | | if (_data < 65534) |
967 | | { |
968 | | uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1); |
969 | | |
970 | | return reinterpret_cast<T*>(base + (_data - 1 - 65533) * compact_alignment); |
971 | | } |
972 | | else if (_data == 65534) |
973 | | return static_cast<T*>(compact_get_page(this, header_offset)->compact_shared_parent); |
974 | | else |
975 | | return compact_get_value<header_offset, T>(this); |
976 | | } |
977 | | else |
978 | | return NULL; |
979 | | } |
980 | | |
981 | | T* operator->() const |
982 | | { |
983 | | return *this; |
984 | | } |
985 | | |
986 | | private: |
987 | | uint16_t _data; |
988 | | }; |
989 | | |
990 | | template <int header_offset, int base_offset> class compact_string |
991 | | { |
992 | | public: |
993 | | compact_string(): _data(0) |
994 | | { |
995 | | } |
996 | | |
997 | | void operator=(const compact_string& rhs) |
998 | | { |
999 | | *this = rhs + 0; |
1000 | | } |
1001 | | |
1002 | | void operator=(char_t* value) |
1003 | | { |
1004 | | if (value) |
1005 | | { |
1006 | | xml_memory_page* page = compact_get_page(this, header_offset); |
1007 | | |
1008 | | if (PUGI_IMPL_UNLIKELY(page->compact_string_base == NULL)) |
1009 | | page->compact_string_base = value; |
1010 | | |
1011 | | ptrdiff_t offset = value - page->compact_string_base; |
1012 | | |
1013 | | if (static_cast<uintptr_t>(offset) < (65535 << 7)) |
1014 | | { |
1015 | | // round-trip through void* to silence 'cast increases required alignment of target type' warnings |
1016 | | uint16_t* base = reinterpret_cast<uint16_t*>(static_cast<void*>(reinterpret_cast<char*>(this) - base_offset)); |
1017 | | |
1018 | | if (*base == 0) |
1019 | | { |
1020 | | *base = static_cast<uint16_t>((offset >> 7) + 1); |
1021 | | _data = static_cast<unsigned char>((offset & 127) + 1); |
1022 | | } |
1023 | | else |
1024 | | { |
1025 | | ptrdiff_t remainder = offset - ((*base - 1) << 7); |
1026 | | |
1027 | | if (static_cast<uintptr_t>(remainder) <= 253) |
1028 | | { |
1029 | | _data = static_cast<unsigned char>(remainder + 1); |
1030 | | } |
1031 | | else |
1032 | | { |
1033 | | compact_set_value<header_offset>(this, value); |
1034 | | |
1035 | | _data = 255; |
1036 | | } |
1037 | | } |
1038 | | } |
1039 | | else |
1040 | | { |
1041 | | compact_set_value<header_offset>(this, value); |
1042 | | |
1043 | | _data = 255; |
1044 | | } |
1045 | | } |
1046 | | else |
1047 | | { |
1048 | | _data = 0; |
1049 | | } |
1050 | | } |
1051 | | |
1052 | | operator char_t*() const |
1053 | | { |
1054 | | if (_data) |
1055 | | { |
1056 | | if (_data < 255) |
1057 | | { |
1058 | | xml_memory_page* page = compact_get_page(this, header_offset); |
1059 | | |
1060 | | // round-trip through void* to silence 'cast increases required alignment of target type' warnings |
1061 | | const uint16_t* base = reinterpret_cast<const uint16_t*>(static_cast<const void*>(reinterpret_cast<const char*>(this) - base_offset)); |
1062 | | assert(*base); |
1063 | | |
1064 | | ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1); |
1065 | | |
1066 | | return page->compact_string_base + offset; |
1067 | | } |
1068 | | else |
1069 | | { |
1070 | | return compact_get_value<header_offset, char_t>(this); |
1071 | | } |
1072 | | } |
1073 | | else |
1074 | | return NULL; |
1075 | | } |
1076 | | |
1077 | | private: |
1078 | | unsigned char _data; |
1079 | | }; |
1080 | | PUGI_IMPL_NS_END |
1081 | | #endif |
1082 | | |
1083 | | #ifdef PUGIXML_COMPACT |
1084 | | namespace pugi |
1085 | | { |
1086 | | struct xml_attribute_struct |
1087 | | { |
1088 | | xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0) |
1089 | | { |
1090 | | PUGI_IMPL_STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); |
1091 | | } |
1092 | | |
1093 | | impl::compact_header header; |
1094 | | |
1095 | | uint16_t namevalue_base; |
1096 | | |
1097 | | impl::compact_string<4, 2> name; |
1098 | | impl::compact_string<5, 3> value; |
1099 | | |
1100 | | impl::compact_pointer<xml_attribute_struct, 6> prev_attribute_c; |
1101 | | impl::compact_pointer<xml_attribute_struct, 7, 0> next_attribute; |
1102 | | }; |
1103 | | |
1104 | | struct xml_node_struct |
1105 | | { |
1106 | | xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0) |
1107 | | { |
1108 | | PUGI_IMPL_STATIC_ASSERT(sizeof(xml_node_struct) == 12); |
1109 | | } |
1110 | | |
1111 | | impl::compact_header header; |
1112 | | |
1113 | | uint16_t namevalue_base; |
1114 | | |
1115 | | impl::compact_string<4, 2> name; |
1116 | | impl::compact_string<5, 3> value; |
1117 | | |
1118 | | impl::compact_pointer_parent<xml_node_struct, 6> parent; |
1119 | | |
1120 | | impl::compact_pointer<xml_node_struct, 8, 0> first_child; |
1121 | | |
1122 | | impl::compact_pointer<xml_node_struct, 9> prev_sibling_c; |
1123 | | impl::compact_pointer<xml_node_struct, 10, 0> next_sibling; |
1124 | | |
1125 | | impl::compact_pointer<xml_attribute_struct, 11, 0> first_attribute; |
1126 | | }; |
1127 | | } |
1128 | | #else |
1129 | | namespace pugi |
1130 | | { |
1131 | | struct xml_attribute_struct |
1132 | | { |
1133 | 102k | xml_attribute_struct(impl::xml_memory_page* page): name(NULL), value(NULL), prev_attribute_c(NULL), next_attribute(NULL) |
1134 | 102k | { |
1135 | 102k | header = PUGI_IMPL_GETHEADER_IMPL(this, page, 0); |
1136 | 102k | } |
1137 | | |
1138 | | uintptr_t header; |
1139 | | |
1140 | | char_t* name; |
1141 | | char_t* value; |
1142 | | |
1143 | | xml_attribute_struct* prev_attribute_c; |
1144 | | xml_attribute_struct* next_attribute; |
1145 | | }; |
1146 | | |
1147 | | struct xml_node_struct |
1148 | | { |
1149 | 780k | xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(NULL), value(NULL), parent(NULL), first_child(NULL), prev_sibling_c(NULL), next_sibling(NULL), first_attribute(NULL) |
1150 | 780k | { |
1151 | 780k | header = PUGI_IMPL_GETHEADER_IMPL(this, page, type); |
1152 | 780k | } |
1153 | | |
1154 | | uintptr_t header; |
1155 | | |
1156 | | char_t* name; |
1157 | | char_t* value; |
1158 | | |
1159 | | xml_node_struct* parent; |
1160 | | |
1161 | | xml_node_struct* first_child; |
1162 | | |
1163 | | xml_node_struct* prev_sibling_c; |
1164 | | xml_node_struct* next_sibling; |
1165 | | |
1166 | | xml_attribute_struct* first_attribute; |
1167 | | }; |
1168 | | } |
1169 | | #endif |
1170 | | |
1171 | | PUGI_IMPL_NS_BEGIN |
1172 | | struct xml_extra_buffer |
1173 | | { |
1174 | | char_t* buffer; |
1175 | | xml_extra_buffer* next; |
1176 | | }; |
1177 | | |
1178 | | struct xml_document_struct: public xml_node_struct, public xml_allocator |
1179 | | { |
1180 | 15.2k | xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(NULL), extra_buffers(NULL) |
1181 | 15.2k | { |
1182 | 15.2k | } |
1183 | | |
1184 | | const char_t* buffer; |
1185 | | |
1186 | | xml_extra_buffer* extra_buffers; |
1187 | | |
1188 | | #ifdef PUGIXML_COMPACT |
1189 | | compact_hash_table hash; |
1190 | | #endif |
1191 | | }; |
1192 | | |
1193 | | template <typename Object> inline xml_allocator& get_allocator(const Object* object) |
1194 | 0 | { |
1195 | 0 | assert(object); |
1196 | | |
1197 | 0 | return *PUGI_IMPL_GETPAGE(object)->allocator; |
1198 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_allocator& pugi::impl::(anonymous namespace)::get_allocator<pugi::xml_attribute_struct>(pugi::xml_attribute_struct const*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_allocator& pugi::impl::(anonymous namespace)::get_allocator<pugi::xml_node_struct>(pugi::xml_node_struct const*) |
1199 | | |
1200 | | template <typename Object> inline xml_document_struct& get_document(const Object* object) |
1201 | 0 | { |
1202 | 0 | assert(object); |
1203 | | |
1204 | 0 | return *static_cast<xml_document_struct*>(PUGI_IMPL_GETPAGE(object)->allocator); |
1205 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_document_struct& pugi::impl::(anonymous namespace)::get_document<pugi::xml_attribute_struct>(pugi::xml_attribute_struct const*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_document_struct& pugi::impl::(anonymous namespace)::get_document<pugi::xml_node_struct>(pugi::xml_node_struct const*) |
1206 | | PUGI_IMPL_NS_END |
1207 | | |
1208 | | // Low-level DOM operations |
1209 | | PUGI_IMPL_NS_BEGIN |
1210 | | inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) |
1211 | 102k | { |
1212 | 102k | xml_memory_page* page; |
1213 | 102k | void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page); |
1214 | 102k | if (!memory) return NULL; |
1215 | | |
1216 | 102k | return new (memory) xml_attribute_struct(page); |
1217 | 102k | } |
1218 | | |
1219 | | inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) |
1220 | 764k | { |
1221 | 764k | xml_memory_page* page; |
1222 | 764k | void* memory = alloc.allocate_object(sizeof(xml_node_struct), page); |
1223 | 764k | if (!memory) return NULL; |
1224 | | |
1225 | 764k | return new (memory) xml_node_struct(page, type); |
1226 | 764k | } |
1227 | | |
1228 | | inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) |
1229 | 0 | { |
1230 | 0 | if (a->header & impl::xml_memory_page_name_allocated_mask) |
1231 | 0 | alloc.deallocate_string(a->name); |
1232 | |
|
1233 | 0 | if (a->header & impl::xml_memory_page_value_allocated_mask) |
1234 | 0 | alloc.deallocate_string(a->value); |
1235 | |
|
1236 | 0 | alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI_IMPL_GETPAGE(a)); |
1237 | 0 | } |
1238 | | |
1239 | | inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) |
1240 | 0 | { |
1241 | 0 | if (n->header & impl::xml_memory_page_name_allocated_mask) |
1242 | 0 | alloc.deallocate_string(n->name); |
1243 | |
|
1244 | 0 | if (n->header & impl::xml_memory_page_value_allocated_mask) |
1245 | 0 | alloc.deallocate_string(n->value); |
1246 | |
|
1247 | 0 | for (xml_attribute_struct* attr = n->first_attribute; attr; ) |
1248 | 0 | { |
1249 | 0 | xml_attribute_struct* next = attr->next_attribute; |
1250 | |
|
1251 | 0 | destroy_attribute(attr, alloc); |
1252 | |
|
1253 | 0 | attr = next; |
1254 | 0 | } |
1255 | |
|
1256 | 0 | for (xml_node_struct* child = n->first_child; child; ) |
1257 | 0 | { |
1258 | 0 | xml_node_struct* next = child->next_sibling; |
1259 | |
|
1260 | 0 | destroy_node(child, alloc); |
1261 | |
|
1262 | 0 | child = next; |
1263 | 0 | } |
1264 | |
|
1265 | 0 | alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI_IMPL_GETPAGE(n)); |
1266 | 0 | } |
1267 | | |
1268 | | inline void append_node(xml_node_struct* child, xml_node_struct* node) |
1269 | 764k | { |
1270 | 764k | child->parent = node; |
1271 | | |
1272 | 764k | xml_node_struct* head = node->first_child; |
1273 | | |
1274 | 764k | if (head) |
1275 | 311k | { |
1276 | 311k | xml_node_struct* tail = head->prev_sibling_c; |
1277 | | |
1278 | 311k | tail->next_sibling = child; |
1279 | 311k | child->prev_sibling_c = tail; |
1280 | 311k | head->prev_sibling_c = child; |
1281 | 311k | } |
1282 | 453k | else |
1283 | 453k | { |
1284 | 453k | node->first_child = child; |
1285 | 453k | child->prev_sibling_c = child; |
1286 | 453k | } |
1287 | 764k | } |
1288 | | |
1289 | | inline void prepend_node(xml_node_struct* child, xml_node_struct* node) |
1290 | 0 | { |
1291 | 0 | child->parent = node; |
1292 | |
|
1293 | 0 | xml_node_struct* head = node->first_child; |
1294 | |
|
1295 | 0 | if (head) |
1296 | 0 | { |
1297 | 0 | child->prev_sibling_c = head->prev_sibling_c; |
1298 | 0 | head->prev_sibling_c = child; |
1299 | 0 | } |
1300 | 0 | else |
1301 | 0 | child->prev_sibling_c = child; |
1302 | |
|
1303 | 0 | child->next_sibling = head; |
1304 | 0 | node->first_child = child; |
1305 | 0 | } |
1306 | | |
1307 | | inline void insert_node_after(xml_node_struct* child, xml_node_struct* node) |
1308 | 0 | { |
1309 | 0 | xml_node_struct* parent = node->parent; |
1310 | |
|
1311 | 0 | child->parent = parent; |
1312 | |
|
1313 | 0 | xml_node_struct* next = node->next_sibling; |
1314 | |
|
1315 | 0 | if (next) |
1316 | 0 | next->prev_sibling_c = child; |
1317 | 0 | else |
1318 | 0 | parent->first_child->prev_sibling_c = child; |
1319 | |
|
1320 | 0 | child->next_sibling = next; |
1321 | 0 | child->prev_sibling_c = node; |
1322 | |
|
1323 | 0 | node->next_sibling = child; |
1324 | 0 | } |
1325 | | |
1326 | | inline void insert_node_before(xml_node_struct* child, xml_node_struct* node) |
1327 | 0 | { |
1328 | 0 | xml_node_struct* parent = node->parent; |
1329 | |
|
1330 | 0 | child->parent = parent; |
1331 | |
|
1332 | 0 | xml_node_struct* prev = node->prev_sibling_c; |
1333 | |
|
1334 | 0 | if (prev->next_sibling) |
1335 | 0 | prev->next_sibling = child; |
1336 | 0 | else |
1337 | 0 | parent->first_child = child; |
1338 | |
|
1339 | 0 | child->prev_sibling_c = prev; |
1340 | 0 | child->next_sibling = node; |
1341 | |
|
1342 | 0 | node->prev_sibling_c = child; |
1343 | 0 | } |
1344 | | |
1345 | | inline void remove_node(xml_node_struct* node) |
1346 | 0 | { |
1347 | 0 | xml_node_struct* parent = node->parent; |
1348 | |
|
1349 | 0 | xml_node_struct* next = node->next_sibling; |
1350 | 0 | xml_node_struct* prev = node->prev_sibling_c; |
1351 | |
|
1352 | 0 | if (next) |
1353 | 0 | next->prev_sibling_c = prev; |
1354 | 0 | else |
1355 | 0 | parent->first_child->prev_sibling_c = prev; |
1356 | |
|
1357 | 0 | if (prev->next_sibling) |
1358 | 0 | prev->next_sibling = next; |
1359 | 0 | else |
1360 | 0 | parent->first_child = next; |
1361 | |
|
1362 | 0 | node->parent = NULL; |
1363 | 0 | node->prev_sibling_c = NULL; |
1364 | 0 | node->next_sibling = NULL; |
1365 | 0 | } |
1366 | | |
1367 | | inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node) |
1368 | 102k | { |
1369 | 102k | xml_attribute_struct* head = node->first_attribute; |
1370 | | |
1371 | 102k | if (head) |
1372 | 2.95k | { |
1373 | 2.95k | xml_attribute_struct* tail = head->prev_attribute_c; |
1374 | | |
1375 | 2.95k | tail->next_attribute = attr; |
1376 | 2.95k | attr->prev_attribute_c = tail; |
1377 | 2.95k | head->prev_attribute_c = attr; |
1378 | 2.95k | } |
1379 | 99.9k | else |
1380 | 99.9k | { |
1381 | 99.9k | node->first_attribute = attr; |
1382 | 99.9k | attr->prev_attribute_c = attr; |
1383 | 99.9k | } |
1384 | 102k | } |
1385 | | |
1386 | | inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node) |
1387 | 0 | { |
1388 | 0 | xml_attribute_struct* head = node->first_attribute; |
1389 | |
|
1390 | 0 | if (head) |
1391 | 0 | { |
1392 | 0 | attr->prev_attribute_c = head->prev_attribute_c; |
1393 | 0 | head->prev_attribute_c = attr; |
1394 | 0 | } |
1395 | 0 | else |
1396 | 0 | attr->prev_attribute_c = attr; |
1397 | |
|
1398 | 0 | attr->next_attribute = head; |
1399 | 0 | node->first_attribute = attr; |
1400 | 0 | } |
1401 | | |
1402 | | inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) |
1403 | 0 | { |
1404 | 0 | xml_attribute_struct* next = place->next_attribute; |
1405 | |
|
1406 | 0 | if (next) |
1407 | 0 | next->prev_attribute_c = attr; |
1408 | 0 | else |
1409 | 0 | node->first_attribute->prev_attribute_c = attr; |
1410 | |
|
1411 | 0 | attr->next_attribute = next; |
1412 | 0 | attr->prev_attribute_c = place; |
1413 | 0 | place->next_attribute = attr; |
1414 | 0 | } |
1415 | | |
1416 | | inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) |
1417 | 0 | { |
1418 | 0 | xml_attribute_struct* prev = place->prev_attribute_c; |
1419 | |
|
1420 | 0 | if (prev->next_attribute) |
1421 | 0 | prev->next_attribute = attr; |
1422 | 0 | else |
1423 | 0 | node->first_attribute = attr; |
1424 | |
|
1425 | 0 | attr->prev_attribute_c = prev; |
1426 | 0 | attr->next_attribute = place; |
1427 | 0 | place->prev_attribute_c = attr; |
1428 | 0 | } |
1429 | | |
1430 | | inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) |
1431 | 0 | { |
1432 | 0 | xml_attribute_struct* next = attr->next_attribute; |
1433 | 0 | xml_attribute_struct* prev = attr->prev_attribute_c; |
1434 | |
|
1435 | 0 | if (next) |
1436 | 0 | next->prev_attribute_c = prev; |
1437 | 0 | else |
1438 | 0 | node->first_attribute->prev_attribute_c = prev; |
1439 | |
|
1440 | 0 | if (prev->next_attribute) |
1441 | 0 | prev->next_attribute = next; |
1442 | 0 | else |
1443 | 0 | node->first_attribute = next; |
1444 | |
|
1445 | 0 | attr->prev_attribute_c = NULL; |
1446 | 0 | attr->next_attribute = NULL; |
1447 | 0 | } |
1448 | | |
1449 | | PUGI_IMPL_FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) |
1450 | 764k | { |
1451 | 764k | if (!alloc.reserve()) return NULL; |
1452 | | |
1453 | 764k | xml_node_struct* child = allocate_node(alloc, type); |
1454 | 764k | if (!child) return NULL; |
1455 | | |
1456 | 764k | append_node(child, node); |
1457 | | |
1458 | 764k | return child; |
1459 | 764k | } |
1460 | | |
1461 | | PUGI_IMPL_FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) |
1462 | 102k | { |
1463 | 102k | if (!alloc.reserve()) return NULL; |
1464 | | |
1465 | 102k | xml_attribute_struct* attr = allocate_attribute(alloc); |
1466 | 102k | if (!attr) return NULL; |
1467 | | |
1468 | 102k | append_attribute(attr, node); |
1469 | | |
1470 | 102k | return attr; |
1471 | 102k | } |
1472 | | PUGI_IMPL_NS_END |
1473 | | |
1474 | | // Helper classes for code generation |
1475 | | PUGI_IMPL_NS_BEGIN |
1476 | | struct opt_false |
1477 | | { |
1478 | | enum { value = 0 }; |
1479 | | }; |
1480 | | |
1481 | | struct opt_true |
1482 | | { |
1483 | | enum { value = 1 }; |
1484 | | }; |
1485 | | PUGI_IMPL_NS_END |
1486 | | |
1487 | | // Unicode utilities |
1488 | | PUGI_IMPL_NS_BEGIN |
1489 | | inline uint16_t endian_swap(uint16_t value) |
1490 | 7.38M | { |
1491 | 7.38M | return static_cast<uint16_t>(((value & 0xff) << 8) | (value >> 8)); |
1492 | 7.38M | } |
1493 | | |
1494 | | inline uint32_t endian_swap(uint32_t value) |
1495 | 3.25M | { |
1496 | 3.25M | return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); |
1497 | 3.25M | } |
1498 | | |
1499 | | struct utf8_counter |
1500 | | { |
1501 | | typedef size_t value_type; |
1502 | | |
1503 | | static value_type low(value_type result, uint32_t ch) |
1504 | 48.5M | { |
1505 | | // U+0000..U+007F |
1506 | 48.5M | if (ch < 0x80) return result + 1; |
1507 | | // U+0080..U+07FF |
1508 | 14.9M | else if (ch < 0x800) return result + 2; |
1509 | | // U+0800..U+FFFF |
1510 | 9.65M | else return result + 3; |
1511 | 48.5M | } |
1512 | | |
1513 | | static value_type high(value_type result, uint32_t) |
1514 | 3.27M | { |
1515 | | // U+10000..U+10FFFF |
1516 | 3.27M | return result + 4; |
1517 | 3.27M | } |
1518 | | }; |
1519 | | |
1520 | | struct utf8_writer |
1521 | | { |
1522 | | typedef uint8_t* value_type; |
1523 | | |
1524 | | static value_type low(value_type result, uint32_t ch) |
1525 | 48.5M | { |
1526 | | // U+0000..U+007F |
1527 | 48.5M | if (ch < 0x80) |
1528 | 33.6M | { |
1529 | 33.6M | *result = static_cast<uint8_t>(ch); |
1530 | 33.6M | return result + 1; |
1531 | 33.6M | } |
1532 | | // U+0080..U+07FF |
1533 | 14.9M | else if (ch < 0x800) |
1534 | 5.26M | { |
1535 | 5.26M | result[0] = static_cast<uint8_t>(0xC0 | (ch >> 6)); |
1536 | 5.26M | result[1] = static_cast<uint8_t>(0x80 | (ch & 0x3F)); |
1537 | 5.26M | return result + 2; |
1538 | 5.26M | } |
1539 | | // U+0800..U+FFFF |
1540 | 9.65M | else |
1541 | 9.65M | { |
1542 | 9.65M | result[0] = static_cast<uint8_t>(0xE0 | (ch >> 12)); |
1543 | 9.65M | result[1] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F)); |
1544 | 9.65M | result[2] = static_cast<uint8_t>(0x80 | (ch & 0x3F)); |
1545 | 9.65M | return result + 3; |
1546 | 9.65M | } |
1547 | 48.5M | } |
1548 | | |
1549 | | static value_type high(value_type result, uint32_t ch) |
1550 | 3.27M | { |
1551 | | // U+10000..U+10FFFF |
1552 | 3.27M | result[0] = static_cast<uint8_t>(0xF0 | (ch >> 18)); |
1553 | 3.27M | result[1] = static_cast<uint8_t>(0x80 | ((ch >> 12) & 0x3F)); |
1554 | 3.27M | result[2] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F)); |
1555 | 3.27M | result[3] = static_cast<uint8_t>(0x80 | (ch & 0x3F)); |
1556 | 3.27M | return result + 4; |
1557 | 3.27M | } |
1558 | | |
1559 | | static value_type any(value_type result, uint32_t ch) |
1560 | 3.25k | { |
1561 | 3.25k | return (ch < 0x10000) ? low(result, ch) : high(result, ch); |
1562 | 3.25k | } |
1563 | | }; |
1564 | | |
1565 | | struct utf16_counter |
1566 | | { |
1567 | | typedef size_t value_type; |
1568 | | |
1569 | | static value_type low(value_type result, uint32_t) |
1570 | 0 | { |
1571 | 0 | return result + 1; |
1572 | 0 | } |
1573 | | |
1574 | | static value_type high(value_type result, uint32_t) |
1575 | 0 | { |
1576 | 0 | return result + 2; |
1577 | 0 | } |
1578 | | }; |
1579 | | |
1580 | | struct utf16_writer |
1581 | | { |
1582 | | typedef uint16_t* value_type; |
1583 | | |
1584 | | static value_type low(value_type result, uint32_t ch) |
1585 | 0 | { |
1586 | 0 | *result = static_cast<uint16_t>(ch); |
1587 | |
|
1588 | 0 | return result + 1; |
1589 | 0 | } |
1590 | | |
1591 | | static value_type high(value_type result, uint32_t ch) |
1592 | 0 | { |
1593 | 0 | uint32_t msh = (ch - 0x10000U) >> 10; |
1594 | 0 | uint32_t lsh = (ch - 0x10000U) & 0x3ff; |
1595 | |
|
1596 | 0 | result[0] = static_cast<uint16_t>(0xD800 + msh); |
1597 | 0 | result[1] = static_cast<uint16_t>(0xDC00 + lsh); |
1598 | |
|
1599 | 0 | return result + 2; |
1600 | 0 | } |
1601 | | |
1602 | | static value_type any(value_type result, uint32_t ch) |
1603 | 0 | { |
1604 | 0 | return (ch < 0x10000) ? low(result, ch) : high(result, ch); |
1605 | 0 | } |
1606 | | }; |
1607 | | |
1608 | | struct utf32_counter |
1609 | | { |
1610 | | typedef size_t value_type; |
1611 | | |
1612 | | static value_type low(value_type result, uint32_t) |
1613 | 0 | { |
1614 | 0 | return result + 1; |
1615 | 0 | } |
1616 | | |
1617 | | static value_type high(value_type result, uint32_t) |
1618 | 0 | { |
1619 | 0 | return result + 1; |
1620 | 0 | } |
1621 | | }; |
1622 | | |
1623 | | struct utf32_writer |
1624 | | { |
1625 | | typedef uint32_t* value_type; |
1626 | | |
1627 | | static value_type low(value_type result, uint32_t ch) |
1628 | 0 | { |
1629 | 0 | *result = ch; |
1630 | |
|
1631 | 0 | return result + 1; |
1632 | 0 | } |
1633 | | |
1634 | | static value_type high(value_type result, uint32_t ch) |
1635 | 0 | { |
1636 | 0 | *result = ch; |
1637 | |
|
1638 | 0 | return result + 1; |
1639 | 0 | } |
1640 | | |
1641 | | static value_type any(value_type result, uint32_t ch) |
1642 | 0 | { |
1643 | 0 | *result = ch; |
1644 | 0 |
|
1645 | 0 | return result + 1; |
1646 | 0 | } |
1647 | | }; |
1648 | | |
1649 | | struct latin1_writer |
1650 | | { |
1651 | | typedef uint8_t* value_type; |
1652 | | |
1653 | | static value_type low(value_type result, uint32_t ch) |
1654 | 0 | { |
1655 | 0 | *result = static_cast<uint8_t>(ch > 255 ? '?' : ch); |
1656 | |
|
1657 | 0 | return result + 1; |
1658 | 0 | } |
1659 | | |
1660 | | static value_type high(value_type result, uint32_t ch) |
1661 | 0 | { |
1662 | 0 | (void)ch; |
1663 | |
|
1664 | 0 | *result = '?'; |
1665 | |
|
1666 | 0 | return result + 1; |
1667 | 0 | } |
1668 | | }; |
1669 | | |
1670 | | struct utf8_decoder |
1671 | | { |
1672 | | typedef uint8_t type; |
1673 | | |
1674 | | template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) |
1675 | 0 | { |
1676 | 0 | const uint8_t utf8_byte_mask = 0x3f; |
1677 | |
|
1678 | 0 | while (size) |
1679 | 0 | { |
1680 | 0 | uint8_t lead = *data; |
1681 | | |
1682 | | // 0xxxxxxx -> U+0000..U+007F |
1683 | 0 | if (lead < 0x80) |
1684 | 0 | { |
1685 | 0 | result = Traits::low(result, lead); |
1686 | 0 | data += 1; |
1687 | 0 | size -= 1; |
1688 | | |
1689 | | // process aligned single-byte (ascii) blocks |
1690 | 0 | if ((reinterpret_cast<uintptr_t>(data) & 3) == 0) |
1691 | 0 | { |
1692 | | // round-trip through void* to silence 'cast increases required alignment of target type' warnings |
1693 | 0 | while (size >= 4 && (*static_cast<const uint32_t*>(static_cast<const void*>(data)) & 0x80808080) == 0) |
1694 | 0 | { |
1695 | 0 | result = Traits::low(result, data[0]); |
1696 | 0 | result = Traits::low(result, data[1]); |
1697 | 0 | result = Traits::low(result, data[2]); |
1698 | 0 | result = Traits::low(result, data[3]); |
1699 | 0 | data += 4; |
1700 | 0 | size -= 4; |
1701 | 0 | } |
1702 | 0 | } |
1703 | 0 | } |
1704 | | // 110xxxxx -> U+0080..U+07FF |
1705 | 0 | else if (static_cast<unsigned int>(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) |
1706 | 0 | { |
1707 | 0 | result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); |
1708 | 0 | data += 2; |
1709 | 0 | size -= 2; |
1710 | 0 | } |
1711 | | // 1110xxxx -> U+0800-U+FFFF |
1712 | 0 | else if (static_cast<unsigned int>(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) |
1713 | 0 | { |
1714 | 0 | result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); |
1715 | 0 | data += 3; |
1716 | 0 | size -= 3; |
1717 | 0 | } |
1718 | | // 11110xxx -> U+10000..U+10FFFF |
1719 | 0 | else if (static_cast<unsigned int>(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) |
1720 | 0 | { |
1721 | 0 | result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); |
1722 | 0 | data += 4; |
1723 | 0 | size -= 4; |
1724 | 0 | } |
1725 | | // 10xxxxxx or 11111xxx -> invalid |
1726 | 0 | else |
1727 | 0 | { |
1728 | 0 | data += 1; |
1729 | 0 | size -= 1; |
1730 | 0 | } |
1731 | 0 | } |
1732 | |
|
1733 | 0 | return result; |
1734 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::utf16_writer::value_type pugi::impl::(anonymous namespace)::utf8_decoder::process<pugi::impl::(anonymous namespace)::utf16_writer>(unsigned char const*, unsigned long, pugi::impl::(anonymous namespace)::utf16_writer::value_type, pugi::impl::(anonymous namespace)::utf16_writer) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::utf32_writer::value_type pugi::impl::(anonymous namespace)::utf8_decoder::process<pugi::impl::(anonymous namespace)::utf32_writer>(unsigned char const*, unsigned long, pugi::impl::(anonymous namespace)::utf32_writer::value_type, pugi::impl::(anonymous namespace)::utf32_writer) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::latin1_writer::value_type pugi::impl::(anonymous namespace)::utf8_decoder::process<pugi::impl::(anonymous namespace)::latin1_writer>(unsigned char const*, unsigned long, pugi::impl::(anonymous namespace)::latin1_writer::value_type, pugi::impl::(anonymous namespace)::latin1_writer) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::utf32_counter::value_type pugi::impl::(anonymous namespace)::utf8_decoder::process<pugi::impl::(anonymous namespace)::utf32_counter>(unsigned char const*, unsigned long, pugi::impl::(anonymous namespace)::utf32_counter::value_type, pugi::impl::(anonymous namespace)::utf32_counter) |
1735 | | }; |
1736 | | |
1737 | | template <typename opt_swap> struct utf16_decoder |
1738 | | { |
1739 | | typedef uint16_t type; |
1740 | | |
1741 | | template <typename Traits> static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits) |
1742 | 2.08k | { |
1743 | 19.4M | while (size) |
1744 | 19.4M | { |
1745 | 19.4M | uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; |
1746 | | |
1747 | | // U+0000..U+D7FF |
1748 | 19.4M | if (lead < 0xD800) |
1749 | 15.0M | { |
1750 | 15.0M | result = Traits::low(result, lead); |
1751 | 15.0M | data += 1; |
1752 | 15.0M | size -= 1; |
1753 | 15.0M | } |
1754 | | // U+E000..U+FFFF |
1755 | 4.44M | else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000) |
1756 | 4.39M | { |
1757 | 4.39M | result = Traits::low(result, lead); |
1758 | 4.39M | data += 1; |
1759 | 4.39M | size -= 1; |
1760 | 4.39M | } |
1761 | | // surrogate pair lead |
1762 | 51.9k | else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2) |
1763 | 35.2k | { |
1764 | 35.2k | uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; |
1765 | | |
1766 | 35.2k | if (static_cast<unsigned int>(next - 0xDC00) < 0x400) |
1767 | 5.10k | { |
1768 | 5.10k | result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); |
1769 | 5.10k | data += 2; |
1770 | 5.10k | size -= 2; |
1771 | 5.10k | } |
1772 | 30.1k | else |
1773 | 30.1k | { |
1774 | 30.1k | data += 1; |
1775 | 30.1k | size -= 1; |
1776 | 30.1k | } |
1777 | 35.2k | } |
1778 | 16.6k | else |
1779 | 16.6k | { |
1780 | 16.6k | data += 1; |
1781 | 16.6k | size -= 1; |
1782 | 16.6k | } |
1783 | 19.4M | } |
1784 | | |
1785 | 2.08k | return result; |
1786 | 2.08k | } pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_counter::value_type pugi::impl::(anonymous namespace)::utf16_decoder<pugi::impl::(anonymous namespace)::opt_false>::process<pugi::impl::(anonymous namespace)::utf8_counter>(unsigned short const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_counter::value_type, pugi::impl::(anonymous namespace)::utf8_counter) Line | Count | Source | 1742 | 570 | { | 1743 | 6.05M | while (size) | 1744 | 6.05M | { | 1745 | 6.05M | uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; | 1746 | | | 1747 | | // U+0000..U+D7FF | 1748 | 6.05M | if (lead < 0xD800) | 1749 | 5.90M | { | 1750 | 5.90M | result = Traits::low(result, lead); | 1751 | 5.90M | data += 1; | 1752 | 5.90M | size -= 1; | 1753 | 5.90M | } | 1754 | | // U+E000..U+FFFF | 1755 | 153k | else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000) | 1756 | 147k | { | 1757 | 147k | result = Traits::low(result, lead); | 1758 | 147k | data += 1; | 1759 | 147k | size -= 1; | 1760 | 147k | } | 1761 | | // surrogate pair lead | 1762 | 6.64k | else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2) | 1763 | 5.25k | { | 1764 | 5.25k | uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; | 1765 | | | 1766 | 5.25k | if (static_cast<unsigned int>(next - 0xDC00) < 0x400) | 1767 | 1.25k | { | 1768 | 1.25k | result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); | 1769 | 1.25k | data += 2; | 1770 | 1.25k | size -= 2; | 1771 | 1.25k | } | 1772 | 3.99k | else | 1773 | 3.99k | { | 1774 | 3.99k | data += 1; | 1775 | 3.99k | size -= 1; | 1776 | 3.99k | } | 1777 | 5.25k | } | 1778 | 1.39k | else | 1779 | 1.39k | { | 1780 | 1.39k | data += 1; | 1781 | 1.39k | size -= 1; | 1782 | 1.39k | } | 1783 | 6.05M | } | 1784 | | | 1785 | 570 | return result; | 1786 | 570 | } |
pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_writer::value_type pugi::impl::(anonymous namespace)::utf16_decoder<pugi::impl::(anonymous namespace)::opt_false>::process<pugi::impl::(anonymous namespace)::utf8_writer>(unsigned short const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_writer::value_type, pugi::impl::(anonymous namespace)::utf8_writer) Line | Count | Source | 1742 | 570 | { | 1743 | 6.05M | while (size) | 1744 | 6.05M | { | 1745 | 6.05M | uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; | 1746 | | | 1747 | | // U+0000..U+D7FF | 1748 | 6.05M | if (lead < 0xD800) | 1749 | 5.90M | { | 1750 | 5.90M | result = Traits::low(result, lead); | 1751 | 5.90M | data += 1; | 1752 | 5.90M | size -= 1; | 1753 | 5.90M | } | 1754 | | // U+E000..U+FFFF | 1755 | 153k | else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000) | 1756 | 147k | { | 1757 | 147k | result = Traits::low(result, lead); | 1758 | 147k | data += 1; | 1759 | 147k | size -= 1; | 1760 | 147k | } | 1761 | | // surrogate pair lead | 1762 | 6.64k | else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2) | 1763 | 5.25k | { | 1764 | 5.25k | uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; | 1765 | | | 1766 | 5.25k | if (static_cast<unsigned int>(next - 0xDC00) < 0x400) | 1767 | 1.25k | { | 1768 | 1.25k | result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); | 1769 | 1.25k | data += 2; | 1770 | 1.25k | size -= 2; | 1771 | 1.25k | } | 1772 | 3.99k | else | 1773 | 3.99k | { | 1774 | 3.99k | data += 1; | 1775 | 3.99k | size -= 1; | 1776 | 3.99k | } | 1777 | 5.25k | } | 1778 | 1.39k | else | 1779 | 1.39k | { | 1780 | 1.39k | data += 1; | 1781 | 1.39k | size -= 1; | 1782 | 1.39k | } | 1783 | 6.05M | } | 1784 | | | 1785 | 570 | return result; | 1786 | 570 | } |
pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_counter::value_type pugi::impl::(anonymous namespace)::utf16_decoder<pugi::impl::(anonymous namespace)::opt_true>::process<pugi::impl::(anonymous namespace)::utf8_counter>(unsigned short const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_counter::value_type, pugi::impl::(anonymous namespace)::utf8_counter) Line | Count | Source | 1742 | 471 | { | 1743 | 3.68M | while (size) | 1744 | 3.68M | { | 1745 | 3.68M | uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; | 1746 | | | 1747 | | // U+0000..U+D7FF | 1748 | 3.68M | if (lead < 0xD800) | 1749 | 1.61M | { | 1750 | 1.61M | result = Traits::low(result, lead); | 1751 | 1.61M | data += 1; | 1752 | 1.61M | size -= 1; | 1753 | 1.61M | } | 1754 | | // U+E000..U+FFFF | 1755 | 2.06M | else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000) | 1756 | 2.04M | { | 1757 | 2.04M | result = Traits::low(result, lead); | 1758 | 2.04M | data += 1; | 1759 | 2.04M | size -= 1; | 1760 | 2.04M | } | 1761 | | // surrogate pair lead | 1762 | 19.3k | else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2) | 1763 | 12.3k | { | 1764 | 12.3k | uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; | 1765 | | | 1766 | 12.3k | if (static_cast<unsigned int>(next - 0xDC00) < 0x400) | 1767 | 1.29k | { | 1768 | 1.29k | result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); | 1769 | 1.29k | data += 2; | 1770 | 1.29k | size -= 2; | 1771 | 1.29k | } | 1772 | 11.0k | else | 1773 | 11.0k | { | 1774 | 11.0k | data += 1; | 1775 | 11.0k | size -= 1; | 1776 | 11.0k | } | 1777 | 12.3k | } | 1778 | 6.94k | else | 1779 | 6.94k | { | 1780 | 6.94k | data += 1; | 1781 | 6.94k | size -= 1; | 1782 | 6.94k | } | 1783 | 3.68M | } | 1784 | | | 1785 | 471 | return result; | 1786 | 471 | } |
pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_writer::value_type pugi::impl::(anonymous namespace)::utf16_decoder<pugi::impl::(anonymous namespace)::opt_true>::process<pugi::impl::(anonymous namespace)::utf8_writer>(unsigned short const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_writer::value_type, pugi::impl::(anonymous namespace)::utf8_writer) Line | Count | Source | 1742 | 471 | { | 1743 | 3.68M | while (size) | 1744 | 3.68M | { | 1745 | 3.68M | uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; | 1746 | | | 1747 | | // U+0000..U+D7FF | 1748 | 3.68M | if (lead < 0xD800) | 1749 | 1.61M | { | 1750 | 1.61M | result = Traits::low(result, lead); | 1751 | 1.61M | data += 1; | 1752 | 1.61M | size -= 1; | 1753 | 1.61M | } | 1754 | | // U+E000..U+FFFF | 1755 | 2.06M | else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000) | 1756 | 2.04M | { | 1757 | 2.04M | result = Traits::low(result, lead); | 1758 | 2.04M | data += 1; | 1759 | 2.04M | size -= 1; | 1760 | 2.04M | } | 1761 | | // surrogate pair lead | 1762 | 19.3k | else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2) | 1763 | 12.3k | { | 1764 | 12.3k | uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; | 1765 | | | 1766 | 12.3k | if (static_cast<unsigned int>(next - 0xDC00) < 0x400) | 1767 | 1.29k | { | 1768 | 1.29k | result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); | 1769 | 1.29k | data += 2; | 1770 | 1.29k | size -= 2; | 1771 | 1.29k | } | 1772 | 11.0k | else | 1773 | 11.0k | { | 1774 | 11.0k | data += 1; | 1775 | 11.0k | size -= 1; | 1776 | 11.0k | } | 1777 | 12.3k | } | 1778 | 6.94k | else | 1779 | 6.94k | { | 1780 | 6.94k | data += 1; | 1781 | 6.94k | size -= 1; | 1782 | 6.94k | } | 1783 | 3.68M | } | 1784 | | | 1785 | 471 | return result; | 1786 | 471 | } |
|
1787 | | }; |
1788 | | |
1789 | | template <typename opt_swap> struct utf32_decoder |
1790 | | { |
1791 | | typedef uint32_t type; |
1792 | | |
1793 | | template <typename Traits> static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits) |
1794 | 1.38k | { |
1795 | 6.60M | while (size) |
1796 | 6.60M | { |
1797 | 6.60M | uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; |
1798 | | |
1799 | | // U+0000..U+FFFF |
1800 | 6.60M | if (lead < 0x10000) |
1801 | 56.8k | { |
1802 | 56.8k | result = Traits::low(result, lead); |
1803 | 56.8k | data += 1; |
1804 | 56.8k | size -= 1; |
1805 | 56.8k | } |
1806 | | // U+10000..U+10FFFF |
1807 | 6.54M | else |
1808 | 6.54M | { |
1809 | 6.54M | result = Traits::high(result, lead); |
1810 | 6.54M | data += 1; |
1811 | 6.54M | size -= 1; |
1812 | 6.54M | } |
1813 | 6.60M | } |
1814 | | |
1815 | 1.38k | return result; |
1816 | 1.38k | } pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_counter::value_type pugi::impl::(anonymous namespace)::utf32_decoder<pugi::impl::(anonymous namespace)::opt_false>::process<pugi::impl::(anonymous namespace)::utf8_counter>(unsigned int const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_counter::value_type, pugi::impl::(anonymous namespace)::utf8_counter) Line | Count | Source | 1794 | 351 | { | 1795 | 1.67M | while (size) | 1796 | 1.67M | { | 1797 | 1.67M | uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; | 1798 | | | 1799 | | // U+0000..U+FFFF | 1800 | 1.67M | if (lead < 0x10000) | 1801 | 19.5k | { | 1802 | 19.5k | result = Traits::low(result, lead); | 1803 | 19.5k | data += 1; | 1804 | 19.5k | size -= 1; | 1805 | 19.5k | } | 1806 | | // U+10000..U+10FFFF | 1807 | 1.65M | else | 1808 | 1.65M | { | 1809 | 1.65M | result = Traits::high(result, lead); | 1810 | 1.65M | data += 1; | 1811 | 1.65M | size -= 1; | 1812 | 1.65M | } | 1813 | 1.67M | } | 1814 | | | 1815 | 351 | return result; | 1816 | 351 | } |
pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_writer::value_type pugi::impl::(anonymous namespace)::utf32_decoder<pugi::impl::(anonymous namespace)::opt_false>::process<pugi::impl::(anonymous namespace)::utf8_writer>(unsigned int const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_writer::value_type, pugi::impl::(anonymous namespace)::utf8_writer) Line | Count | Source | 1794 | 351 | { | 1795 | 1.67M | while (size) | 1796 | 1.67M | { | 1797 | 1.67M | uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; | 1798 | | | 1799 | | // U+0000..U+FFFF | 1800 | 1.67M | if (lead < 0x10000) | 1801 | 19.5k | { | 1802 | 19.5k | result = Traits::low(result, lead); | 1803 | 19.5k | data += 1; | 1804 | 19.5k | size -= 1; | 1805 | 19.5k | } | 1806 | | // U+10000..U+10FFFF | 1807 | 1.65M | else | 1808 | 1.65M | { | 1809 | 1.65M | result = Traits::high(result, lead); | 1810 | 1.65M | data += 1; | 1811 | 1.65M | size -= 1; | 1812 | 1.65M | } | 1813 | 1.67M | } | 1814 | | | 1815 | 351 | return result; | 1816 | 351 | } |
pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_counter::value_type pugi::impl::(anonymous namespace)::utf32_decoder<pugi::impl::(anonymous namespace)::opt_true>::process<pugi::impl::(anonymous namespace)::utf8_counter>(unsigned int const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_counter::value_type, pugi::impl::(anonymous namespace)::utf8_counter) Line | Count | Source | 1794 | 342 | { | 1795 | 1.62M | while (size) | 1796 | 1.62M | { | 1797 | 1.62M | uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; | 1798 | | | 1799 | | // U+0000..U+FFFF | 1800 | 1.62M | if (lead < 0x10000) | 1801 | 8.83k | { | 1802 | 8.83k | result = Traits::low(result, lead); | 1803 | 8.83k | data += 1; | 1804 | 8.83k | size -= 1; | 1805 | 8.83k | } | 1806 | | // U+10000..U+10FFFF | 1807 | 1.61M | else | 1808 | 1.61M | { | 1809 | 1.61M | result = Traits::high(result, lead); | 1810 | 1.61M | data += 1; | 1811 | 1.61M | size -= 1; | 1812 | 1.61M | } | 1813 | 1.62M | } | 1814 | | | 1815 | 342 | return result; | 1816 | 342 | } |
pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_writer::value_type pugi::impl::(anonymous namespace)::utf32_decoder<pugi::impl::(anonymous namespace)::opt_true>::process<pugi::impl::(anonymous namespace)::utf8_writer>(unsigned int const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_writer::value_type, pugi::impl::(anonymous namespace)::utf8_writer) Line | Count | Source | 1794 | 342 | { | 1795 | 1.62M | while (size) | 1796 | 1.62M | { | 1797 | 1.62M | uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; | 1798 | | | 1799 | | // U+0000..U+FFFF | 1800 | 1.62M | if (lead < 0x10000) | 1801 | 8.83k | { | 1802 | 8.83k | result = Traits::low(result, lead); | 1803 | 8.83k | data += 1; | 1804 | 8.83k | size -= 1; | 1805 | 8.83k | } | 1806 | | // U+10000..U+10FFFF | 1807 | 1.61M | else | 1808 | 1.61M | { | 1809 | 1.61M | result = Traits::high(result, lead); | 1810 | 1.61M | data += 1; | 1811 | 1.61M | size -= 1; | 1812 | 1.61M | } | 1813 | 1.62M | } | 1814 | | | 1815 | 342 | return result; | 1816 | 342 | } |
|
1817 | | }; |
1818 | | |
1819 | | struct latin1_decoder |
1820 | | { |
1821 | | typedef uint8_t type; |
1822 | | |
1823 | | template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) |
1824 | 1.04k | { |
1825 | 77.6M | while (size) |
1826 | 77.6M | { |
1827 | 77.6M | result = Traits::low(result, *data); |
1828 | 77.6M | data += 1; |
1829 | 77.6M | size -= 1; |
1830 | 77.6M | } |
1831 | | |
1832 | 1.04k | return result; |
1833 | 1.04k | } pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_counter::value_type pugi::impl::(anonymous namespace)::latin1_decoder::process<pugi::impl::(anonymous namespace)::utf8_counter>(unsigned char const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_counter::value_type, pugi::impl::(anonymous namespace)::utf8_counter) Line | Count | Source | 1824 | 522 | { | 1825 | 38.8M | while (size) | 1826 | 38.8M | { | 1827 | 38.8M | result = Traits::low(result, *data); | 1828 | 38.8M | data += 1; | 1829 | 38.8M | size -= 1; | 1830 | 38.8M | } | 1831 | | | 1832 | 522 | return result; | 1833 | 522 | } |
pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_writer::value_type pugi::impl::(anonymous namespace)::latin1_decoder::process<pugi::impl::(anonymous namespace)::utf8_writer>(unsigned char const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_writer::value_type, pugi::impl::(anonymous namespace)::utf8_writer) Line | Count | Source | 1824 | 522 | { | 1825 | 38.8M | while (size) | 1826 | 38.8M | { | 1827 | 38.8M | result = Traits::low(result, *data); | 1828 | 38.8M | data += 1; | 1829 | 38.8M | size -= 1; | 1830 | 38.8M | } | 1831 | | | 1832 | 522 | return result; | 1833 | 522 | } |
|
1834 | | }; |
1835 | | |
1836 | | template <size_t size> struct wchar_selector; |
1837 | | |
1838 | | template <> struct wchar_selector<2> |
1839 | | { |
1840 | | typedef uint16_t type; |
1841 | | typedef utf16_counter counter; |
1842 | | typedef utf16_writer writer; |
1843 | | typedef utf16_decoder<opt_false> decoder; |
1844 | | }; |
1845 | | |
1846 | | template <> struct wchar_selector<4> |
1847 | | { |
1848 | | typedef uint32_t type; |
1849 | | typedef utf32_counter counter; |
1850 | | typedef utf32_writer writer; |
1851 | | typedef utf32_decoder<opt_false> decoder; |
1852 | | }; |
1853 | | |
1854 | | typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter; |
1855 | | typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer; |
1856 | | |
1857 | | struct wchar_decoder |
1858 | | { |
1859 | | typedef wchar_t type; |
1860 | | |
1861 | | template <typename Traits> static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits) |
1862 | 0 | { |
1863 | 0 | typedef wchar_selector<sizeof(wchar_t)>::decoder decoder; |
1864 | |
|
1865 | 0 | return decoder::process(reinterpret_cast<const typename decoder::type*>(data), size, result, traits); |
1866 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_counter::value_type pugi::impl::(anonymous namespace)::wchar_decoder::process<pugi::impl::(anonymous namespace)::utf8_counter>(wchar_t const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_counter::value_type, pugi::impl::(anonymous namespace)::utf8_counter) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::utf8_writer::value_type pugi::impl::(anonymous namespace)::wchar_decoder::process<pugi::impl::(anonymous namespace)::utf8_writer>(wchar_t const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_writer::value_type, pugi::impl::(anonymous namespace)::utf8_writer) |
1867 | | }; |
1868 | | |
1869 | | #ifdef PUGIXML_WCHAR_MODE |
1870 | | PUGI_IMPL_FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) |
1871 | | { |
1872 | | for (size_t i = 0; i < length; ++i) |
1873 | | result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i]))); |
1874 | | } |
1875 | | #endif |
1876 | | PUGI_IMPL_NS_END |
1877 | | |
1878 | | PUGI_IMPL_NS_BEGIN |
1879 | | enum chartype_t |
1880 | | { |
1881 | | ct_parse_pcdata = 1, // \0, &, \r, < |
1882 | | ct_parse_attr = 2, // \0, &, \r, ', " |
1883 | | ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab |
1884 | | ct_space = 8, // \r, \n, space, tab |
1885 | | ct_parse_cdata = 16, // \0, ], >, \r |
1886 | | ct_parse_comment = 32, // \0, -, >, \r |
1887 | | ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . |
1888 | | ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : |
1889 | | }; |
1890 | | |
1891 | | static const unsigned char chartype_table[256] = |
1892 | | { |
1893 | | 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 |
1894 | | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 |
1895 | | 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 |
1896 | | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 |
1897 | | 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 |
1898 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 |
1899 | | 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 |
1900 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 |
1901 | | |
1902 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ |
1903 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, |
1904 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, |
1905 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, |
1906 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, |
1907 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, |
1908 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, |
1909 | | 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 |
1910 | | }; |
1911 | | |
1912 | | enum chartypex_t |
1913 | | { |
1914 | | ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > |
1915 | | ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, ", ' |
1916 | | ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ |
1917 | | ctx_digit = 8, // 0-9 |
1918 | | ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . |
1919 | | }; |
1920 | | |
1921 | | static const unsigned char chartypex_table[256] = |
1922 | | { |
1923 | | 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, // 0-15 |
1924 | | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 |
1925 | | 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 |
1926 | | 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 1, 0, // 48-63 |
1927 | | |
1928 | | 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 |
1929 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 |
1930 | | 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 |
1931 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 |
1932 | | |
1933 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ |
1934 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, |
1935 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, |
1936 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, |
1937 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, |
1938 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, |
1939 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, |
1940 | | 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 |
1941 | | }; |
1942 | | |
1943 | | #ifdef PUGIXML_WCHAR_MODE |
1944 | | #define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) ((static_cast<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct)) |
1945 | | #else |
1946 | 23.9M | #define PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(c)] & (ct)) |
1947 | | #endif |
1948 | | |
1949 | 23.2M | #define PUGI_IMPL_IS_CHARTYPE(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartype_table) |
1950 | 0 | #define PUGI_IMPL_IS_CHARTYPEX(c, ct) PUGI_IMPL_IS_CHARTYPE_IMPL(c, ct, chartypex_table) |
1951 | | |
1952 | | PUGI_IMPL_FN bool is_little_endian() |
1953 | 1.73k | { |
1954 | 1.73k | unsigned int ui = 1; |
1955 | | |
1956 | 1.73k | return *reinterpret_cast<unsigned char*>(&ui) == 1; |
1957 | 1.73k | } |
1958 | | |
1959 | | PUGI_IMPL_FN xml_encoding get_wchar_encoding() |
1960 | 0 | { |
1961 | 0 | PUGI_IMPL_STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); |
1962 | |
|
1963 | 0 | if (sizeof(wchar_t) == 2) |
1964 | 0 | return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; |
1965 | 0 | else |
1966 | 0 | return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; |
1967 | 0 | } |
1968 | | |
1969 | | PUGI_IMPL_FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length) |
1970 | 2.47k | { |
1971 | 17.4k | #define PUGI_IMPL_SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; } |
1972 | 22.1M | #define PUGI_IMPL_SCANCHARTYPE(ct) { while (offset < size && PUGI_IMPL_IS_CHARTYPE(data[offset], ct)) offset++; } |
1973 | | |
1974 | | // check if we have a non-empty XML declaration |
1975 | 2.47k | if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI_IMPL_IS_CHARTYPE(data[5], ct_space))) |
1976 | 156 | return false; |
1977 | | |
1978 | | // scan XML declaration until the encoding field |
1979 | 18.6M | for (size_t i = 6; i + 1 < size; ++i) |
1980 | 18.6M | { |
1981 | | // declaration can not contain ? in quoted values |
1982 | 18.6M | if (data[i] == '?') |
1983 | 363 | return false; |
1984 | | |
1985 | 18.6M | if (data[i] == 'e' && data[i + 1] == 'n') |
1986 | 1.81k | { |
1987 | 1.81k | size_t offset = i; |
1988 | | |
1989 | | // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed |
1990 | 1.81k | PUGI_IMPL_SCANCHAR('e'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('c'); PUGI_IMPL_SCANCHAR('o'); |
1991 | 1.68k | PUGI_IMPL_SCANCHAR('d'); PUGI_IMPL_SCANCHAR('i'); PUGI_IMPL_SCANCHAR('n'); PUGI_IMPL_SCANCHAR('g'); |
1992 | | |
1993 | | // S? = S? |
1994 | 1.40k | PUGI_IMPL_SCANCHARTYPE(ct_space); |
1995 | 1.40k | PUGI_IMPL_SCANCHAR('='); |
1996 | 1.31k | PUGI_IMPL_SCANCHARTYPE(ct_space); |
1997 | | |
1998 | | // the only two valid delimiters are ' and " |
1999 | 1.31k | uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\''; |
2000 | | |
2001 | 1.31k | PUGI_IMPL_SCANCHAR(delimiter); |
2002 | | |
2003 | 1.17k | size_t start = offset; |
2004 | | |
2005 | 1.17k | out_encoding = data + offset; |
2006 | | |
2007 | 1.17k | PUGI_IMPL_SCANCHARTYPE(ct_symbol); |
2008 | | |
2009 | 1.17k | out_length = offset - start; |
2010 | | |
2011 | 1.17k | PUGI_IMPL_SCANCHAR(delimiter); |
2012 | | |
2013 | 1.07k | return true; |
2014 | 1.17k | } |
2015 | 18.6M | } |
2016 | | |
2017 | 138 | return false; |
2018 | | |
2019 | 2.31k | #undef PUGI_IMPL_SCANCHAR |
2020 | 2.31k | #undef PUGI_IMPL_SCANCHARTYPE |
2021 | 2.31k | } |
2022 | | |
2023 | | PUGI_IMPL_FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size) |
2024 | 11.4k | { |
2025 | | // skip encoding autodetection if input buffer is too small |
2026 | 11.4k | if (size < 4) return encoding_utf8; |
2027 | | |
2028 | 11.0k | uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; |
2029 | | |
2030 | | // look for BOM in first few bytes |
2031 | 11.0k | if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; |
2032 | 10.9k | if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; |
2033 | 10.8k | if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; |
2034 | 10.7k | if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; |
2035 | 10.6k | if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; |
2036 | | |
2037 | | // look for <, <? or <?xm in various encodings |
2038 | 10.5k | if (d0 == 0 && d1 == 0 && d2 == 0 && d3 == 0x3c) return encoding_utf32_be; |
2039 | 10.3k | if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le; |
2040 | 10.0k | if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be; |
2041 | 10.0k | if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le; |
2042 | | |
2043 | | // look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early) |
2044 | 10.0k | if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be; |
2045 | 9.72k | if (d0 == 0x3c && d1 == 0) return encoding_utf16_le; |
2046 | | |
2047 | | // no known BOM detected; parse declaration |
2048 | 9.30k | const uint8_t* enc = NULL; |
2049 | 9.30k | size_t enc_length = 0; |
2050 | | |
2051 | 9.30k | if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d && parse_declaration_encoding(data, size, enc, enc_length)) |
2052 | 1.07k | { |
2053 | | // iso-8859-1 (case-insensitive) |
2054 | 1.07k | if (enc_length == 10 |
2055 | 231 | && (enc[0] | ' ') == 'i' && (enc[1] | ' ') == 's' && (enc[2] | ' ') == 'o' |
2056 | 141 | && enc[3] == '-' && enc[4] == '8' && enc[5] == '8' && enc[6] == '5' && enc[7] == '9' |
2057 | 33 | && enc[8] == '-' && enc[9] == '1') |
2058 | 3 | return encoding_latin1; |
2059 | | |
2060 | | // latin1 (case-insensitive) |
2061 | 1.07k | if (enc_length == 6 |
2062 | 708 | && (enc[0] | ' ') == 'l' && (enc[1] | ' ') == 'a' && (enc[2] | ' ') == 't' |
2063 | 636 | && (enc[3] | ' ') == 'i' && (enc[4] | ' ') == 'n' |
2064 | 573 | && enc[5] == '1') |
2065 | 546 | return encoding_latin1; |
2066 | 1.07k | } |
2067 | | |
2068 | 8.75k | return encoding_utf8; |
2069 | 9.30k | } |
2070 | | |
2071 | | PUGI_IMPL_FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size) |
2072 | 11.4k | { |
2073 | | // replace wchar encoding with utf implementation |
2074 | 11.4k | if (encoding == encoding_wchar) return get_wchar_encoding(); |
2075 | | |
2076 | | // replace utf16 encoding with utf16 with specific endianness |
2077 | 11.4k | if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; |
2078 | | |
2079 | | // replace utf32 encoding with utf32 with specific endianness |
2080 | 11.4k | if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; |
2081 | | |
2082 | | // only do autodetection if no explicit encoding is requested |
2083 | 11.4k | if (encoding != encoding_auto) return encoding; |
2084 | | |
2085 | | // try to guess encoding (based on XML specification, Appendix F.1) |
2086 | 11.4k | const uint8_t* data = static_cast<const uint8_t*>(contents); |
2087 | | |
2088 | 11.4k | return guess_buffer_encoding(data, size); |
2089 | 11.4k | } |
2090 | | |
2091 | | PUGI_IMPL_FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) |
2092 | 9.17k | { |
2093 | 9.17k | size_t length = size / sizeof(char_t); |
2094 | | |
2095 | 9.17k | if (is_mutable) |
2096 | 0 | { |
2097 | 0 | out_buffer = static_cast<char_t*>(const_cast<void*>(contents)); |
2098 | 0 | out_length = length; |
2099 | 0 | } |
2100 | 9.17k | else |
2101 | 9.17k | { |
2102 | 9.17k | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); |
2103 | 9.17k | if (!buffer) return false; |
2104 | | |
2105 | 9.17k | if (contents) |
2106 | 9.17k | memcpy(buffer, contents, length * sizeof(char_t)); |
2107 | 0 | else |
2108 | 9.17k | assert(length == 0); |
2109 | | |
2110 | 9.17k | buffer[length] = 0; |
2111 | | |
2112 | 9.17k | out_buffer = buffer; |
2113 | 9.17k | out_length = length + 1; |
2114 | 9.17k | } |
2115 | | |
2116 | 9.17k | return true; |
2117 | 9.17k | } |
2118 | | |
2119 | | #ifdef PUGIXML_WCHAR_MODE |
2120 | | PUGI_IMPL_FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) |
2121 | | { |
2122 | | return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || |
2123 | | (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); |
2124 | | } |
2125 | | |
2126 | | PUGI_IMPL_FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) |
2127 | | { |
2128 | | const char_t* data = static_cast<const char_t*>(contents); |
2129 | | size_t length = size / sizeof(char_t); |
2130 | | |
2131 | | if (is_mutable) |
2132 | | { |
2133 | | char_t* buffer = const_cast<char_t*>(data); |
2134 | | |
2135 | | convert_wchar_endian_swap(buffer, data, length); |
2136 | | |
2137 | | out_buffer = buffer; |
2138 | | out_length = length; |
2139 | | } |
2140 | | else |
2141 | | { |
2142 | | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); |
2143 | | if (!buffer) return false; |
2144 | | |
2145 | | convert_wchar_endian_swap(buffer, data, length); |
2146 | | buffer[length] = 0; |
2147 | | |
2148 | | out_buffer = buffer; |
2149 | | out_length = length + 1; |
2150 | | } |
2151 | | |
2152 | | return true; |
2153 | | } |
2154 | | |
2155 | | template <typename D> PUGI_IMPL_FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) |
2156 | | { |
2157 | | const typename D::type* data = static_cast<const typename D::type*>(contents); |
2158 | | size_t data_length = size / sizeof(typename D::type); |
2159 | | |
2160 | | // first pass: get length in wchar_t units |
2161 | | size_t length = D::process(data, data_length, 0, wchar_counter()); |
2162 | | |
2163 | | // allocate buffer of suitable length |
2164 | | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); |
2165 | | if (!buffer) return false; |
2166 | | |
2167 | | // second pass: convert utf16 input to wchar_t |
2168 | | wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer); |
2169 | | wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer()); |
2170 | | |
2171 | | assert(oend == obegin + length); |
2172 | | *oend = 0; |
2173 | | |
2174 | | out_buffer = buffer; |
2175 | | out_length = length + 1; |
2176 | | |
2177 | | return true; |
2178 | | } |
2179 | | |
2180 | | PUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) |
2181 | | { |
2182 | | // get native encoding |
2183 | | xml_encoding wchar_encoding = get_wchar_encoding(); |
2184 | | |
2185 | | // fast path: no conversion required |
2186 | | if (encoding == wchar_encoding) |
2187 | | return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); |
2188 | | |
2189 | | // only endian-swapping is required |
2190 | | if (need_endian_swap_utf(encoding, wchar_encoding)) |
2191 | | return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); |
2192 | | |
2193 | | // source encoding is utf8 |
2194 | | if (encoding == encoding_utf8) |
2195 | | return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder()); |
2196 | | |
2197 | | // source encoding is utf16 |
2198 | | if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) |
2199 | | { |
2200 | | xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; |
2201 | | |
2202 | | return (native_encoding == encoding) ? |
2203 | | convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) : |
2204 | | convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>()); |
2205 | | } |
2206 | | |
2207 | | // source encoding is utf32 |
2208 | | if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) |
2209 | | { |
2210 | | xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; |
2211 | | |
2212 | | return (native_encoding == encoding) ? |
2213 | | convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) : |
2214 | | convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>()); |
2215 | | } |
2216 | | |
2217 | | // source encoding is latin1 |
2218 | | if (encoding == encoding_latin1) |
2219 | | return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder()); |
2220 | | |
2221 | | assert(false && "Invalid encoding"); // unreachable |
2222 | | return false; |
2223 | | } |
2224 | | #else |
2225 | | template <typename D> PUGI_IMPL_FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) |
2226 | 1.73k | { |
2227 | 1.73k | const typename D::type* data = static_cast<const typename D::type*>(contents); |
2228 | 1.73k | size_t data_length = size / sizeof(typename D::type); |
2229 | | |
2230 | | // first pass: get length in utf8 units |
2231 | 1.73k | size_t length = D::process(data, data_length, 0, utf8_counter()); |
2232 | | |
2233 | | // allocate buffer of suitable length |
2234 | 1.73k | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); |
2235 | 1.73k | if (!buffer) return false; |
2236 | | |
2237 | | // second pass: convert utf16 input to utf8 |
2238 | 1.73k | uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer); |
2239 | 1.73k | uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); |
2240 | | |
2241 | 1.73k | assert(oend == obegin + length); |
2242 | 1.73k | *oend = 0; |
2243 | | |
2244 | 1.73k | out_buffer = buffer; |
2245 | 1.73k | out_length = length + 1; |
2246 | | |
2247 | 1.73k | return true; |
2248 | 1.73k | } pugixml.cpp:bool pugi::impl::(anonymous namespace)::convert_buffer_generic<pugi::impl::(anonymous namespace)::utf16_decoder<pugi::impl::(anonymous namespace)::opt_false> >(char*&, unsigned long&, void const*, unsigned long, pugi::impl::(anonymous namespace)::utf16_decoder<pugi::impl::(anonymous namespace)::opt_false>) Line | Count | Source | 2226 | 570 | { | 2227 | 570 | const typename D::type* data = static_cast<const typename D::type*>(contents); | 2228 | 570 | size_t data_length = size / sizeof(typename D::type); | 2229 | | | 2230 | | // first pass: get length in utf8 units | 2231 | 570 | size_t length = D::process(data, data_length, 0, utf8_counter()); | 2232 | | | 2233 | | // allocate buffer of suitable length | 2234 | 570 | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); | 2235 | 570 | if (!buffer) return false; | 2236 | | | 2237 | | // second pass: convert utf16 input to utf8 | 2238 | 570 | uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer); | 2239 | 570 | uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); | 2240 | | | 2241 | 570 | assert(oend == obegin + length); | 2242 | 570 | *oend = 0; | 2243 | | | 2244 | 570 | out_buffer = buffer; | 2245 | 570 | out_length = length + 1; | 2246 | | | 2247 | 570 | return true; | 2248 | 570 | } |
pugixml.cpp:bool pugi::impl::(anonymous namespace)::convert_buffer_generic<pugi::impl::(anonymous namespace)::utf16_decoder<pugi::impl::(anonymous namespace)::opt_true> >(char*&, unsigned long&, void const*, unsigned long, pugi::impl::(anonymous namespace)::utf16_decoder<pugi::impl::(anonymous namespace)::opt_true>) Line | Count | Source | 2226 | 471 | { | 2227 | 471 | const typename D::type* data = static_cast<const typename D::type*>(contents); | 2228 | 471 | size_t data_length = size / sizeof(typename D::type); | 2229 | | | 2230 | | // first pass: get length in utf8 units | 2231 | 471 | size_t length = D::process(data, data_length, 0, utf8_counter()); | 2232 | | | 2233 | | // allocate buffer of suitable length | 2234 | 471 | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); | 2235 | 471 | if (!buffer) return false; | 2236 | | | 2237 | | // second pass: convert utf16 input to utf8 | 2238 | 471 | uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer); | 2239 | 471 | uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); | 2240 | | | 2241 | 471 | assert(oend == obegin + length); | 2242 | 471 | *oend = 0; | 2243 | | | 2244 | 471 | out_buffer = buffer; | 2245 | 471 | out_length = length + 1; | 2246 | | | 2247 | 471 | return true; | 2248 | 471 | } |
pugixml.cpp:bool pugi::impl::(anonymous namespace)::convert_buffer_generic<pugi::impl::(anonymous namespace)::utf32_decoder<pugi::impl::(anonymous namespace)::opt_false> >(char*&, unsigned long&, void const*, unsigned long, pugi::impl::(anonymous namespace)::utf32_decoder<pugi::impl::(anonymous namespace)::opt_false>) Line | Count | Source | 2226 | 351 | { | 2227 | 351 | const typename D::type* data = static_cast<const typename D::type*>(contents); | 2228 | 351 | size_t data_length = size / sizeof(typename D::type); | 2229 | | | 2230 | | // first pass: get length in utf8 units | 2231 | 351 | size_t length = D::process(data, data_length, 0, utf8_counter()); | 2232 | | | 2233 | | // allocate buffer of suitable length | 2234 | 351 | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); | 2235 | 351 | if (!buffer) return false; | 2236 | | | 2237 | | // second pass: convert utf16 input to utf8 | 2238 | 351 | uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer); | 2239 | 351 | uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); | 2240 | | | 2241 | 351 | assert(oend == obegin + length); | 2242 | 351 | *oend = 0; | 2243 | | | 2244 | 351 | out_buffer = buffer; | 2245 | 351 | out_length = length + 1; | 2246 | | | 2247 | 351 | return true; | 2248 | 351 | } |
pugixml.cpp:bool pugi::impl::(anonymous namespace)::convert_buffer_generic<pugi::impl::(anonymous namespace)::utf32_decoder<pugi::impl::(anonymous namespace)::opt_true> >(char*&, unsigned long&, void const*, unsigned long, pugi::impl::(anonymous namespace)::utf32_decoder<pugi::impl::(anonymous namespace)::opt_true>) Line | Count | Source | 2226 | 342 | { | 2227 | 342 | const typename D::type* data = static_cast<const typename D::type*>(contents); | 2228 | 342 | size_t data_length = size / sizeof(typename D::type); | 2229 | | | 2230 | | // first pass: get length in utf8 units | 2231 | 342 | size_t length = D::process(data, data_length, 0, utf8_counter()); | 2232 | | | 2233 | | // allocate buffer of suitable length | 2234 | 342 | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); | 2235 | 342 | if (!buffer) return false; | 2236 | | | 2237 | | // second pass: convert utf16 input to utf8 | 2238 | 342 | uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer); | 2239 | 342 | uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); | 2240 | | | 2241 | 342 | assert(oend == obegin + length); | 2242 | 342 | *oend = 0; | 2243 | | | 2244 | 342 | out_buffer = buffer; | 2245 | 342 | out_length = length + 1; | 2246 | | | 2247 | 342 | return true; | 2248 | 342 | } |
|
2249 | | |
2250 | | PUGI_IMPL_FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) |
2251 | 549 | { |
2252 | 34.1k | for (size_t i = 0; i < size; ++i) |
2253 | 34.1k | if (data[i] > 127) |
2254 | 522 | return i; |
2255 | | |
2256 | 27 | return size; |
2257 | 549 | } |
2258 | | |
2259 | | PUGI_IMPL_FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) |
2260 | 549 | { |
2261 | 549 | const uint8_t* data = static_cast<const uint8_t*>(contents); |
2262 | 549 | size_t data_length = size; |
2263 | | |
2264 | | // get size of prefix that does not need utf8 conversion |
2265 | 549 | size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); |
2266 | 549 | assert(prefix_length <= data_length); |
2267 | | |
2268 | 549 | const uint8_t* postfix = data + prefix_length; |
2269 | 549 | size_t postfix_length = data_length - prefix_length; |
2270 | | |
2271 | | // if no conversion is needed, just return the original buffer |
2272 | 549 | if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); |
2273 | | |
2274 | | // first pass: get length in utf8 units |
2275 | 522 | size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter()); |
2276 | | |
2277 | | // allocate buffer of suitable length |
2278 | 522 | char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); |
2279 | 522 | if (!buffer) return false; |
2280 | | |
2281 | | // second pass: convert latin1 input to utf8 |
2282 | 522 | memcpy(buffer, data, prefix_length); |
2283 | | |
2284 | 522 | uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer); |
2285 | 522 | uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer()); |
2286 | | |
2287 | 522 | assert(oend == obegin + length); |
2288 | 522 | *oend = 0; |
2289 | | |
2290 | 522 | out_buffer = buffer; |
2291 | 522 | out_length = length + 1; |
2292 | | |
2293 | 522 | return true; |
2294 | 522 | } |
2295 | | |
2296 | | PUGI_IMPL_FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) |
2297 | 11.4k | { |
2298 | | // fast path: no conversion required |
2299 | 11.4k | if (encoding == encoding_utf8) |
2300 | 9.14k | return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); |
2301 | | |
2302 | | // source encoding is utf16 |
2303 | 2.28k | if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) |
2304 | 1.04k | { |
2305 | 1.04k | xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; |
2306 | | |
2307 | 1.04k | return (native_encoding == encoding) ? |
2308 | 570 | convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) : |
2309 | 1.04k | convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>()); |
2310 | 1.04k | } |
2311 | | |
2312 | | // source encoding is utf32 |
2313 | 1.24k | if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) |
2314 | 693 | { |
2315 | 693 | xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; |
2316 | | |
2317 | 693 | return (native_encoding == encoding) ? |
2318 | 351 | convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) : |
2319 | 693 | convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>()); |
2320 | 693 | } |
2321 | | |
2322 | | // source encoding is latin1 |
2323 | 549 | if (encoding == encoding_latin1) |
2324 | 549 | return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); |
2325 | | |
2326 | 549 | assert(false && "Invalid encoding"); // unreachable |
2327 | 0 | return false; |
2328 | 0 | } |
2329 | | #endif |
2330 | | |
2331 | | PUGI_IMPL_FN size_t as_utf8_begin(const wchar_t* str, size_t length) |
2332 | 0 | { |
2333 | | // get length in utf8 characters |
2334 | 0 | return wchar_decoder::process(str, length, 0, utf8_counter()); |
2335 | 0 | } |
2336 | | |
2337 | | PUGI_IMPL_FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) |
2338 | 0 | { |
2339 | | // convert to utf8 |
2340 | 0 | uint8_t* begin = reinterpret_cast<uint8_t*>(buffer); |
2341 | 0 | uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer()); |
2342 | |
|
2343 | 0 | assert(begin + size == end); |
2344 | 0 | (void)!end; |
2345 | 0 | (void)!size; |
2346 | 0 | } |
2347 | | |
2348 | | #ifndef PUGIXML_NO_STL |
2349 | | PUGI_IMPL_FN std::string as_utf8_impl(const wchar_t* str, size_t length) |
2350 | 0 | { |
2351 | | // first pass: get length in utf8 characters |
2352 | 0 | size_t size = as_utf8_begin(str, length); |
2353 | | |
2354 | | // allocate resulting string |
2355 | 0 | std::string result; |
2356 | 0 | result.resize(size); |
2357 | | |
2358 | | // second pass: convert to utf8 |
2359 | 0 | if (size > 0) as_utf8_end(&result[0], size, str, length); |
2360 | |
|
2361 | 0 | return result; |
2362 | 0 | } |
2363 | | |
2364 | | PUGI_IMPL_FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size) |
2365 | 0 | { |
2366 | 0 | const uint8_t* data = reinterpret_cast<const uint8_t*>(str); |
2367 | | |
2368 | | // first pass: get length in wchar_t units |
2369 | 0 | size_t length = utf8_decoder::process(data, size, 0, wchar_counter()); |
2370 | | |
2371 | | // allocate resulting string |
2372 | 0 | std::basic_string<wchar_t> result; |
2373 | 0 | result.resize(length); |
2374 | | |
2375 | | // second pass: convert to wchar_t |
2376 | 0 | if (length > 0) |
2377 | 0 | { |
2378 | 0 | wchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]); |
2379 | 0 | wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer()); |
2380 | |
|
2381 | 0 | assert(begin + length == end); |
2382 | 0 | (void)!end; |
2383 | 0 | } |
2384 | | |
2385 | 0 | return result; |
2386 | 0 | } |
2387 | | #endif |
2388 | | |
2389 | | template <typename Header> |
2390 | | inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target) |
2391 | 0 | { |
2392 | | // never reuse shared memory |
2393 | 0 | if (header & xml_memory_page_contents_shared_mask) return false; |
2394 | | |
2395 | 0 | size_t target_length = strlength(target); |
2396 | | |
2397 | | // always reuse document buffer memory if possible |
2398 | 0 | if ((header & header_mask) == 0) return target_length >= length; |
2399 | | |
2400 | | // reuse heap memory if waste is not too great |
2401 | 0 | const size_t reuse_threshold = 32; |
2402 | |
|
2403 | 0 | return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); |
2404 | 0 | } |
2405 | | |
2406 | | template <typename String, typename Header> |
2407 | | PUGI_IMPL_FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) |
2408 | 0 | { |
2409 | 0 | assert((header & header_mask) == 0 || dest); // header bit indicates whether dest was previously allocated |
2410 | | |
2411 | 0 | if (source_length == 0) |
2412 | 0 | { |
2413 | | // empty string and null pointer are equivalent, so just deallocate old memory |
2414 | 0 | xml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator; |
2415 | |
|
2416 | 0 | if (header & header_mask) alloc->deallocate_string(dest); |
2417 | | |
2418 | | // mark the string as not allocated |
2419 | 0 | dest = NULL; |
2420 | 0 | header &= ~header_mask; |
2421 | |
|
2422 | 0 | return true; |
2423 | 0 | } |
2424 | 0 | else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) |
2425 | 0 | { |
2426 | | // we can reuse old buffer, so just copy the new data (including zero terminator) |
2427 | 0 | memcpy(dest, source, source_length * sizeof(char_t)); |
2428 | 0 | dest[source_length] = 0; |
2429 | |
|
2430 | 0 | return true; |
2431 | 0 | } |
2432 | 0 | else |
2433 | 0 | { |
2434 | 0 | xml_allocator* alloc = PUGI_IMPL_GETPAGE_IMPL(header)->allocator; |
2435 | |
|
2436 | 0 | if (!alloc->reserve()) return false; |
2437 | | |
2438 | | // allocate new buffer |
2439 | 0 | char_t* buf = alloc->allocate_string(source_length + 1); |
2440 | 0 | if (!buf) return false; |
2441 | | |
2442 | | // copy the string (including zero terminator) |
2443 | 0 | memcpy(buf, source, source_length * sizeof(char_t)); |
2444 | 0 | buf[source_length] = 0; |
2445 | | |
2446 | | // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) |
2447 | 0 | if (header & header_mask) alloc->deallocate_string(dest); |
2448 | | |
2449 | | // the string is now allocated, so set the flag |
2450 | 0 | dest = buf; |
2451 | 0 | header |= header_mask; |
2452 | |
|
2453 | 0 | return true; |
2454 | 0 | } |
2455 | 0 | } |
2456 | | |
2457 | | struct gap |
2458 | | { |
2459 | | char_t* end; |
2460 | | size_t size; |
2461 | | |
2462 | 413k | gap(): end(NULL), size(0) |
2463 | 413k | { |
2464 | 413k | } |
2465 | | |
2466 | | // Push new gap, move s count bytes further (skipping the gap). |
2467 | | // Collapse previous gap. |
2468 | | void push(char_t*& s, size_t count) |
2469 | 18.5k | { |
2470 | 18.5k | if (end) // there was a gap already; collapse it |
2471 | 5.93k | { |
2472 | | // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) |
2473 | 5.93k | assert(s >= end); |
2474 | 5.93k | memmove(end - size, end, (s - end) * sizeof(char_t)); |
2475 | 5.93k | } |
2476 | | |
2477 | 18.5k | s += count; // end of current gap |
2478 | | |
2479 | | // "merge" two gaps |
2480 | 18.5k | end = s; |
2481 | 18.5k | size += count; |
2482 | 18.5k | } |
2483 | | |
2484 | | // Collapse all gaps, return past-the-end pointer |
2485 | | char_t* flush(char_t* s) |
2486 | 412k | { |
2487 | 412k | if (end) |
2488 | 12.5k | { |
2489 | | // Move [old_gap_end, current_pos) to [old_gap_start, ...) |
2490 | 12.5k | assert(s >= end); |
2491 | 12.5k | memmove(end - size, end, (s - end) * sizeof(char_t)); |
2492 | | |
2493 | 12.5k | return s - size; |
2494 | 12.5k | } |
2495 | 400k | else return s; |
2496 | 412k | } |
2497 | | }; |
2498 | | |
2499 | | PUGI_IMPL_FN char_t* strconv_escape(char_t* s, gap& g) |
2500 | 61.5k | { |
2501 | 61.5k | char_t* stre = s + 1; |
2502 | | |
2503 | 61.5k | switch (*stre) |
2504 | 61.5k | { |
2505 | 7.77k | case '#': // &#... |
2506 | 7.77k | { |
2507 | 7.77k | unsigned int ucsc = 0; |
2508 | | |
2509 | 7.77k | if (stre[1] == 'x') // &#x... (hex code) |
2510 | 2.66k | { |
2511 | 2.66k | stre += 2; |
2512 | | |
2513 | 2.66k | char_t ch = *stre; |
2514 | | |
2515 | 2.66k | if (ch == ';') return stre; |
2516 | | |
2517 | 2.41k | for (;;) |
2518 | 7.54k | { |
2519 | 7.54k | if (static_cast<unsigned int>(ch - '0') <= 9) |
2520 | 1.34k | ucsc = 16 * ucsc + (ch - '0'); |
2521 | 6.19k | else if (static_cast<unsigned int>((ch | ' ') - 'a') <= 5) |
2522 | 3.77k | ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); |
2523 | 2.41k | else if (ch == ';') |
2524 | 1.95k | break; |
2525 | 465 | else // cancel |
2526 | 465 | return stre; |
2527 | | |
2528 | 5.12k | ch = *++stre; |
2529 | 5.12k | } |
2530 | | |
2531 | 1.95k | ++stre; |
2532 | 1.95k | } |
2533 | 5.11k | else // &#... (dec code) |
2534 | 5.11k | { |
2535 | 5.11k | char_t ch = *++stre; |
2536 | | |
2537 | 5.11k | if (ch == ';') return stre; |
2538 | | |
2539 | 4.05k | for (;;) |
2540 | 9.66k | { |
2541 | 9.66k | if (static_cast<unsigned int>(ch - '0') <= 9) |
2542 | 5.61k | ucsc = 10 * ucsc + (ch - '0'); |
2543 | 4.05k | else if (ch == ';') |
2544 | 1.30k | break; |
2545 | 2.75k | else // cancel |
2546 | 2.75k | return stre; |
2547 | | |
2548 | 5.61k | ch = *++stre; |
2549 | 5.61k | } |
2550 | | |
2551 | 1.30k | ++stre; |
2552 | 1.30k | } |
2553 | | |
2554 | | #ifdef PUGIXML_WCHAR_MODE |
2555 | | s = reinterpret_cast<char_t*>(wchar_writer::any(reinterpret_cast<wchar_writer::value_type>(s), ucsc)); |
2556 | | #else |
2557 | 3.25k | s = reinterpret_cast<char_t*>(utf8_writer::any(reinterpret_cast<uint8_t*>(s), ucsc)); |
2558 | 3.25k | #endif |
2559 | | |
2560 | 3.25k | g.push(s, stre - s); |
2561 | 3.25k | return stre; |
2562 | 7.77k | } |
2563 | | |
2564 | 9.38k | case 'a': // &a |
2565 | 9.38k | { |
2566 | 9.38k | ++stre; |
2567 | | |
2568 | 9.38k | if (*stre == 'm') // &am |
2569 | 1.23k | { |
2570 | 1.23k | if (*++stre == 'p' && *++stre == ';') // & |
2571 | 684 | { |
2572 | 684 | *s++ = '&'; |
2573 | 684 | ++stre; |
2574 | | |
2575 | 684 | g.push(s, stre - s); |
2576 | 684 | return stre; |
2577 | 684 | } |
2578 | 1.23k | } |
2579 | 8.15k | else if (*stre == 'p') // &ap |
2580 | 4.24k | { |
2581 | 4.24k | if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' |
2582 | 359 | { |
2583 | 359 | *s++ = '\''; |
2584 | 359 | ++stre; |
2585 | | |
2586 | 359 | g.push(s, stre - s); |
2587 | 359 | return stre; |
2588 | 359 | } |
2589 | 4.24k | } |
2590 | 8.34k | break; |
2591 | 9.38k | } |
2592 | | |
2593 | 12.0k | case 'g': // &g |
2594 | 12.0k | { |
2595 | 12.0k | if (*++stre == 't' && *++stre == ';') // > |
2596 | 509 | { |
2597 | 509 | *s++ = '>'; |
2598 | 509 | ++stre; |
2599 | | |
2600 | 509 | g.push(s, stre - s); |
2601 | 509 | return stre; |
2602 | 509 | } |
2603 | 11.4k | break; |
2604 | 12.0k | } |
2605 | | |
2606 | 11.4k | case 'l': // &l |
2607 | 9.91k | { |
2608 | 9.91k | if (*++stre == 't' && *++stre == ';') // < |
2609 | 704 | { |
2610 | 704 | *s++ = '<'; |
2611 | 704 | ++stre; |
2612 | | |
2613 | 704 | g.push(s, stre - s); |
2614 | 704 | return stre; |
2615 | 704 | } |
2616 | 9.21k | break; |
2617 | 9.91k | } |
2618 | | |
2619 | 12.5k | case 'q': // &q |
2620 | 12.5k | { |
2621 | 12.5k | if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " |
2622 | 10.0k | { |
2623 | 10.0k | *s++ = '"'; |
2624 | 10.0k | ++stre; |
2625 | | |
2626 | 10.0k | g.push(s, stre - s); |
2627 | 10.0k | return stre; |
2628 | 10.0k | } |
2629 | 2.46k | break; |
2630 | 12.5k | } |
2631 | | |
2632 | 9.95k | default: |
2633 | 9.95k | break; |
2634 | 61.5k | } |
2635 | | |
2636 | 41.4k | return stre; |
2637 | 61.5k | } |
2638 | | |
2639 | | // Parser utilities |
2640 | 7.18k | #define PUGI_IMPL_ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) |
2641 | 670k | #define PUGI_IMPL_SKIPWS() { while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) ++s; } |
2642 | 1.57M | #define PUGI_IMPL_OPTSET(OPT) ( optmsk & (OPT) ) |
2643 | 764k | #define PUGI_IMPL_PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s); } |
2644 | 2.89k | #define PUGI_IMPL_POPNODE() { cursor = cursor->parent; } |
2645 | 34.3M | #define PUGI_IMPL_SCANFOR(X) { while (*s != 0 && !(X)) ++s; } |
2646 | 1.01M | #define PUGI_IMPL_SCANWHILE(X) { while (X) ++s; } |
2647 | 2.69M | #define PUGI_IMPL_SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI_IMPL_UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI_IMPL_UNLIKELY(!(X))) { s += 3; break; } s += 4; } } |
2648 | 556k | #define PUGI_IMPL_ENDSEG() { ch = *s; *s = 0; ++s; } |
2649 | 9.87k | #define PUGI_IMPL_THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast<char_t*>(NULL) |
2650 | 18.9k | #define PUGI_IMPL_CHECK_ERROR(err, m) { if (*s == 0) PUGI_IMPL_THROW_ERROR(err, m); } |
2651 | | |
2652 | | PUGI_IMPL_FN char_t* strconv_comment(char_t* s, char_t endch) |
2653 | 1.09k | { |
2654 | 1.09k | gap g; |
2655 | | |
2656 | 3.58k | while (true) |
2657 | 3.58k | { |
2658 | 3.58k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_comment)); |
2659 | | |
2660 | 3.58k | if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair |
2661 | 1.43k | { |
2662 | 1.43k | *s++ = '\n'; // replace first one with 0x0a |
2663 | | |
2664 | 1.43k | if (*s == '\n') g.push(s, 1); |
2665 | 1.43k | } |
2666 | 2.14k | else if (s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>')) // comment ends here |
2667 | 948 | { |
2668 | 948 | *g.flush(s) = 0; |
2669 | | |
2670 | 948 | return s + (s[2] == '>' ? 3 : 2); |
2671 | 948 | } |
2672 | 1.20k | else if (*s == 0) |
2673 | 148 | { |
2674 | 148 | return NULL; |
2675 | 148 | } |
2676 | 1.05k | else ++s; |
2677 | 3.58k | } |
2678 | 1.09k | } |
2679 | | |
2680 | | PUGI_IMPL_FN char_t* strconv_cdata(char_t* s, char_t endch) |
2681 | 1.23k | { |
2682 | 1.23k | gap g; |
2683 | | |
2684 | 8.88k | while (true) |
2685 | 8.88k | { |
2686 | 8.88k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_cdata)); |
2687 | | |
2688 | 8.88k | if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair |
2689 | 4.68k | { |
2690 | 4.68k | *s++ = '\n'; // replace first one with 0x0a |
2691 | | |
2692 | 4.68k | if (*s == '\n') g.push(s, 1); |
2693 | 4.68k | } |
2694 | 4.19k | else if (s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')) // CDATA ends here |
2695 | 970 | { |
2696 | 970 | *g.flush(s) = 0; |
2697 | | |
2698 | 970 | return s + 1; |
2699 | 970 | } |
2700 | 3.22k | else if (*s == 0) |
2701 | 267 | { |
2702 | 267 | return NULL; |
2703 | 267 | } |
2704 | 2.95k | else ++s; |
2705 | 8.88k | } |
2706 | 1.23k | } |
2707 | | |
2708 | | typedef char_t* (*strconv_pcdata_t)(char_t*); |
2709 | | |
2710 | | template <typename opt_trim, typename opt_eol, typename opt_escape> struct strconv_pcdata_impl |
2711 | | { |
2712 | | static char_t* parse(char_t* s) |
2713 | 308k | { |
2714 | 308k | gap g; |
2715 | | |
2716 | 308k | char_t* begin = s; |
2717 | | |
2718 | 402k | while (true) |
2719 | 402k | { |
2720 | 402k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_pcdata)); |
2721 | | |
2722 | 402k | if (*s == '<') // PCDATA ends here |
2723 | 306k | { |
2724 | 306k | char_t* end = g.flush(s); |
2725 | | |
2726 | 306k | if (opt_trim::value) |
2727 | 0 | while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) |
2728 | 0 | --end; |
2729 | | |
2730 | 306k | *end = 0; |
2731 | | |
2732 | 306k | return s + 1; |
2733 | 306k | } |
2734 | 95.9k | else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair |
2735 | 14.0k | { |
2736 | 14.0k | *s++ = '\n'; // replace first one with 0x0a |
2737 | | |
2738 | 14.0k | if (*s == '\n') g.push(s, 1); |
2739 | 14.0k | } |
2740 | 81.8k | else if (opt_escape::value && *s == '&') |
2741 | 48.3k | { |
2742 | 48.3k | s = strconv_escape(s, g); |
2743 | 48.3k | } |
2744 | 33.4k | else if (*s == 0) |
2745 | 1.97k | { |
2746 | 1.97k | char_t* end = g.flush(s); |
2747 | | |
2748 | 1.97k | if (opt_trim::value) |
2749 | 0 | while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) |
2750 | 0 | --end; |
2751 | | |
2752 | 1.97k | *end = 0; |
2753 | | |
2754 | 1.97k | return s; |
2755 | 1.97k | } |
2756 | 31.5k | else ++s; |
2757 | 402k | } |
2758 | 308k | } pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_pcdata_impl<pugi::impl::(anonymous namespace)::opt_false, pugi::impl::(anonymous namespace)::opt_false, pugi::impl::(anonymous namespace)::opt_false>::parse(char*) Line | Count | Source | 2713 | 103k | { | 2714 | 103k | gap g; | 2715 | | | 2716 | 103k | char_t* begin = s; | 2717 | | | 2718 | 134k | while (true) | 2719 | 134k | { | 2720 | 134k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_pcdata)); | 2721 | | | 2722 | 134k | if (*s == '<') // PCDATA ends here | 2723 | 102k | { | 2724 | 102k | char_t* end = g.flush(s); | 2725 | | | 2726 | 102k | if (opt_trim::value) | 2727 | 0 | while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) | 2728 | 0 | --end; | 2729 | | | 2730 | 102k | *end = 0; | 2731 | | | 2732 | 102k | return s + 1; | 2733 | 102k | } | 2734 | 32.1k | else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair | 2735 | 0 | { | 2736 | 0 | *s++ = '\n'; // replace first one with 0x0a | 2737 | |
| 2738 | 0 | if (*s == '\n') g.push(s, 1); | 2739 | 0 | } | 2740 | 32.1k | else if (opt_escape::value && *s == '&') | 2741 | 0 | { | 2742 | 0 | s = strconv_escape(s, g); | 2743 | 0 | } | 2744 | 32.1k | else if (*s == 0) | 2745 | 681 | { | 2746 | 681 | char_t* end = g.flush(s); | 2747 | | | 2748 | 681 | if (opt_trim::value) | 2749 | 0 | while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) | 2750 | 0 | --end; | 2751 | | | 2752 | 681 | *end = 0; | 2753 | | | 2754 | 681 | return s; | 2755 | 681 | } | 2756 | 31.5k | else ++s; | 2757 | 134k | } | 2758 | 103k | } |
Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_pcdata_impl<pugi::impl::(anonymous namespace)::opt_false, pugi::impl::(anonymous namespace)::opt_false, pugi::impl::(anonymous namespace)::opt_true>::parse(char*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_pcdata_impl<pugi::impl::(anonymous namespace)::opt_false, pugi::impl::(anonymous namespace)::opt_true, pugi::impl::(anonymous namespace)::opt_false>::parse(char*) pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_pcdata_impl<pugi::impl::(anonymous namespace)::opt_false, pugi::impl::(anonymous namespace)::opt_true, pugi::impl::(anonymous namespace)::opt_true>::parse(char*) Line | Count | Source | 2713 | 205k | { | 2714 | 205k | gap g; | 2715 | | | 2716 | 205k | char_t* begin = s; | 2717 | | | 2718 | 267k | while (true) | 2719 | 267k | { | 2720 | 267k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_pcdata)); | 2721 | | | 2722 | 267k | if (*s == '<') // PCDATA ends here | 2723 | 204k | { | 2724 | 204k | char_t* end = g.flush(s); | 2725 | | | 2726 | 204k | if (opt_trim::value) | 2727 | 0 | while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) | 2728 | 0 | --end; | 2729 | | | 2730 | 204k | *end = 0; | 2731 | | | 2732 | 204k | return s + 1; | 2733 | 204k | } | 2734 | 63.7k | else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair | 2735 | 14.0k | { | 2736 | 14.0k | *s++ = '\n'; // replace first one with 0x0a | 2737 | | | 2738 | 14.0k | if (*s == '\n') g.push(s, 1); | 2739 | 14.0k | } | 2740 | 49.6k | else if (opt_escape::value && *s == '&') | 2741 | 48.3k | { | 2742 | 48.3k | s = strconv_escape(s, g); | 2743 | 48.3k | } | 2744 | 1.29k | else if (*s == 0) | 2745 | 1.29k | { | 2746 | 1.29k | char_t* end = g.flush(s); | 2747 | | | 2748 | 1.29k | if (opt_trim::value) | 2749 | 0 | while (end > begin && PUGI_IMPL_IS_CHARTYPE(end[-1], ct_space)) | 2750 | 0 | --end; | 2751 | | | 2752 | 1.29k | *end = 0; | 2753 | | | 2754 | 1.29k | return s; | 2755 | 1.29k | } | 2756 | 0 | else ++s; | 2757 | 267k | } | 2758 | 205k | } |
Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_pcdata_impl<pugi::impl::(anonymous namespace)::opt_true, pugi::impl::(anonymous namespace)::opt_false, pugi::impl::(anonymous namespace)::opt_false>::parse(char*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_pcdata_impl<pugi::impl::(anonymous namespace)::opt_true, pugi::impl::(anonymous namespace)::opt_false, pugi::impl::(anonymous namespace)::opt_true>::parse(char*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_pcdata_impl<pugi::impl::(anonymous namespace)::opt_true, pugi::impl::(anonymous namespace)::opt_true, pugi::impl::(anonymous namespace)::opt_false>::parse(char*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_pcdata_impl<pugi::impl::(anonymous namespace)::opt_true, pugi::impl::(anonymous namespace)::opt_true, pugi::impl::(anonymous namespace)::opt_true>::parse(char*) |
2759 | | }; |
2760 | | |
2761 | | PUGI_IMPL_FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) |
2762 | 11.4k | { |
2763 | 11.4k | PUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); |
2764 | | |
2765 | 11.4k | switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above |
2766 | 11.4k | { |
2767 | 3.81k | case 0: return strconv_pcdata_impl<opt_false, opt_false, opt_false>::parse; |
2768 | 0 | case 1: return strconv_pcdata_impl<opt_false, opt_false, opt_true>::parse; |
2769 | 0 | case 2: return strconv_pcdata_impl<opt_false, opt_true, opt_false>::parse; |
2770 | 7.62k | case 3: return strconv_pcdata_impl<opt_false, opt_true, opt_true>::parse; |
2771 | 0 | case 4: return strconv_pcdata_impl<opt_true, opt_false, opt_false>::parse; |
2772 | 0 | case 5: return strconv_pcdata_impl<opt_true, opt_false, opt_true>::parse; |
2773 | 0 | case 6: return strconv_pcdata_impl<opt_true, opt_true, opt_false>::parse; |
2774 | 0 | case 7: return strconv_pcdata_impl<opt_true, opt_true, opt_true>::parse; |
2775 | 0 | default: assert(false); return NULL; // unreachable |
2776 | 11.4k | } |
2777 | 11.4k | } |
2778 | | |
2779 | | typedef char_t* (*strconv_attribute_t)(char_t*, char_t); |
2780 | | |
2781 | | template <typename opt_escape> struct strconv_attribute_impl |
2782 | | { |
2783 | | static char_t* parse_wnorm(char_t* s, char_t end_quote) |
2784 | 0 | { |
2785 | 0 | gap g; |
2786 | | |
2787 | | // trim leading whitespaces |
2788 | 0 | if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) |
2789 | 0 | { |
2790 | 0 | char_t* str = s; |
2791 | |
|
2792 | 0 | do ++str; |
2793 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)); |
2794 | |
|
2795 | 0 | g.push(s, str - s); |
2796 | 0 | } |
2797 | |
|
2798 | 0 | while (true) |
2799 | 0 | { |
2800 | 0 | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); |
2801 | |
|
2802 | 0 | if (*s == end_quote) |
2803 | 0 | { |
2804 | 0 | char_t* str = g.flush(s); |
2805 | |
|
2806 | 0 | do *str-- = 0; |
2807 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)); |
2808 | |
|
2809 | 0 | return s + 1; |
2810 | 0 | } |
2811 | 0 | else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) |
2812 | 0 | { |
2813 | 0 | *s++ = ' '; |
2814 | |
|
2815 | 0 | if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) |
2816 | 0 | { |
2817 | 0 | char_t* str = s + 1; |
2818 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*str, ct_space)) ++str; |
2819 | |
|
2820 | 0 | g.push(s, str - s); |
2821 | 0 | } |
2822 | 0 | } |
2823 | 0 | else if (opt_escape::value && *s == '&') |
2824 | 0 | { |
2825 | 0 | s = strconv_escape(s, g); |
2826 | 0 | } |
2827 | 0 | else if (!*s) |
2828 | 0 | { |
2829 | 0 | return NULL; |
2830 | 0 | } |
2831 | 0 | else ++s; |
2832 | 0 | } |
2833 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_attribute_impl<pugi::impl::(anonymous namespace)::opt_false>::parse_wnorm(char*, char) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_attribute_impl<pugi::impl::(anonymous namespace)::opt_true>::parse_wnorm(char*, char) |
2834 | | |
2835 | | static char_t* parse_wconv(char_t* s, char_t end_quote) |
2836 | 68.3k | { |
2837 | 68.3k | gap g; |
2838 | | |
2839 | 85.5k | while (true) |
2840 | 85.5k | { |
2841 | 85.5k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws)); |
2842 | | |
2843 | 85.5k | if (*s == end_quote) |
2844 | 68.1k | { |
2845 | 68.1k | *g.flush(s) = 0; |
2846 | | |
2847 | 68.1k | return s + 1; |
2848 | 68.1k | } |
2849 | 17.4k | else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) |
2850 | 3.49k | { |
2851 | 3.49k | if (*s == '\r') |
2852 | 3.06k | { |
2853 | 3.06k | *s++ = ' '; |
2854 | | |
2855 | 3.06k | if (*s == '\n') g.push(s, 1); |
2856 | 3.06k | } |
2857 | 434 | else *s++ = ' '; |
2858 | 3.49k | } |
2859 | 13.9k | else if (opt_escape::value && *s == '&') |
2860 | 13.1k | { |
2861 | 13.1k | s = strconv_escape(s, g); |
2862 | 13.1k | } |
2863 | 756 | else if (!*s) |
2864 | 270 | { |
2865 | 270 | return NULL; |
2866 | 270 | } |
2867 | 486 | else ++s; |
2868 | 85.5k | } |
2869 | 68.3k | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_attribute_impl<pugi::impl::(anonymous namespace)::opt_false>::parse_wconv(char*, char) pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_attribute_impl<pugi::impl::(anonymous namespace)::opt_true>::parse_wconv(char*, char) Line | Count | Source | 2836 | 68.3k | { | 2837 | 68.3k | gap g; | 2838 | | | 2839 | 85.5k | while (true) | 2840 | 85.5k | { | 2841 | 85.5k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr_ws)); | 2842 | | | 2843 | 85.5k | if (*s == end_quote) | 2844 | 68.1k | { | 2845 | 68.1k | *g.flush(s) = 0; | 2846 | | | 2847 | 68.1k | return s + 1; | 2848 | 68.1k | } | 2849 | 17.4k | else if (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) | 2850 | 3.49k | { | 2851 | 3.49k | if (*s == '\r') | 2852 | 3.06k | { | 2853 | 3.06k | *s++ = ' '; | 2854 | | | 2855 | 3.06k | if (*s == '\n') g.push(s, 1); | 2856 | 3.06k | } | 2857 | 434 | else *s++ = ' '; | 2858 | 3.49k | } | 2859 | 13.9k | else if (opt_escape::value && *s == '&') | 2860 | 13.1k | { | 2861 | 13.1k | s = strconv_escape(s, g); | 2862 | 13.1k | } | 2863 | 756 | else if (!*s) | 2864 | 270 | { | 2865 | 270 | return NULL; | 2866 | 270 | } | 2867 | 486 | else ++s; | 2868 | 85.5k | } | 2869 | 68.3k | } |
|
2870 | | |
2871 | | static char_t* parse_eol(char_t* s, char_t end_quote) |
2872 | 0 | { |
2873 | 0 | gap g; |
2874 | |
|
2875 | 0 | while (true) |
2876 | 0 | { |
2877 | 0 | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr)); |
2878 | |
|
2879 | 0 | if (*s == end_quote) |
2880 | 0 | { |
2881 | 0 | *g.flush(s) = 0; |
2882 | |
|
2883 | 0 | return s + 1; |
2884 | 0 | } |
2885 | 0 | else if (*s == '\r') |
2886 | 0 | { |
2887 | 0 | *s++ = '\n'; |
2888 | |
|
2889 | 0 | if (*s == '\n') g.push(s, 1); |
2890 | 0 | } |
2891 | 0 | else if (opt_escape::value && *s == '&') |
2892 | 0 | { |
2893 | 0 | s = strconv_escape(s, g); |
2894 | 0 | } |
2895 | 0 | else if (!*s) |
2896 | 0 | { |
2897 | 0 | return NULL; |
2898 | 0 | } |
2899 | 0 | else ++s; |
2900 | 0 | } |
2901 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_attribute_impl<pugi::impl::(anonymous namespace)::opt_false>::parse_eol(char*, char) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_attribute_impl<pugi::impl::(anonymous namespace)::opt_true>::parse_eol(char*, char) |
2902 | | |
2903 | | static char_t* parse_simple(char_t* s, char_t end_quote) |
2904 | 34.1k | { |
2905 | 34.1k | gap g; |
2906 | | |
2907 | 42.4k | while (true) |
2908 | 42.4k | { |
2909 | 42.4k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr)); |
2910 | | |
2911 | 42.4k | if (*s == end_quote) |
2912 | 34.0k | { |
2913 | 34.0k | *g.flush(s) = 0; |
2914 | | |
2915 | 34.0k | return s + 1; |
2916 | 34.0k | } |
2917 | 8.42k | else if (opt_escape::value && *s == '&') |
2918 | 0 | { |
2919 | 0 | s = strconv_escape(s, g); |
2920 | 0 | } |
2921 | 8.42k | else if (!*s) |
2922 | 127 | { |
2923 | 127 | return NULL; |
2924 | 127 | } |
2925 | 8.29k | else ++s; |
2926 | 42.4k | } |
2927 | 34.1k | } pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_attribute_impl<pugi::impl::(anonymous namespace)::opt_false>::parse_simple(char*, char) Line | Count | Source | 2904 | 34.1k | { | 2905 | 34.1k | gap g; | 2906 | | | 2907 | 42.4k | while (true) | 2908 | 42.4k | { | 2909 | 42.4k | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPE(ss, ct_parse_attr)); | 2910 | | | 2911 | 42.4k | if (*s == end_quote) | 2912 | 34.0k | { | 2913 | 34.0k | *g.flush(s) = 0; | 2914 | | | 2915 | 34.0k | return s + 1; | 2916 | 34.0k | } | 2917 | 8.42k | else if (opt_escape::value && *s == '&') | 2918 | 0 | { | 2919 | 0 | s = strconv_escape(s, g); | 2920 | 0 | } | 2921 | 8.42k | else if (!*s) | 2922 | 127 | { | 2923 | 127 | return NULL; | 2924 | 127 | } | 2925 | 8.29k | else ++s; | 2926 | 42.4k | } | 2927 | 34.1k | } |
Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::strconv_attribute_impl<pugi::impl::(anonymous namespace)::opt_true>::parse_simple(char*, char) |
2928 | | }; |
2929 | | |
2930 | | PUGI_IMPL_FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) |
2931 | 11.4k | { |
2932 | 11.4k | PUGI_IMPL_STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); |
2933 | | |
2934 | 11.4k | switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above |
2935 | 11.4k | { |
2936 | 3.81k | case 0: return strconv_attribute_impl<opt_false>::parse_simple; |
2937 | 0 | case 1: return strconv_attribute_impl<opt_true>::parse_simple; |
2938 | 0 | case 2: return strconv_attribute_impl<opt_false>::parse_eol; |
2939 | 0 | case 3: return strconv_attribute_impl<opt_true>::parse_eol; |
2940 | 0 | case 4: return strconv_attribute_impl<opt_false>::parse_wconv; |
2941 | 0 | case 5: return strconv_attribute_impl<opt_true>::parse_wconv; |
2942 | 0 | case 6: return strconv_attribute_impl<opt_false>::parse_wconv; |
2943 | 7.62k | case 7: return strconv_attribute_impl<opt_true>::parse_wconv; |
2944 | 0 | case 8: return strconv_attribute_impl<opt_false>::parse_wnorm; |
2945 | 0 | case 9: return strconv_attribute_impl<opt_true>::parse_wnorm; |
2946 | 0 | case 10: return strconv_attribute_impl<opt_false>::parse_wnorm; |
2947 | 0 | case 11: return strconv_attribute_impl<opt_true>::parse_wnorm; |
2948 | 0 | case 12: return strconv_attribute_impl<opt_false>::parse_wnorm; |
2949 | 0 | case 13: return strconv_attribute_impl<opt_true>::parse_wnorm; |
2950 | 0 | case 14: return strconv_attribute_impl<opt_false>::parse_wnorm; |
2951 | 0 | case 15: return strconv_attribute_impl<opt_true>::parse_wnorm; |
2952 | 0 | default: assert(false); return NULL; // unreachable |
2953 | 11.4k | } |
2954 | 11.4k | } |
2955 | | |
2956 | | inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) |
2957 | 12.9k | { |
2958 | 12.9k | xml_parse_result result; |
2959 | 12.9k | result.status = status; |
2960 | 12.9k | result.offset = offset; |
2961 | | |
2962 | 12.9k | return result; |
2963 | 12.9k | } |
2964 | | |
2965 | | struct xml_parser |
2966 | | { |
2967 | | xml_allocator* alloc; |
2968 | | char_t* error_offset; |
2969 | | xml_parse_status error_status; |
2970 | | |
2971 | 11.4k | xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(NULL), error_status(status_ok) |
2972 | 11.4k | { |
2973 | 11.4k | } |
2974 | | |
2975 | | // DOCTYPE consists of nested sections of the following possible types: |
2976 | | // <!-- ... -->, <? ... ?>, "...", '...' |
2977 | | // <![...]]> |
2978 | | // <!...> |
2979 | | // First group can not contain nested groups |
2980 | | // Second group can contain nested groups of the same type |
2981 | | // Third group can contain all other groups |
2982 | | char_t* parse_doctype_primitive(char_t* s) |
2983 | 2.20k | { |
2984 | 2.20k | if (*s == '"' || *s == '\'') |
2985 | 820 | { |
2986 | | // quoted string |
2987 | 820 | char_t ch = *s++; |
2988 | 820 | PUGI_IMPL_SCANFOR(*s == ch); |
2989 | 820 | if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); |
2990 | | |
2991 | 719 | s++; |
2992 | 719 | } |
2993 | 1.38k | else if (s[0] == '<' && s[1] == '?') |
2994 | 543 | { |
2995 | | // <? ... ?> |
2996 | 543 | s += 2; |
2997 | 543 | PUGI_IMPL_SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype |
2998 | 543 | if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); |
2999 | | |
3000 | 430 | s += 2; |
3001 | 430 | } |
3002 | 838 | else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') |
3003 | 706 | { |
3004 | 706 | s += 4; |
3005 | 706 | PUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype |
3006 | 706 | if (!*s) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); |
3007 | | |
3008 | 567 | s += 3; |
3009 | 567 | } |
3010 | 132 | else PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); |
3011 | | |
3012 | 1.71k | return s; |
3013 | 2.20k | } |
3014 | | |
3015 | | char_t* parse_doctype_ignore(char_t* s) |
3016 | 569 | { |
3017 | 569 | size_t depth = 0; |
3018 | | |
3019 | 569 | assert(s[0] == '<' && s[1] == '!' && s[2] == '['); |
3020 | 569 | s += 3; |
3021 | | |
3022 | 1.79M | while (*s) |
3023 | 1.79M | { |
3024 | 1.79M | if (s[0] == '<' && s[1] == '!' && s[2] == '[') |
3025 | 1.58M | { |
3026 | | // nested ignore section |
3027 | 1.58M | s += 3; |
3028 | 1.58M | depth++; |
3029 | 1.58M | } |
3030 | 205k | else if (s[0] == ']' && s[1] == ']' && s[2] == '>') |
3031 | 1.20k | { |
3032 | | // ignore section end |
3033 | 1.20k | s += 3; |
3034 | | |
3035 | 1.20k | if (depth == 0) |
3036 | 228 | return s; |
3037 | | |
3038 | 974 | depth--; |
3039 | 974 | } |
3040 | 203k | else s++; |
3041 | 1.79M | } |
3042 | | |
3043 | 569 | PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); |
3044 | 569 | } |
3045 | | |
3046 | | char_t* parse_doctype_group(char_t* s, char_t endch) |
3047 | 2.18k | { |
3048 | 2.18k | size_t depth = 0; |
3049 | | |
3050 | 2.18k | assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); |
3051 | 2.18k | s += 2; |
3052 | | |
3053 | 2.74M | while (*s) |
3054 | 2.74M | { |
3055 | 2.74M | if (s[0] == '<' && s[1] == '!' && s[2] != '-') |
3056 | 2.67M | { |
3057 | 2.67M | if (s[2] == '[') |
3058 | 569 | { |
3059 | | // ignore |
3060 | 569 | s = parse_doctype_ignore(s); |
3061 | 569 | if (!s) return s; |
3062 | 569 | } |
3063 | 2.67M | else |
3064 | 2.67M | { |
3065 | | // some control group |
3066 | 2.67M | s += 2; |
3067 | 2.67M | depth++; |
3068 | 2.67M | } |
3069 | 2.67M | } |
3070 | 65.5k | else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') |
3071 | 2.20k | { |
3072 | | // unknown tag (forbidden), or some primitive group |
3073 | 2.20k | s = parse_doctype_primitive(s); |
3074 | 2.20k | if (!s) return s; |
3075 | 2.20k | } |
3076 | 63.3k | else if (*s == '>') |
3077 | 3.30k | { |
3078 | 3.30k | if (depth == 0) |
3079 | 1.05k | return s; |
3080 | | |
3081 | 2.24k | depth--; |
3082 | 2.24k | s++; |
3083 | 2.24k | } |
3084 | 60.0k | else s++; |
3085 | 2.74M | } |
3086 | | |
3087 | 306 | if (depth != 0 || endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); |
3088 | | |
3089 | 0 | return s; |
3090 | 306 | } |
3091 | | |
3092 | | char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) |
3093 | 7.81k | { |
3094 | | // parse node contents, starting with exclamation mark |
3095 | 7.81k | ++s; |
3096 | | |
3097 | 7.81k | if (*s == '-') // '<!-...' |
3098 | 3.33k | { |
3099 | 3.33k | ++s; |
3100 | | |
3101 | 3.33k | if (*s == '-') // '<!--...' |
3102 | 3.29k | { |
3103 | 3.29k | ++s; |
3104 | | |
3105 | 3.29k | if (PUGI_IMPL_OPTSET(parse_comments)) |
3106 | 1.09k | { |
3107 | 1.09k | PUGI_IMPL_PUSHNODE(node_comment); // Append a new node on the tree. |
3108 | 1.09k | cursor->value = s; // Save the offset. |
3109 | 1.09k | } |
3110 | | |
3111 | 3.29k | if (PUGI_IMPL_OPTSET(parse_eol) && PUGI_IMPL_OPTSET(parse_comments)) |
3112 | 1.09k | { |
3113 | 1.09k | s = strconv_comment(s, endch); |
3114 | | |
3115 | 1.09k | if (!s) PUGI_IMPL_THROW_ERROR(status_bad_comment, cursor->value); |
3116 | 1.09k | } |
3117 | 2.20k | else |
3118 | 2.20k | { |
3119 | | // Scan for terminating '-->'. |
3120 | 2.20k | PUGI_IMPL_SCANFOR(s[0] == '-' && s[1] == '-' && PUGI_IMPL_ENDSWITH(s[2], '>')); |
3121 | 2.20k | PUGI_IMPL_CHECK_ERROR(status_bad_comment, s); |
3122 | | |
3123 | 1.89k | if (PUGI_IMPL_OPTSET(parse_comments)) |
3124 | 0 | *s = 0; // Zero-terminate this segment at the first terminating '-'. |
3125 | | |
3126 | 1.89k | s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. |
3127 | 1.89k | } |
3128 | 3.29k | } |
3129 | 39 | else PUGI_IMPL_THROW_ERROR(status_bad_comment, s); |
3130 | 3.33k | } |
3131 | 4.47k | else if (*s == '[') |
3132 | 2.04k | { |
3133 | | // '<![CDATA[...' |
3134 | 2.04k | if (*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[') |
3135 | 1.88k | { |
3136 | 1.88k | ++s; |
3137 | | |
3138 | 1.88k | if (PUGI_IMPL_OPTSET(parse_cdata)) |
3139 | 1.23k | { |
3140 | 1.23k | PUGI_IMPL_PUSHNODE(node_cdata); // Append a new node on the tree. |
3141 | 1.23k | cursor->value = s; // Save the offset. |
3142 | | |
3143 | 1.23k | if (PUGI_IMPL_OPTSET(parse_eol)) |
3144 | 1.23k | { |
3145 | 1.23k | s = strconv_cdata(s, endch); |
3146 | | |
3147 | 1.23k | if (!s) PUGI_IMPL_THROW_ERROR(status_bad_cdata, cursor->value); |
3148 | 1.23k | } |
3149 | 0 | else |
3150 | 0 | { |
3151 | | // Scan for terminating ']]>'. |
3152 | 0 | PUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')); |
3153 | 0 | PUGI_IMPL_CHECK_ERROR(status_bad_cdata, s); |
3154 | |
|
3155 | 0 | *s++ = 0; // Zero-terminate this segment. |
3156 | 0 | } |
3157 | 1.23k | } |
3158 | 644 | else // Flagged for discard, but we still have to scan for the terminator. |
3159 | 644 | { |
3160 | | // Scan for terminating ']]>'. |
3161 | 644 | PUGI_IMPL_SCANFOR(s[0] == ']' && s[1] == ']' && PUGI_IMPL_ENDSWITH(s[2], '>')); |
3162 | 644 | PUGI_IMPL_CHECK_ERROR(status_bad_cdata, s); |
3163 | | |
3164 | 498 | ++s; |
3165 | 498 | } |
3166 | | |
3167 | 1.46k | s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. |
3168 | 1.46k | } |
3169 | 167 | else PUGI_IMPL_THROW_ERROR(status_bad_cdata, s); |
3170 | 2.04k | } |
3171 | 2.42k | else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI_IMPL_ENDSWITH(s[6], 'E')) |
3172 | 2.19k | { |
3173 | 2.19k | s -= 2; |
3174 | | |
3175 | 2.19k | if (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_doctype, s); |
3176 | | |
3177 | 2.18k | char_t* mark = s + 9; |
3178 | | |
3179 | 2.18k | s = parse_doctype_group(s, endch); |
3180 | 2.18k | if (!s) return s; |
3181 | | |
3182 | 2.18k | assert((*s == 0 && endch == '>') || *s == '>'); |
3183 | 1.05k | if (*s) *s++ = 0; |
3184 | | |
3185 | 1.05k | if (PUGI_IMPL_OPTSET(parse_doctype)) |
3186 | 354 | { |
3187 | 354 | while (PUGI_IMPL_IS_CHARTYPE(*mark, ct_space)) ++mark; |
3188 | | |
3189 | 354 | PUGI_IMPL_PUSHNODE(node_doctype); |
3190 | | |
3191 | 354 | cursor->value = mark; |
3192 | 354 | } |
3193 | 1.05k | } |
3194 | 231 | else if (*s == 0 && endch == '-') PUGI_IMPL_THROW_ERROR(status_bad_comment, s); |
3195 | 231 | else if (*s == 0 && endch == '[') PUGI_IMPL_THROW_ERROR(status_bad_cdata, s); |
3196 | 231 | else PUGI_IMPL_THROW_ERROR(status_unrecognized_tag, s); |
3197 | | |
3198 | 5.36k | return s; |
3199 | 7.81k | } |
3200 | | |
3201 | | char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) |
3202 | 8.96k | { |
3203 | | // load into registers |
3204 | 8.96k | xml_node_struct* cursor = ref_cursor; |
3205 | 8.96k | char_t ch = 0; |
3206 | | |
3207 | | // parse node contents, starting with question mark |
3208 | 8.96k | ++s; |
3209 | | |
3210 | | // read PI target |
3211 | 8.96k | char_t* target = s; |
3212 | | |
3213 | 8.96k | if (!PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); |
3214 | | |
3215 | 8.91k | PUGI_IMPL_SCANWHILE(PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol)); |
3216 | 8.91k | PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); |
3217 | | |
3218 | | // determine node type; stricmp / strcasecmp is not portable |
3219 | 8.86k | bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; |
3220 | | |
3221 | 8.86k | if (declaration ? PUGI_IMPL_OPTSET(parse_declaration) : PUGI_IMPL_OPTSET(parse_pi)) |
3222 | 2.94k | { |
3223 | 2.94k | if (declaration) |
3224 | 1.35k | { |
3225 | | // disallow non top-level declarations |
3226 | 1.35k | if (cursor->parent) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); |
3227 | | |
3228 | 1.35k | PUGI_IMPL_PUSHNODE(node_declaration); |
3229 | 1.35k | } |
3230 | 1.59k | else |
3231 | 1.59k | { |
3232 | 1.59k | PUGI_IMPL_PUSHNODE(node_pi); |
3233 | 1.59k | } |
3234 | | |
3235 | 2.94k | cursor->name = target; |
3236 | | |
3237 | 2.94k | PUGI_IMPL_ENDSEG(); |
3238 | | |
3239 | | // parse value/attributes |
3240 | 2.94k | if (ch == '?') |
3241 | 1.41k | { |
3242 | | // empty node |
3243 | 1.41k | if (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_pi, s); |
3244 | 1.35k | s += (*s == '>'); |
3245 | | |
3246 | 1.35k | PUGI_IMPL_POPNODE(); |
3247 | 1.35k | } |
3248 | 1.52k | else if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) |
3249 | 1.26k | { |
3250 | 1.26k | PUGI_IMPL_SKIPWS(); |
3251 | | |
3252 | | // scan for tag end |
3253 | 1.26k | char_t* value = s; |
3254 | | |
3255 | 1.26k | PUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>')); |
3256 | 1.26k | PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); |
3257 | | |
3258 | 633 | if (declaration) |
3259 | 436 | { |
3260 | | // replace ending ? with / so that 'element' terminates properly |
3261 | 436 | *s = '/'; |
3262 | | |
3263 | | // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES |
3264 | 436 | s = value; |
3265 | 436 | } |
3266 | 197 | else |
3267 | 197 | { |
3268 | | // store value and step over > |
3269 | 197 | cursor->value = value; |
3270 | | |
3271 | 197 | PUGI_IMPL_POPNODE(); |
3272 | | |
3273 | 197 | PUGI_IMPL_ENDSEG(); |
3274 | | |
3275 | 197 | s += (*s == '>'); |
3276 | 197 | } |
3277 | 633 | } |
3278 | 254 | else PUGI_IMPL_THROW_ERROR(status_bad_pi, s); |
3279 | 2.94k | } |
3280 | 5.91k | else |
3281 | 5.91k | { |
3282 | | // scan for tag end |
3283 | 5.91k | PUGI_IMPL_SCANFOR(s[0] == '?' && PUGI_IMPL_ENDSWITH(s[1], '>')); |
3284 | 5.91k | PUGI_IMPL_CHECK_ERROR(status_bad_pi, s); |
3285 | | |
3286 | 4.44k | s += (s[1] == '>' ? 2 : 1); |
3287 | 4.44k | } |
3288 | | |
3289 | | // store from registers |
3290 | 6.43k | ref_cursor = cursor; |
3291 | | |
3292 | 6.43k | return s; |
3293 | 8.86k | } |
3294 | | |
3295 | | char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) |
3296 | 11.4k | { |
3297 | 11.4k | strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); |
3298 | 11.4k | strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); |
3299 | | |
3300 | 11.4k | char_t ch = 0; |
3301 | 11.4k | xml_node_struct* cursor = root; |
3302 | 11.4k | char_t* mark = s; |
3303 | 11.4k | char_t* merged_pcdata = s; |
3304 | | |
3305 | 473k | while (*s != 0) |
3306 | 472k | { |
3307 | 472k | if (*s == '<') |
3308 | 160k | { |
3309 | 160k | ++s; |
3310 | | |
3311 | 468k | LOC_TAG: |
3312 | 468k | if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' |
3313 | 450k | { |
3314 | 450k | PUGI_IMPL_PUSHNODE(node_element); // Append a new node to the tree. |
3315 | | |
3316 | 450k | cursor->name = s; |
3317 | | |
3318 | 450k | PUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. |
3319 | 450k | PUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over. |
3320 | | |
3321 | 450k | if (ch == '>') |
3322 | 297k | { |
3323 | | // end of tag |
3324 | 297k | } |
3325 | 153k | else if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) |
3326 | 151k | { |
3327 | 152k | LOC_ATTRIBUTES: |
3328 | 254k | while (true) |
3329 | 254k | { |
3330 | 254k | PUGI_IMPL_SKIPWS(); // Eat any whitespace. |
3331 | | |
3332 | 254k | if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) // <... #... |
3333 | 102k | { |
3334 | 102k | xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute. |
3335 | 102k | if (!a) PUGI_IMPL_THROW_ERROR(status_out_of_memory, s); |
3336 | | |
3337 | 102k | a->name = s; // Save the offset. |
3338 | | |
3339 | 102k | PUGI_IMPL_SCANWHILE_UNROLL(PUGI_IMPL_IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. |
3340 | 102k | PUGI_IMPL_ENDSEG(); // Save char in 'ch', terminate & step over. |
3341 | | |
3342 | 102k | if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) |
3343 | 232 | { |
3344 | 232 | PUGI_IMPL_SKIPWS(); // Eat any whitespace. |
3345 | | |
3346 | 232 | ch = *s; |
3347 | 232 | ++s; |
3348 | 232 | } |
3349 | | |
3350 | 102k | if (ch == '=') // '<... #=...' |
3351 | 102k | { |
3352 | 102k | PUGI_IMPL_SKIPWS(); // Eat any whitespace. |
3353 | | |
3354 | 102k | if (*s == '"' || *s == '\'') // '<... #="...' |
3355 | 102k | { |
3356 | 102k | ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. |
3357 | 102k | ++s; // Step over the quote. |
3358 | 102k | a->value = s; // Save the offset. |
3359 | | |
3360 | 102k | s = strconv_attribute(s, ch); |
3361 | | |
3362 | 102k | if (!s) PUGI_IMPL_THROW_ERROR(status_bad_attribute, a->value); |
3363 | | |
3364 | | // After this line the loop continues from the start; |
3365 | | // Whitespaces, / and > are ok, symbols and EOF are wrong, |
3366 | | // everything else will be detected |
3367 | 102k | if (PUGI_IMPL_IS_CHARTYPE(*s, ct_start_symbol)) PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); |
3368 | 102k | } |
3369 | 77 | else PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); |
3370 | 102k | } |
3371 | 259 | else PUGI_IMPL_THROW_ERROR(status_bad_attribute, s); |
3372 | 102k | } |
3373 | 151k | else if (*s == '/') |
3374 | 575 | { |
3375 | 575 | ++s; |
3376 | | |
3377 | 575 | if (*s == '>') |
3378 | 518 | { |
3379 | 518 | PUGI_IMPL_POPNODE(); |
3380 | 518 | s++; |
3381 | 518 | break; |
3382 | 518 | } |
3383 | 57 | else if (*s == 0 && endch == '>') |
3384 | 0 | { |
3385 | 0 | PUGI_IMPL_POPNODE(); |
3386 | 0 | break; |
3387 | 0 | } |
3388 | 57 | else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); |
3389 | 575 | } |
3390 | 150k | else if (*s == '>') |
3391 | 150k | { |
3392 | 150k | ++s; |
3393 | | |
3394 | 150k | break; |
3395 | 150k | } |
3396 | 188 | else if (*s == 0 && endch == '>') |
3397 | 0 | { |
3398 | 0 | break; |
3399 | 0 | } |
3400 | 188 | else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); |
3401 | 254k | } |
3402 | | |
3403 | | // !!! |
3404 | 152k | } |
3405 | 1.72k | else if (ch == '/') // '<#.../' |
3406 | 552 | { |
3407 | 552 | if (!PUGI_IMPL_ENDSWITH(*s, '>')) PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); |
3408 | | |
3409 | 499 | PUGI_IMPL_POPNODE(); // Pop. |
3410 | | |
3411 | 499 | s += (*s == '>'); |
3412 | 499 | } |
3413 | 1.17k | else if (ch == 0) |
3414 | 1.07k | { |
3415 | | // we stepped over null terminator, backtrack & handle closing tag |
3416 | 1.07k | --s; |
3417 | | |
3418 | 1.07k | if (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); |
3419 | 1.07k | } |
3420 | 94 | else PUGI_IMPL_THROW_ERROR(status_bad_start_element, s); |
3421 | 450k | } |
3422 | 17.5k | else if (*s == '/') |
3423 | 390 | { |
3424 | 390 | ++s; |
3425 | | |
3426 | 390 | mark = s; |
3427 | | |
3428 | 390 | char_t* name = cursor->name; |
3429 | 390 | if (!name) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); |
3430 | | |
3431 | 375 | while (PUGI_IMPL_IS_CHARTYPE(*s, ct_symbol)) |
3432 | 522 | { |
3433 | 522 | if (*s++ != *name++) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); |
3434 | 522 | } |
3435 | | |
3436 | 369 | if (*name) |
3437 | 42 | { |
3438 | 42 | if (*s == 0 && name[0] == endch && name[1] == 0) PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); |
3439 | 42 | else PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, mark); |
3440 | 42 | } |
3441 | | |
3442 | 327 | PUGI_IMPL_POPNODE(); // Pop. |
3443 | | |
3444 | 327 | PUGI_IMPL_SKIPWS(); |
3445 | | |
3446 | 327 | if (*s == 0) |
3447 | 41 | { |
3448 | 41 | if (endch != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); |
3449 | 41 | } |
3450 | 286 | else |
3451 | 286 | { |
3452 | 286 | if (*s != '>') PUGI_IMPL_THROW_ERROR(status_bad_end_element, s); |
3453 | 237 | ++s; |
3454 | 237 | } |
3455 | 327 | } |
3456 | 17.2k | else if (*s == '?') // '<?...' |
3457 | 8.96k | { |
3458 | 8.96k | s = parse_question(s, cursor, optmsk, endch); |
3459 | 8.96k | if (!s) return s; |
3460 | | |
3461 | 8.96k | assert(cursor); |
3462 | 6.43k | if (PUGI_IMPL_NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES; |
3463 | 6.43k | } |
3464 | 8.24k | else if (*s == '!') // '<!...' |
3465 | 7.81k | { |
3466 | 7.81k | s = parse_exclamation(s, cursor, optmsk, endch); |
3467 | 7.81k | if (!s) return s; |
3468 | 7.81k | } |
3469 | 431 | else if (*s == 0 && endch == '?') PUGI_IMPL_THROW_ERROR(status_bad_pi, s); |
3470 | 431 | else PUGI_IMPL_THROW_ERROR(status_unrecognized_tag, s); |
3471 | 468k | } |
3472 | 311k | else |
3473 | 311k | { |
3474 | 311k | mark = s; // Save this offset while searching for a terminator. |
3475 | | |
3476 | 311k | PUGI_IMPL_SKIPWS(); // Eat whitespace if no genuine PCDATA here. |
3477 | | |
3478 | 311k | if (*s == '<' || !*s) |
3479 | 1.71k | { |
3480 | | // We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one |
3481 | 1.71k | assert(mark != s); |
3482 | | |
3483 | 1.71k | if (!PUGI_IMPL_OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI_IMPL_OPTSET(parse_trim_pcdata)) |
3484 | 1.71k | { |
3485 | 1.71k | continue; |
3486 | 1.71k | } |
3487 | 0 | else if (PUGI_IMPL_OPTSET(parse_ws_pcdata_single)) |
3488 | 0 | { |
3489 | 0 | if (s[0] != '<' || s[1] != '/' || cursor->first_child) continue; |
3490 | 0 | } |
3491 | 1.71k | } |
3492 | | |
3493 | 310k | if (!PUGI_IMPL_OPTSET(parse_trim_pcdata)) |
3494 | 310k | s = mark; |
3495 | | |
3496 | 310k | if (cursor->parent || PUGI_IMPL_OPTSET(parse_fragment)) |
3497 | 308k | { |
3498 | 308k | char_t* parsed_pcdata = s; |
3499 | | |
3500 | 308k | s = strconv_pcdata(s); |
3501 | | |
3502 | 308k | if (PUGI_IMPL_OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value) |
3503 | 0 | { |
3504 | 0 | cursor->value = parsed_pcdata; // Save the offset. |
3505 | 0 | } |
3506 | 308k | else if (PUGI_IMPL_OPTSET(parse_merge_pcdata) && cursor->first_child && PUGI_IMPL_NODETYPE(cursor->first_child->prev_sibling_c) == node_pcdata) |
3507 | 0 | { |
3508 | 0 | assert(merged_pcdata >= cursor->first_child->prev_sibling_c->value); |
3509 | | |
3510 | | // Catch up to the end of last parsed value; only needed for the first fragment. |
3511 | 0 | merged_pcdata += strlength(merged_pcdata); |
3512 | |
|
3513 | 0 | size_t length = strlength(parsed_pcdata); |
3514 | | |
3515 | | // Must use memmove instead of memcpy as this move may overlap |
3516 | 0 | memmove(merged_pcdata, parsed_pcdata, (length + 1) * sizeof(char_t)); |
3517 | 0 | merged_pcdata += length; |
3518 | 0 | } |
3519 | 308k | else |
3520 | 308k | { |
3521 | 308k | xml_node_struct* prev_cursor = cursor; |
3522 | 308k | PUGI_IMPL_PUSHNODE(node_pcdata); // Append a new node on the tree. |
3523 | | |
3524 | 308k | cursor->value = parsed_pcdata; // Save the offset. |
3525 | 308k | merged_pcdata = parsed_pcdata; // Used for parse_merge_pcdata above, cheaper to save unconditionally |
3526 | | |
3527 | 308k | cursor = prev_cursor; // Pop since this is a standalone. |
3528 | 308k | } |
3529 | | |
3530 | 308k | if (!*s) break; |
3531 | 308k | } |
3532 | 1.52k | else |
3533 | 1.52k | { |
3534 | 1.52k | PUGI_IMPL_SCANFOR(*s == '<'); // '...<' |
3535 | 1.52k | if (!*s) break; |
3536 | | |
3537 | 738 | ++s; |
3538 | 738 | } |
3539 | | |
3540 | | // We're after '<' |
3541 | 307k | goto LOC_TAG; |
3542 | 310k | } |
3543 | 472k | } |
3544 | | |
3545 | | // check that last tag is closed |
3546 | 3.65k | if (cursor != root) PUGI_IMPL_THROW_ERROR(status_end_element_mismatch, s); |
3547 | | |
3548 | 1.55k | return s; |
3549 | 3.65k | } |
3550 | | |
3551 | | #ifdef PUGIXML_WCHAR_MODE |
3552 | | static char_t* parse_skip_bom(char_t* s) |
3553 | | { |
3554 | | unsigned int bom = 0xfeff; |
3555 | | return (s[0] == static_cast<wchar_t>(bom)) ? s + 1 : s; |
3556 | | } |
3557 | | #else |
3558 | | static char_t* parse_skip_bom(char_t* s) |
3559 | 11.4k | { |
3560 | 11.4k | return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; |
3561 | 11.4k | } |
3562 | | #endif |
3563 | | |
3564 | | static bool has_element_node_siblings(xml_node_struct* node) |
3565 | 1.55k | { |
3566 | 4.02k | while (node) |
3567 | 2.55k | { |
3568 | 2.55k | if (PUGI_IMPL_NODETYPE(node) == node_element) return true; |
3569 | | |
3570 | 2.47k | node = node->next_sibling; |
3571 | 2.47k | } |
3572 | | |
3573 | 1.47k | return false; |
3574 | 1.55k | } |
3575 | | |
3576 | | static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) |
3577 | 11.4k | { |
3578 | | // early-out for empty documents |
3579 | 11.4k | if (length == 0) |
3580 | 0 | return make_parse_result(PUGI_IMPL_OPTSET(parse_fragment) ? status_ok : status_no_document_element); |
3581 | | |
3582 | | // get last child of the root before parsing |
3583 | 11.4k | xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : NULL; |
3584 | | |
3585 | | // create parser on stack |
3586 | 11.4k | xml_parser parser(static_cast<xml_allocator*>(xmldoc)); |
3587 | | |
3588 | | // save last character and make buffer zero-terminated (speeds up parsing) |
3589 | 11.4k | char_t endch = buffer[length - 1]; |
3590 | 11.4k | buffer[length - 1] = 0; |
3591 | | |
3592 | | // skip BOM to make sure it does not end up as part of parse output |
3593 | 11.4k | char_t* buffer_data = parse_skip_bom(buffer); |
3594 | | |
3595 | | // perform actual parsing |
3596 | 11.4k | parser.parse_tree(buffer_data, root, optmsk, endch); |
3597 | | |
3598 | 11.4k | xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); |
3599 | 11.4k | assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length); |
3600 | | |
3601 | 11.4k | if (result) |
3602 | 1.55k | { |
3603 | | // since we removed last character, we have to handle the only possible false positive (stray <) |
3604 | 1.55k | if (endch == '<') |
3605 | 0 | return make_parse_result(status_unrecognized_tag, length - 1); |
3606 | | |
3607 | | // check if there are any element nodes parsed |
3608 | 1.55k | xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child + 0; |
3609 | | |
3610 | 1.55k | if (!PUGI_IMPL_OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) |
3611 | 1.47k | return make_parse_result(status_no_document_element, length - 1); |
3612 | 1.55k | } |
3613 | 9.87k | else |
3614 | 9.87k | { |
3615 | | // roll back offset if it occurs on a null terminator in the source buffer |
3616 | 9.87k | if (result.offset > 0 && static_cast<size_t>(result.offset) == length - 1 && endch == 0) |
3617 | 6.81k | result.offset--; |
3618 | 9.87k | } |
3619 | | |
3620 | 9.95k | return result; |
3621 | 11.4k | } |
3622 | | }; |
3623 | | |
3624 | | // Output facilities |
3625 | | PUGI_IMPL_FN xml_encoding get_write_native_encoding() |
3626 | 0 | { |
3627 | | #ifdef PUGIXML_WCHAR_MODE |
3628 | | return get_wchar_encoding(); |
3629 | | #else |
3630 | 0 | return encoding_utf8; |
3631 | 0 | #endif |
3632 | 0 | } |
3633 | | |
3634 | | PUGI_IMPL_FN xml_encoding get_write_encoding(xml_encoding encoding) |
3635 | 0 | { |
3636 | | // replace wchar encoding with utf implementation |
3637 | 0 | if (encoding == encoding_wchar) return get_wchar_encoding(); |
3638 | | |
3639 | | // replace utf16 encoding with utf16 with specific endianness |
3640 | 0 | if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; |
3641 | | |
3642 | | // replace utf32 encoding with utf32 with specific endianness |
3643 | 0 | if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; |
3644 | | |
3645 | | // only do autodetection if no explicit encoding is requested |
3646 | 0 | if (encoding != encoding_auto) return encoding; |
3647 | | |
3648 | | // assume utf8 encoding |
3649 | 0 | return encoding_utf8; |
3650 | 0 | } |
3651 | | |
3652 | | template <typename D, typename T> PUGI_IMPL_FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) |
3653 | 0 | { |
3654 | 0 | PUGI_IMPL_STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); |
3655 | |
|
3656 | 0 | typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T()); |
3657 | |
|
3658 | 0 | return static_cast<size_t>(end - dest) * sizeof(*dest); |
3659 | 0 | } |
3660 | | |
3661 | | template <typename D, typename T> PUGI_IMPL_FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) |
3662 | 0 | { |
3663 | 0 | PUGI_IMPL_STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); |
3664 | |
|
3665 | 0 | typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T()); |
3666 | |
|
3667 | 0 | if (opt_swap) |
3668 | 0 | { |
3669 | 0 | for (typename T::value_type i = dest; i != end; ++i) |
3670 | 0 | *i = endian_swap(*i); |
3671 | 0 | } |
3672 | |
|
3673 | 0 | return static_cast<size_t>(end - dest) * sizeof(*dest); |
3674 | 0 | } Unexecuted instantiation: pugixml.cpp:unsigned long pugi::impl::(anonymous namespace)::convert_buffer_output_generic<pugi::impl::(anonymous namespace)::utf8_decoder, pugi::impl::(anonymous namespace)::utf16_writer>(pugi::impl::(anonymous namespace)::utf16_writer::value_type, char const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_decoder, pugi::impl::(anonymous namespace)::utf16_writer, bool) Unexecuted instantiation: pugixml.cpp:unsigned long pugi::impl::(anonymous namespace)::convert_buffer_output_generic<pugi::impl::(anonymous namespace)::utf8_decoder, pugi::impl::(anonymous namespace)::utf32_writer>(pugi::impl::(anonymous namespace)::utf32_writer::value_type, char const*, unsigned long, pugi::impl::(anonymous namespace)::utf8_decoder, pugi::impl::(anonymous namespace)::utf32_writer, bool) |
3675 | | |
3676 | | #ifdef PUGIXML_WCHAR_MODE |
3677 | | PUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length) |
3678 | | { |
3679 | | if (length < 1) return 0; |
3680 | | |
3681 | | // discard last character if it's the lead of a surrogate pair |
3682 | | return (sizeof(wchar_t) == 2 && static_cast<unsigned int>(static_cast<uint16_t>(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; |
3683 | | } |
3684 | | |
3685 | | PUGI_IMPL_FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) |
3686 | | { |
3687 | | // only endian-swapping is required |
3688 | | if (need_endian_swap_utf(encoding, get_wchar_encoding())) |
3689 | | { |
3690 | | convert_wchar_endian_swap(r_char, data, length); |
3691 | | |
3692 | | return length * sizeof(char_t); |
3693 | | } |
3694 | | |
3695 | | // convert to utf8 |
3696 | | if (encoding == encoding_utf8) |
3697 | | return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer()); |
3698 | | |
3699 | | // convert to utf16 |
3700 | | if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) |
3701 | | { |
3702 | | xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; |
3703 | | |
3704 | | return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding); |
3705 | | } |
3706 | | |
3707 | | // convert to utf32 |
3708 | | if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) |
3709 | | { |
3710 | | xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; |
3711 | | |
3712 | | return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding); |
3713 | | } |
3714 | | |
3715 | | // convert to latin1 |
3716 | | if (encoding == encoding_latin1) |
3717 | | return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer()); |
3718 | | |
3719 | | assert(false && "Invalid encoding"); // unreachable |
3720 | | return 0; |
3721 | | } |
3722 | | #else |
3723 | | PUGI_IMPL_FN size_t get_valid_length(const char_t* data, size_t length) |
3724 | 0 | { |
3725 | 0 | if (length < 5) return 0; |
3726 | | |
3727 | 0 | for (size_t i = 1; i <= 4; ++i) |
3728 | 0 | { |
3729 | 0 | uint8_t ch = static_cast<uint8_t>(data[length - i]); |
3730 | | |
3731 | | // either a standalone character or a leading one |
3732 | 0 | if ((ch & 0xc0) != 0x80) return length - i; |
3733 | 0 | } |
3734 | | |
3735 | | // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk |
3736 | 0 | return length; |
3737 | 0 | } |
3738 | | |
3739 | | PUGI_IMPL_FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) |
3740 | 0 | { |
3741 | 0 | if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) |
3742 | 0 | { |
3743 | 0 | xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; |
3744 | |
|
3745 | 0 | return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding); |
3746 | 0 | } |
3747 | | |
3748 | 0 | if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) |
3749 | 0 | { |
3750 | 0 | xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; |
3751 | |
|
3752 | 0 | return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding); |
3753 | 0 | } |
3754 | | |
3755 | 0 | if (encoding == encoding_latin1) |
3756 | 0 | return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer()); |
3757 | | |
3758 | 0 | assert(false && "Invalid encoding"); // unreachable |
3759 | 0 | return 0; |
3760 | 0 | } |
3761 | | #endif |
3762 | | |
3763 | | class xml_buffered_writer |
3764 | | { |
3765 | | xml_buffered_writer(const xml_buffered_writer&); |
3766 | | xml_buffered_writer& operator=(const xml_buffered_writer&); |
3767 | | |
3768 | | public: |
3769 | 0 | xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) |
3770 | 0 | { |
3771 | 0 | PUGI_IMPL_STATIC_ASSERT(bufcapacity >= 8); |
3772 | 0 | } |
3773 | | |
3774 | | size_t flush() |
3775 | 0 | { |
3776 | 0 | flush(buffer, bufsize); |
3777 | 0 | bufsize = 0; |
3778 | 0 | return 0; |
3779 | 0 | } |
3780 | | |
3781 | | void flush(const char_t* data, size_t size) |
3782 | 0 | { |
3783 | 0 | if (size == 0) return; |
3784 | | |
3785 | | // fast path, just write data |
3786 | 0 | if (encoding == get_write_native_encoding()) |
3787 | 0 | writer.write(data, size * sizeof(char_t)); |
3788 | 0 | else |
3789 | 0 | { |
3790 | | // convert chunk |
3791 | 0 | size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); |
3792 | 0 | assert(result <= sizeof(scratch)); |
3793 | | |
3794 | | // write data |
3795 | 0 | writer.write(scratch.data_u8, result); |
3796 | 0 | } |
3797 | 0 | } |
3798 | | |
3799 | | void write_direct(const char_t* data, size_t length) |
3800 | 0 | { |
3801 | | // flush the remaining buffer contents |
3802 | 0 | flush(); |
3803 | | |
3804 | | // handle large chunks |
3805 | 0 | if (length > bufcapacity) |
3806 | 0 | { |
3807 | 0 | if (encoding == get_write_native_encoding()) |
3808 | 0 | { |
3809 | | // fast path, can just write data chunk |
3810 | 0 | writer.write(data, length * sizeof(char_t)); |
3811 | 0 | return; |
3812 | 0 | } |
3813 | | |
3814 | | // need to convert in suitable chunks |
3815 | 0 | while (length > bufcapacity) |
3816 | 0 | { |
3817 | | // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer |
3818 | | // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) |
3819 | 0 | size_t chunk_size = get_valid_length(data, bufcapacity); |
3820 | 0 | assert(chunk_size); |
3821 | | |
3822 | | // convert chunk and write |
3823 | 0 | flush(data, chunk_size); |
3824 | | |
3825 | | // iterate |
3826 | 0 | data += chunk_size; |
3827 | 0 | length -= chunk_size; |
3828 | 0 | } |
3829 | | |
3830 | | // small tail is copied below |
3831 | 0 | bufsize = 0; |
3832 | 0 | } |
3833 | | |
3834 | 0 | memcpy(buffer + bufsize, data, length * sizeof(char_t)); |
3835 | 0 | bufsize += length; |
3836 | 0 | } |
3837 | | |
3838 | | void write_buffer(const char_t* data, size_t length) |
3839 | 0 | { |
3840 | 0 | size_t offset = bufsize; |
3841 | |
|
3842 | 0 | if (offset + length <= bufcapacity) |
3843 | 0 | { |
3844 | 0 | memcpy(buffer + offset, data, length * sizeof(char_t)); |
3845 | 0 | bufsize = offset + length; |
3846 | 0 | } |
3847 | 0 | else |
3848 | 0 | { |
3849 | 0 | write_direct(data, length); |
3850 | 0 | } |
3851 | 0 | } |
3852 | | |
3853 | | void write_string(const char_t* data) |
3854 | 0 | { |
3855 | | // write the part of the string that fits in the buffer |
3856 | 0 | size_t offset = bufsize; |
3857 | |
|
3858 | 0 | while (*data && offset < bufcapacity) |
3859 | 0 | buffer[offset++] = *data++; |
3860 | | |
3861 | | // write the rest |
3862 | 0 | if (offset < bufcapacity) |
3863 | 0 | { |
3864 | 0 | bufsize = offset; |
3865 | 0 | } |
3866 | 0 | else |
3867 | 0 | { |
3868 | | // backtrack a bit if we have split the codepoint |
3869 | 0 | size_t length = offset - bufsize; |
3870 | 0 | size_t extra = length - get_valid_length(data - length, length); |
3871 | |
|
3872 | 0 | bufsize = offset - extra; |
3873 | |
|
3874 | 0 | write_direct(data - extra, strlength(data) + extra); |
3875 | 0 | } |
3876 | 0 | } |
3877 | | |
3878 | | void write(char_t d0) |
3879 | 0 | { |
3880 | 0 | size_t offset = bufsize; |
3881 | 0 | if (offset > bufcapacity - 1) offset = flush(); |
3882 | |
|
3883 | 0 | buffer[offset + 0] = d0; |
3884 | 0 | bufsize = offset + 1; |
3885 | 0 | } |
3886 | | |
3887 | | void write(char_t d0, char_t d1) |
3888 | 0 | { |
3889 | 0 | size_t offset = bufsize; |
3890 | 0 | if (offset > bufcapacity - 2) offset = flush(); |
3891 | |
|
3892 | 0 | buffer[offset + 0] = d0; |
3893 | 0 | buffer[offset + 1] = d1; |
3894 | 0 | bufsize = offset + 2; |
3895 | 0 | } |
3896 | | |
3897 | | void write(char_t d0, char_t d1, char_t d2) |
3898 | 0 | { |
3899 | 0 | size_t offset = bufsize; |
3900 | 0 | if (offset > bufcapacity - 3) offset = flush(); |
3901 | |
|
3902 | 0 | buffer[offset + 0] = d0; |
3903 | 0 | buffer[offset + 1] = d1; |
3904 | 0 | buffer[offset + 2] = d2; |
3905 | 0 | bufsize = offset + 3; |
3906 | 0 | } |
3907 | | |
3908 | | void write(char_t d0, char_t d1, char_t d2, char_t d3) |
3909 | 0 | { |
3910 | 0 | size_t offset = bufsize; |
3911 | 0 | if (offset > bufcapacity - 4) offset = flush(); |
3912 | |
|
3913 | 0 | buffer[offset + 0] = d0; |
3914 | 0 | buffer[offset + 1] = d1; |
3915 | 0 | buffer[offset + 2] = d2; |
3916 | 0 | buffer[offset + 3] = d3; |
3917 | 0 | bufsize = offset + 4; |
3918 | 0 | } |
3919 | | |
3920 | | void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) |
3921 | 0 | { |
3922 | 0 | size_t offset = bufsize; |
3923 | 0 | if (offset > bufcapacity - 5) offset = flush(); |
3924 | |
|
3925 | 0 | buffer[offset + 0] = d0; |
3926 | 0 | buffer[offset + 1] = d1; |
3927 | 0 | buffer[offset + 2] = d2; |
3928 | 0 | buffer[offset + 3] = d3; |
3929 | 0 | buffer[offset + 4] = d4; |
3930 | 0 | bufsize = offset + 5; |
3931 | 0 | } |
3932 | | |
3933 | | void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) |
3934 | 0 | { |
3935 | 0 | size_t offset = bufsize; |
3936 | 0 | if (offset > bufcapacity - 6) offset = flush(); |
3937 | |
|
3938 | 0 | buffer[offset + 0] = d0; |
3939 | 0 | buffer[offset + 1] = d1; |
3940 | 0 | buffer[offset + 2] = d2; |
3941 | 0 | buffer[offset + 3] = d3; |
3942 | 0 | buffer[offset + 4] = d4; |
3943 | 0 | buffer[offset + 5] = d5; |
3944 | 0 | bufsize = offset + 6; |
3945 | 0 | } |
3946 | | |
3947 | | // utf8 maximum expansion: x4 (-> utf32) |
3948 | | // utf16 maximum expansion: x2 (-> utf32) |
3949 | | // utf32 maximum expansion: x1 |
3950 | | enum |
3951 | | { |
3952 | | bufcapacitybytes = |
3953 | | #ifdef PUGIXML_MEMORY_OUTPUT_STACK |
3954 | | PUGIXML_MEMORY_OUTPUT_STACK |
3955 | | #else |
3956 | | 10240 |
3957 | | #endif |
3958 | | , |
3959 | | bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) |
3960 | | }; |
3961 | | |
3962 | | char_t buffer[bufcapacity]; |
3963 | | |
3964 | | union |
3965 | | { |
3966 | | uint8_t data_u8[4 * bufcapacity]; |
3967 | | uint16_t data_u16[2 * bufcapacity]; |
3968 | | uint32_t data_u32[bufcapacity]; |
3969 | | char_t data_char[bufcapacity]; |
3970 | | } scratch; |
3971 | | |
3972 | | xml_writer& writer; |
3973 | | size_t bufsize; |
3974 | | xml_encoding encoding; |
3975 | | }; |
3976 | | |
3977 | | PUGI_IMPL_FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) |
3978 | 0 | { |
3979 | 0 | while (*s) |
3980 | 0 | { |
3981 | 0 | const char_t* prev = s; |
3982 | | |
3983 | | // While *s is a usual symbol |
3984 | 0 | PUGI_IMPL_SCANWHILE_UNROLL(!PUGI_IMPL_IS_CHARTYPEX(ss, type)); |
3985 | |
|
3986 | 0 | writer.write_buffer(prev, static_cast<size_t>(s - prev)); |
3987 | |
|
3988 | 0 | switch (*s) |
3989 | 0 | { |
3990 | 0 | case 0: break; |
3991 | 0 | case '&': |
3992 | 0 | writer.write('&', 'a', 'm', 'p', ';'); |
3993 | 0 | ++s; |
3994 | 0 | break; |
3995 | 0 | case '<': |
3996 | 0 | writer.write('&', 'l', 't', ';'); |
3997 | 0 | ++s; |
3998 | 0 | break; |
3999 | 0 | case '>': |
4000 | 0 | writer.write('&', 'g', 't', ';'); |
4001 | 0 | ++s; |
4002 | 0 | break; |
4003 | 0 | case '"': |
4004 | 0 | if (flags & format_attribute_single_quote) |
4005 | 0 | writer.write('"'); |
4006 | 0 | else |
4007 | 0 | writer.write('&', 'q', 'u', 'o', 't', ';'); |
4008 | 0 | ++s; |
4009 | 0 | break; |
4010 | 0 | case '\'': |
4011 | 0 | if (flags & format_attribute_single_quote) |
4012 | 0 | writer.write('&', 'a', 'p', 'o', 's', ';'); |
4013 | 0 | else |
4014 | 0 | writer.write('\''); |
4015 | 0 | ++s; |
4016 | 0 | break; |
4017 | 0 | default: // s is not a usual symbol |
4018 | 0 | { |
4019 | 0 | unsigned int ch = static_cast<unsigned int>(*s++); |
4020 | 0 | assert(ch < 32); |
4021 | | |
4022 | 0 | if (!(flags & format_skip_control_chars)) |
4023 | 0 | writer.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';'); |
4024 | 0 | } |
4025 | 0 | } |
4026 | 0 | } |
4027 | 0 | } |
4028 | | |
4029 | | PUGI_IMPL_FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) |
4030 | 0 | { |
4031 | 0 | if (flags & format_no_escapes) |
4032 | 0 | writer.write_string(s); |
4033 | 0 | else |
4034 | 0 | text_output_escaped(writer, s, type, flags); |
4035 | 0 | } |
4036 | | |
4037 | | PUGI_IMPL_FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) |
4038 | 0 | { |
4039 | 0 | do |
4040 | 0 | { |
4041 | 0 | writer.write('<', '!', '[', 'C', 'D'); |
4042 | 0 | writer.write('A', 'T', 'A', '['); |
4043 | |
|
4044 | 0 | const char_t* prev = s; |
4045 | | |
4046 | | // look for ]]> sequence - we can't output it as is since it terminates CDATA |
4047 | 0 | while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; |
4048 | | |
4049 | | // skip ]] if we stopped at ]]>, > will go to the next CDATA section |
4050 | 0 | if (*s) s += 2; |
4051 | |
|
4052 | 0 | writer.write_buffer(prev, static_cast<size_t>(s - prev)); |
4053 | |
|
4054 | 0 | writer.write(']', ']', '>'); |
4055 | 0 | } |
4056 | 0 | while (*s); |
4057 | 0 | } |
4058 | | |
4059 | | PUGI_IMPL_FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) |
4060 | 0 | { |
4061 | 0 | switch (indent_length) |
4062 | 0 | { |
4063 | 0 | case 1: |
4064 | 0 | { |
4065 | 0 | for (unsigned int i = 0; i < depth; ++i) |
4066 | 0 | writer.write(indent[0]); |
4067 | 0 | break; |
4068 | 0 | } |
4069 | | |
4070 | 0 | case 2: |
4071 | 0 | { |
4072 | 0 | for (unsigned int i = 0; i < depth; ++i) |
4073 | 0 | writer.write(indent[0], indent[1]); |
4074 | 0 | break; |
4075 | 0 | } |
4076 | | |
4077 | 0 | case 3: |
4078 | 0 | { |
4079 | 0 | for (unsigned int i = 0; i < depth; ++i) |
4080 | 0 | writer.write(indent[0], indent[1], indent[2]); |
4081 | 0 | break; |
4082 | 0 | } |
4083 | | |
4084 | 0 | case 4: |
4085 | 0 | { |
4086 | 0 | for (unsigned int i = 0; i < depth; ++i) |
4087 | 0 | writer.write(indent[0], indent[1], indent[2], indent[3]); |
4088 | 0 | break; |
4089 | 0 | } |
4090 | | |
4091 | 0 | default: |
4092 | 0 | { |
4093 | 0 | for (unsigned int i = 0; i < depth; ++i) |
4094 | 0 | writer.write_buffer(indent, indent_length); |
4095 | 0 | } |
4096 | 0 | } |
4097 | 0 | } |
4098 | | |
4099 | | PUGI_IMPL_FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) |
4100 | 0 | { |
4101 | 0 | writer.write('<', '!', '-', '-'); |
4102 | |
|
4103 | 0 | while (*s) |
4104 | 0 | { |
4105 | 0 | const char_t* prev = s; |
4106 | | |
4107 | | // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body |
4108 | 0 | while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s; |
4109 | |
|
4110 | 0 | writer.write_buffer(prev, static_cast<size_t>(s - prev)); |
4111 | |
|
4112 | 0 | if (*s) |
4113 | 0 | { |
4114 | 0 | assert(*s == '-'); |
4115 | | |
4116 | 0 | writer.write('-', ' '); |
4117 | 0 | ++s; |
4118 | 0 | } |
4119 | 0 | } |
4120 | | |
4121 | 0 | writer.write('-', '-', '>'); |
4122 | 0 | } |
4123 | | |
4124 | | PUGI_IMPL_FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) |
4125 | 0 | { |
4126 | 0 | while (*s) |
4127 | 0 | { |
4128 | 0 | const char_t* prev = s; |
4129 | | |
4130 | | // look for ?> sequence - we can't output it since ?> terminates PI |
4131 | 0 | while (*s && !(s[0] == '?' && s[1] == '>')) ++s; |
4132 | |
|
4133 | 0 | writer.write_buffer(prev, static_cast<size_t>(s - prev)); |
4134 | |
|
4135 | 0 | if (*s) |
4136 | 0 | { |
4137 | 0 | assert(s[0] == '?' && s[1] == '>'); |
4138 | | |
4139 | 0 | writer.write('?', ' ', '>'); |
4140 | 0 | s += 2; |
4141 | 0 | } |
4142 | 0 | } |
4143 | 0 | } |
4144 | | |
4145 | | PUGI_IMPL_FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) |
4146 | 0 | { |
4147 | 0 | const char_t* default_name = PUGIXML_TEXT(":anonymous"); |
4148 | 0 | const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"'; |
4149 | |
|
4150 | 0 | for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) |
4151 | 0 | { |
4152 | 0 | if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes) |
4153 | 0 | { |
4154 | 0 | writer.write('\n'); |
4155 | |
|
4156 | 0 | text_output_indent(writer, indent, indent_length, depth + 1); |
4157 | 0 | } |
4158 | 0 | else |
4159 | 0 | { |
4160 | 0 | writer.write(' '); |
4161 | 0 | } |
4162 | |
|
4163 | 0 | writer.write_string(a->name ? a->name + 0 : default_name); |
4164 | 0 | writer.write('=', enquotation_char); |
4165 | |
|
4166 | 0 | if (a->value) |
4167 | 0 | text_output(writer, a->value, ctx_special_attr, flags); |
4168 | |
|
4169 | 0 | writer.write(enquotation_char); |
4170 | 0 | } |
4171 | 0 | } |
4172 | | |
4173 | | PUGI_IMPL_FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) |
4174 | 0 | { |
4175 | 0 | const char_t* default_name = PUGIXML_TEXT(":anonymous"); |
4176 | 0 | const char_t* name = node->name ? node->name + 0 : default_name; |
4177 | |
|
4178 | 0 | writer.write('<'); |
4179 | 0 | writer.write_string(name); |
4180 | |
|
4181 | 0 | if (node->first_attribute) |
4182 | 0 | node_output_attributes(writer, node, indent, indent_length, flags, depth); |
4183 | | |
4184 | | // element nodes can have value if parse_embed_pcdata was used |
4185 | 0 | if (!node->value) |
4186 | 0 | { |
4187 | 0 | if (!node->first_child) |
4188 | 0 | { |
4189 | 0 | if (flags & format_no_empty_element_tags) |
4190 | 0 | { |
4191 | 0 | writer.write('>', '<', '/'); |
4192 | 0 | writer.write_string(name); |
4193 | 0 | writer.write('>'); |
4194 | |
|
4195 | 0 | return false; |
4196 | 0 | } |
4197 | 0 | else |
4198 | 0 | { |
4199 | 0 | if ((flags & format_raw) == 0) |
4200 | 0 | writer.write(' '); |
4201 | |
|
4202 | 0 | writer.write('/', '>'); |
4203 | |
|
4204 | 0 | return false; |
4205 | 0 | } |
4206 | 0 | } |
4207 | 0 | else |
4208 | 0 | { |
4209 | 0 | writer.write('>'); |
4210 | |
|
4211 | 0 | return true; |
4212 | 0 | } |
4213 | 0 | } |
4214 | 0 | else |
4215 | 0 | { |
4216 | 0 | writer.write('>'); |
4217 | |
|
4218 | 0 | text_output(writer, node->value, ctx_special_pcdata, flags); |
4219 | |
|
4220 | 0 | if (!node->first_child) |
4221 | 0 | { |
4222 | 0 | writer.write('<', '/'); |
4223 | 0 | writer.write_string(name); |
4224 | 0 | writer.write('>'); |
4225 | |
|
4226 | 0 | return false; |
4227 | 0 | } |
4228 | 0 | else |
4229 | 0 | { |
4230 | 0 | return true; |
4231 | 0 | } |
4232 | 0 | } |
4233 | 0 | } |
4234 | | |
4235 | | PUGI_IMPL_FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) |
4236 | 0 | { |
4237 | 0 | const char_t* default_name = PUGIXML_TEXT(":anonymous"); |
4238 | 0 | const char_t* name = node->name ? node->name + 0 : default_name; |
4239 | |
|
4240 | 0 | writer.write('<', '/'); |
4241 | 0 | writer.write_string(name); |
4242 | 0 | writer.write('>'); |
4243 | 0 | } |
4244 | | |
4245 | | PUGI_IMPL_FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) |
4246 | 0 | { |
4247 | 0 | const char_t* default_name = PUGIXML_TEXT(":anonymous"); |
4248 | |
|
4249 | 0 | switch (PUGI_IMPL_NODETYPE(node)) |
4250 | 0 | { |
4251 | 0 | case node_pcdata: |
4252 | 0 | text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags); |
4253 | 0 | break; |
4254 | | |
4255 | 0 | case node_cdata: |
4256 | 0 | text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); |
4257 | 0 | break; |
4258 | | |
4259 | 0 | case node_comment: |
4260 | 0 | node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); |
4261 | 0 | break; |
4262 | | |
4263 | 0 | case node_pi: |
4264 | 0 | writer.write('<', '?'); |
4265 | 0 | writer.write_string(node->name ? node->name + 0 : default_name); |
4266 | |
|
4267 | 0 | if (node->value) |
4268 | 0 | { |
4269 | 0 | writer.write(' '); |
4270 | 0 | node_output_pi_value(writer, node->value); |
4271 | 0 | } |
4272 | |
|
4273 | 0 | writer.write('?', '>'); |
4274 | 0 | break; |
4275 | | |
4276 | 0 | case node_declaration: |
4277 | 0 | writer.write('<', '?'); |
4278 | 0 | writer.write_string(node->name ? node->name + 0 : default_name); |
4279 | 0 | node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); |
4280 | 0 | writer.write('?', '>'); |
4281 | 0 | break; |
4282 | | |
4283 | 0 | case node_doctype: |
4284 | 0 | writer.write('<', '!', 'D', 'O', 'C'); |
4285 | 0 | writer.write('T', 'Y', 'P', 'E'); |
4286 | |
|
4287 | 0 | if (node->value) |
4288 | 0 | { |
4289 | 0 | writer.write(' '); |
4290 | 0 | writer.write_string(node->value); |
4291 | 0 | } |
4292 | |
|
4293 | 0 | writer.write('>'); |
4294 | 0 | break; |
4295 | | |
4296 | 0 | default: |
4297 | 0 | assert(false && "Invalid node type"); // unreachable |
4298 | 0 | } |
4299 | 0 | } |
4300 | | |
4301 | | enum indent_flags_t |
4302 | | { |
4303 | | indent_newline = 1, |
4304 | | indent_indent = 2 |
4305 | | }; |
4306 | | |
4307 | | PUGI_IMPL_FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) |
4308 | 0 | { |
4309 | 0 | size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; |
4310 | 0 | unsigned int indent_flags = indent_indent; |
4311 | |
|
4312 | 0 | xml_node_struct* node = root; |
4313 | |
|
4314 | 0 | do |
4315 | 0 | { |
4316 | 0 | assert(node); |
4317 | | |
4318 | | // begin writing current node |
4319 | 0 | if (PUGI_IMPL_NODETYPE(node) == node_pcdata || PUGI_IMPL_NODETYPE(node) == node_cdata) |
4320 | 0 | { |
4321 | 0 | node_output_simple(writer, node, flags); |
4322 | |
|
4323 | 0 | indent_flags = 0; |
4324 | 0 | } |
4325 | 0 | else |
4326 | 0 | { |
4327 | 0 | if ((indent_flags & indent_newline) && (flags & format_raw) == 0) |
4328 | 0 | writer.write('\n'); |
4329 | |
|
4330 | 0 | if ((indent_flags & indent_indent) && indent_length) |
4331 | 0 | text_output_indent(writer, indent, indent_length, depth); |
4332 | |
|
4333 | 0 | if (PUGI_IMPL_NODETYPE(node) == node_element) |
4334 | 0 | { |
4335 | 0 | indent_flags = indent_newline | indent_indent; |
4336 | |
|
4337 | 0 | if (node_output_start(writer, node, indent, indent_length, flags, depth)) |
4338 | 0 | { |
4339 | | // element nodes can have value if parse_embed_pcdata was used |
4340 | 0 | if (node->value) |
4341 | 0 | indent_flags = 0; |
4342 | |
|
4343 | 0 | node = node->first_child; |
4344 | 0 | depth++; |
4345 | 0 | continue; |
4346 | 0 | } |
4347 | 0 | } |
4348 | 0 | else if (PUGI_IMPL_NODETYPE(node) == node_document) |
4349 | 0 | { |
4350 | 0 | indent_flags = indent_indent; |
4351 | |
|
4352 | 0 | if (node->first_child) |
4353 | 0 | { |
4354 | 0 | node = node->first_child; |
4355 | 0 | continue; |
4356 | 0 | } |
4357 | 0 | } |
4358 | 0 | else |
4359 | 0 | { |
4360 | 0 | node_output_simple(writer, node, flags); |
4361 | |
|
4362 | 0 | indent_flags = indent_newline | indent_indent; |
4363 | 0 | } |
4364 | 0 | } |
4365 | | |
4366 | | // continue to the next node |
4367 | 0 | while (node != root) |
4368 | 0 | { |
4369 | 0 | if (node->next_sibling) |
4370 | 0 | { |
4371 | 0 | node = node->next_sibling; |
4372 | 0 | break; |
4373 | 0 | } |
4374 | | |
4375 | 0 | node = node->parent; |
4376 | | |
4377 | | // write closing node |
4378 | 0 | if (PUGI_IMPL_NODETYPE(node) == node_element) |
4379 | 0 | { |
4380 | 0 | depth--; |
4381 | |
|
4382 | 0 | if ((indent_flags & indent_newline) && (flags & format_raw) == 0) |
4383 | 0 | writer.write('\n'); |
4384 | |
|
4385 | 0 | if ((indent_flags & indent_indent) && indent_length) |
4386 | 0 | text_output_indent(writer, indent, indent_length, depth); |
4387 | |
|
4388 | 0 | node_output_end(writer, node); |
4389 | |
|
4390 | 0 | indent_flags = indent_newline | indent_indent; |
4391 | 0 | } |
4392 | 0 | } |
4393 | 0 | } |
4394 | 0 | while (node != root); |
4395 | | |
4396 | 0 | if ((indent_flags & indent_newline) && (flags & format_raw) == 0) |
4397 | 0 | writer.write('\n'); |
4398 | 0 | } |
4399 | | |
4400 | | PUGI_IMPL_FN bool has_declaration(xml_node_struct* node) |
4401 | 0 | { |
4402 | 0 | for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) |
4403 | 0 | { |
4404 | 0 | xml_node_type type = PUGI_IMPL_NODETYPE(child); |
4405 | |
|
4406 | 0 | if (type == node_declaration) return true; |
4407 | 0 | if (type == node_element) return false; |
4408 | 0 | } |
4409 | | |
4410 | 0 | return false; |
4411 | 0 | } |
4412 | | |
4413 | | PUGI_IMPL_FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) |
4414 | 0 | { |
4415 | 0 | for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) |
4416 | 0 | if (a == attr) |
4417 | 0 | return true; |
4418 | | |
4419 | 0 | return false; |
4420 | 0 | } |
4421 | | |
4422 | | PUGI_IMPL_FN bool allow_insert_attribute(xml_node_type parent) |
4423 | 0 | { |
4424 | 0 | return parent == node_element || parent == node_declaration; |
4425 | 0 | } |
4426 | | |
4427 | | PUGI_IMPL_FN bool allow_insert_child(xml_node_type parent, xml_node_type child) |
4428 | 0 | { |
4429 | 0 | if (parent != node_document && parent != node_element) return false; |
4430 | 0 | if (child == node_document || child == node_null) return false; |
4431 | 0 | if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; |
4432 | | |
4433 | 0 | return true; |
4434 | 0 | } |
4435 | | |
4436 | | PUGI_IMPL_FN bool allow_move(xml_node parent, xml_node child) |
4437 | 0 | { |
4438 | | // check that child can be a child of parent |
4439 | 0 | if (!allow_insert_child(parent.type(), child.type())) |
4440 | 0 | return false; |
4441 | | |
4442 | | // check that node is not moved between documents |
4443 | 0 | if (parent.root() != child.root()) |
4444 | 0 | return false; |
4445 | | |
4446 | | // check that new parent is not in the child subtree |
4447 | 0 | xml_node cur = parent; |
4448 | |
|
4449 | 0 | while (cur) |
4450 | 0 | { |
4451 | 0 | if (cur == child) |
4452 | 0 | return false; |
4453 | | |
4454 | 0 | cur = cur.parent(); |
4455 | 0 | } |
4456 | | |
4457 | 0 | return true; |
4458 | 0 | } |
4459 | | |
4460 | | template <typename String, typename Header> |
4461 | | PUGI_IMPL_FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc) |
4462 | 0 | { |
4463 | 0 | assert(!dest && (header & header_mask) == 0); // copies are performed into fresh nodes |
4464 | | |
4465 | 0 | if (source) |
4466 | 0 | { |
4467 | 0 | if (alloc && (source_header & header_mask) == 0) |
4468 | 0 | { |
4469 | 0 | dest = source; |
4470 | | |
4471 | | // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared |
4472 | 0 | header |= xml_memory_page_contents_shared_mask; |
4473 | 0 | source_header |= xml_memory_page_contents_shared_mask; |
4474 | 0 | } |
4475 | 0 | else |
4476 | 0 | { |
4477 | | // if strcpy_insitu fails (out of memory) we just leave the destination name/value empty |
4478 | 0 | (void)strcpy_insitu(dest, header, header_mask, source, strlength(source)); |
4479 | 0 | } |
4480 | 0 | } |
4481 | 0 | } |
4482 | | |
4483 | | PUGI_IMPL_FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) |
4484 | 0 | { |
4485 | 0 | node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); |
4486 | 0 | node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); |
4487 | |
|
4488 | 0 | for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute) |
4489 | 0 | { |
4490 | 0 | xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); |
4491 | |
|
4492 | 0 | if (da) |
4493 | 0 | { |
4494 | 0 | node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); |
4495 | 0 | node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); |
4496 | 0 | } |
4497 | 0 | } |
4498 | 0 | } |
4499 | | |
4500 | | PUGI_IMPL_FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) |
4501 | 0 | { |
4502 | 0 | xml_allocator& alloc = get_allocator(dn); |
4503 | 0 | xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : NULL; |
4504 | |
|
4505 | 0 | node_copy_contents(dn, sn, shared_alloc); |
4506 | |
|
4507 | 0 | xml_node_struct* dit = dn; |
4508 | 0 | xml_node_struct* sit = sn->first_child; |
4509 | |
|
4510 | 0 | while (sit && sit != sn) |
4511 | 0 | { |
4512 | | // loop invariant: dit is inside the subtree rooted at dn |
4513 | 0 | assert(dit); |
4514 | | |
4515 | | // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop |
4516 | 0 | if (sit != dn) |
4517 | 0 | { |
4518 | 0 | xml_node_struct* copy = append_new_node(dit, alloc, PUGI_IMPL_NODETYPE(sit)); |
4519 | |
|
4520 | 0 | if (copy) |
4521 | 0 | { |
4522 | 0 | node_copy_contents(copy, sit, shared_alloc); |
4523 | |
|
4524 | 0 | if (sit->first_child) |
4525 | 0 | { |
4526 | 0 | dit = copy; |
4527 | 0 | sit = sit->first_child; |
4528 | 0 | continue; |
4529 | 0 | } |
4530 | 0 | } |
4531 | 0 | } |
4532 | | |
4533 | | // continue to the next node |
4534 | 0 | do |
4535 | 0 | { |
4536 | 0 | if (sit->next_sibling) |
4537 | 0 | { |
4538 | 0 | sit = sit->next_sibling; |
4539 | 0 | break; |
4540 | 0 | } |
4541 | | |
4542 | 0 | sit = sit->parent; |
4543 | 0 | dit = dit->parent; |
4544 | | |
4545 | | // loop invariant: dit is inside the subtree rooted at dn while sit is inside sn |
4546 | 0 | assert(sit == sn || dit); |
4547 | 0 | } |
4548 | 0 | while (sit != sn); |
4549 | 0 | } |
4550 | | |
4551 | 0 | assert(!sit || dit == dn->parent); |
4552 | 0 | } |
4553 | | |
4554 | | PUGI_IMPL_FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) |
4555 | 0 | { |
4556 | 0 | xml_allocator& alloc = get_allocator(da); |
4557 | 0 | xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : NULL; |
4558 | |
|
4559 | 0 | node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); |
4560 | 0 | node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); |
4561 | 0 | } |
4562 | | |
4563 | | inline bool is_text_node(xml_node_struct* node) |
4564 | 0 | { |
4565 | 0 | xml_node_type type = PUGI_IMPL_NODETYPE(node); |
4566 | |
|
4567 | 0 | return type == node_pcdata || type == node_cdata; |
4568 | 0 | } |
4569 | | |
4570 | | // get value with conversion functions |
4571 | | template <typename U> PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv) |
4572 | 0 | { |
4573 | 0 | U result = 0; |
4574 | 0 | const char_t* s = value; |
4575 | |
|
4576 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*s, ct_space)) |
4577 | 0 | s++; |
4578 | |
|
4579 | 0 | bool negative = (*s == '-'); |
4580 | |
|
4581 | 0 | s += (*s == '+' || *s == '-'); |
4582 | |
|
4583 | 0 | bool overflow = false; |
4584 | |
|
4585 | 0 | if (s[0] == '0' && (s[1] | ' ') == 'x') |
4586 | 0 | { |
4587 | 0 | s += 2; |
4588 | | |
4589 | | // since overflow detection relies on length of the sequence skip leading zeros |
4590 | 0 | while (*s == '0') |
4591 | 0 | s++; |
4592 | |
|
4593 | 0 | const char_t* start = s; |
4594 | |
|
4595 | 0 | for (;;) |
4596 | 0 | { |
4597 | 0 | if (static_cast<unsigned>(*s - '0') < 10) |
4598 | 0 | result = result * 16 + (*s - '0'); |
4599 | 0 | else if (static_cast<unsigned>((*s | ' ') - 'a') < 6) |
4600 | 0 | result = result * 16 + ((*s | ' ') - 'a' + 10); |
4601 | 0 | else |
4602 | 0 | break; |
4603 | | |
4604 | 0 | s++; |
4605 | 0 | } |
4606 | |
|
4607 | 0 | size_t digits = static_cast<size_t>(s - start); |
4608 | |
|
4609 | 0 | overflow = digits > sizeof(U) * 2; |
4610 | 0 | } |
4611 | 0 | else |
4612 | 0 | { |
4613 | | // since overflow detection relies on length of the sequence skip leading zeros |
4614 | 0 | while (*s == '0') |
4615 | 0 | s++; |
4616 | |
|
4617 | 0 | const char_t* start = s; |
4618 | |
|
4619 | 0 | for (;;) |
4620 | 0 | { |
4621 | 0 | if (static_cast<unsigned>(*s - '0') < 10) |
4622 | 0 | result = result * 10 + (*s - '0'); |
4623 | 0 | else |
4624 | 0 | break; |
4625 | | |
4626 | 0 | s++; |
4627 | 0 | } |
4628 | |
|
4629 | 0 | size_t digits = static_cast<size_t>(s - start); |
4630 | |
|
4631 | 0 | PUGI_IMPL_STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); |
4632 | |
|
4633 | 0 | const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5; |
4634 | 0 | const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; |
4635 | 0 | const size_t high_bit = sizeof(U) * 8 - 1; |
4636 | |
|
4637 | 0 | overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit))); |
4638 | 0 | } |
4639 | |
|
4640 | 0 | if (negative) |
4641 | 0 | { |
4642 | | // Workaround for crayc++ CC-3059: Expected no overflow in routine. |
4643 | | #ifdef _CRAYC |
4644 | | return (overflow || result > ~minv + 1) ? minv : ~result + 1; |
4645 | | #else |
4646 | 0 | return (overflow || result > 0 - minv) ? minv : 0 - result; |
4647 | 0 | #endif |
4648 | 0 | } |
4649 | 0 | else |
4650 | 0 | return (overflow || result > maxv) ? maxv : result; |
4651 | 0 | } Unexecuted instantiation: pugixml.cpp:unsigned int pugi::impl::(anonymous namespace)::string_to_integer<unsigned int>(char const*, unsigned int, unsigned int) Unexecuted instantiation: pugixml.cpp:unsigned long long pugi::impl::(anonymous namespace)::string_to_integer<unsigned long long>(char const*, unsigned long long, unsigned long long) |
4652 | | |
4653 | | PUGI_IMPL_FN int get_value_int(const char_t* value) |
4654 | 0 | { |
4655 | 0 | return string_to_integer<unsigned int>(value, static_cast<unsigned int>(INT_MIN), INT_MAX); |
4656 | 0 | } |
4657 | | |
4658 | | PUGI_IMPL_FN unsigned int get_value_uint(const char_t* value) |
4659 | 0 | { |
4660 | 0 | return string_to_integer<unsigned int>(value, 0, UINT_MAX); |
4661 | 0 | } |
4662 | | |
4663 | | PUGI_IMPL_FN double get_value_double(const char_t* value) |
4664 | 0 | { |
4665 | | #ifdef PUGIXML_WCHAR_MODE |
4666 | | return wcstod(value, NULL); |
4667 | | #else |
4668 | 0 | return strtod(value, NULL); |
4669 | 0 | #endif |
4670 | 0 | } |
4671 | | |
4672 | | PUGI_IMPL_FN float get_value_float(const char_t* value) |
4673 | 0 | { |
4674 | | #ifdef PUGIXML_WCHAR_MODE |
4675 | | return static_cast<float>(wcstod(value, NULL)); |
4676 | | #else |
4677 | 0 | return static_cast<float>(strtod(value, NULL)); |
4678 | 0 | #endif |
4679 | 0 | } |
4680 | | |
4681 | | PUGI_IMPL_FN bool get_value_bool(const char_t* value) |
4682 | 0 | { |
4683 | | // only look at first char |
4684 | 0 | char_t first = *value; |
4685 | | |
4686 | | // 1*, t* (true), T* (True), y* (yes), Y* (YES) |
4687 | 0 | return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); |
4688 | 0 | } |
4689 | | |
4690 | | #ifdef PUGIXML_HAS_LONG_LONG |
4691 | | PUGI_IMPL_FN long long get_value_llong(const char_t* value) |
4692 | 0 | { |
4693 | 0 | return string_to_integer<unsigned long long>(value, static_cast<unsigned long long>(LLONG_MIN), LLONG_MAX); |
4694 | 0 | } |
4695 | | |
4696 | | PUGI_IMPL_FN unsigned long long get_value_ullong(const char_t* value) |
4697 | 0 | { |
4698 | 0 | return string_to_integer<unsigned long long>(value, 0, ULLONG_MAX); |
4699 | 0 | } |
4700 | | #endif |
4701 | | |
4702 | | template <typename U> PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) |
4703 | 0 | { |
4704 | 0 | char_t* result = end - 1; |
4705 | 0 | U rest = negative ? 0 - value : value; |
4706 | |
|
4707 | 0 | do |
4708 | 0 | { |
4709 | 0 | *result-- = static_cast<char_t>('0' + (rest % 10)); |
4710 | 0 | rest /= 10; |
4711 | 0 | } |
4712 | 0 | while (rest); |
4713 | |
|
4714 | 0 | assert(result >= begin); |
4715 | 0 | (void)begin; |
4716 | |
|
4717 | 0 | *result = '-'; |
4718 | |
|
4719 | 0 | return result + !negative; |
4720 | 0 | } Unexecuted instantiation: pugixml.cpp:char* pugi::impl::(anonymous namespace)::integer_to_string<unsigned int>(char*, char*, unsigned int, bool) Unexecuted instantiation: pugixml.cpp:char* pugi::impl::(anonymous namespace)::integer_to_string<unsigned long>(char*, char*, unsigned long, bool) Unexecuted instantiation: pugixml.cpp:char* pugi::impl::(anonymous namespace)::integer_to_string<unsigned long long>(char*, char*, unsigned long long, bool) |
4721 | | |
4722 | | // set value with conversion functions |
4723 | | template <typename String, typename Header> |
4724 | | PUGI_IMPL_FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf) |
4725 | 0 | { |
4726 | | #ifdef PUGIXML_WCHAR_MODE |
4727 | | char_t wbuf[128]; |
4728 | | assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0])); |
4729 | | |
4730 | | size_t offset = 0; |
4731 | | for (; buf[offset]; ++offset) wbuf[offset] = buf[offset]; |
4732 | | |
4733 | | return strcpy_insitu(dest, header, header_mask, wbuf, offset); |
4734 | | #else |
4735 | 0 | return strcpy_insitu(dest, header, header_mask, buf, strlen(buf)); |
4736 | 0 | #endif |
4737 | 0 | } |
4738 | | |
4739 | | template <typename U, typename String, typename Header> |
4740 | | PUGI_IMPL_FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative) |
4741 | 0 | { |
4742 | 0 | char_t buf[64]; |
4743 | 0 | char_t* end = buf + sizeof(buf) / sizeof(buf[0]); |
4744 | 0 | char_t* begin = integer_to_string(buf, end, value, negative); |
4745 | |
|
4746 | 0 | return strcpy_insitu(dest, header, header_mask, begin, end - begin); |
4747 | 0 | } Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::set_value_integer<unsigned int, char*, unsigned long>(char*&, unsigned long&, unsigned long, unsigned int, bool) Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::set_value_integer<unsigned long, char*, unsigned long>(char*&, unsigned long&, unsigned long, unsigned long, bool) Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::set_value_integer<unsigned long long, char*, unsigned long>(char*&, unsigned long&, unsigned long, unsigned long long, bool) |
4748 | | |
4749 | | template <typename String, typename Header> |
4750 | | PUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision) |
4751 | 0 | { |
4752 | 0 | char buf[128]; |
4753 | 0 | PUGI_IMPL_SNPRINTF(buf, "%.*g", precision, double(value)); |
4754 | |
|
4755 | 0 | return set_value_ascii(dest, header, header_mask, buf); |
4756 | 0 | } |
4757 | | |
4758 | | template <typename String, typename Header> |
4759 | | PUGI_IMPL_FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value, int precision) |
4760 | 0 | { |
4761 | 0 | char buf[128]; |
4762 | 0 | PUGI_IMPL_SNPRINTF(buf, "%.*g", precision, value); |
4763 | |
|
4764 | 0 | return set_value_ascii(dest, header, header_mask, buf); |
4765 | 0 | } |
4766 | | |
4767 | | template <typename String, typename Header> |
4768 | | PUGI_IMPL_FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value) |
4769 | 0 | { |
4770 | 0 | return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5); |
4771 | 0 | } |
4772 | | |
4773 | | PUGI_IMPL_FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) |
4774 | 11.4k | { |
4775 | | // check input buffer |
4776 | 11.4k | if (!contents && size) return make_parse_result(status_io_error); |
4777 | | |
4778 | | // get actual encoding |
4779 | 11.4k | xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); |
4780 | | |
4781 | | // if convert_buffer below throws bad_alloc, we still need to deallocate contents if we own it |
4782 | 11.4k | auto_deleter<void> contents_guard(own ? contents : NULL, xml_memory::deallocate); |
4783 | | |
4784 | | // early-out for empty documents to avoid buffer allocation overhead |
4785 | 11.4k | if (size == 0) return make_parse_result((options & parse_fragment) ? status_ok : status_no_document_element); |
4786 | | |
4787 | | // get private buffer |
4788 | 11.4k | char_t* buffer = NULL; |
4789 | 11.4k | size_t length = 0; |
4790 | | |
4791 | 11.4k | if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); |
4792 | | |
4793 | | // after this we either deallocate contents (below) or hold on to it via doc->buffer, so we don't need to guard it |
4794 | 11.4k | contents_guard.release(); |
4795 | | |
4796 | | // delete original buffer if we performed a conversion |
4797 | 11.4k | if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); |
4798 | | |
4799 | | // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself |
4800 | 11.4k | if (own || buffer != contents) *out_buffer = buffer; |
4801 | | |
4802 | | // store buffer for offset_debug |
4803 | 11.4k | doc->buffer = buffer; |
4804 | | |
4805 | | // parse |
4806 | 11.4k | xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); |
4807 | | |
4808 | | // remember encoding |
4809 | 11.4k | res.encoding = buffer_encoding; |
4810 | | |
4811 | 11.4k | return res; |
4812 | 11.4k | } |
4813 | | |
4814 | | template <typename T> PUGI_IMPL_FN xml_parse_status convert_file_size(T length, size_t& out_result) |
4815 | 0 | { |
4816 | | // check for I/O errors |
4817 | 0 | if (length < 0) return status_io_error; |
4818 | | |
4819 | | // check for overflow |
4820 | 0 | size_t result = static_cast<size_t>(length); |
4821 | |
|
4822 | 0 | if (static_cast<T>(result) != length) return status_out_of_memory; |
4823 | | |
4824 | 0 | out_result = result; |
4825 | 0 | return status_ok; |
4826 | 0 | } |
4827 | | |
4828 | | // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick |
4829 | | PUGI_IMPL_FN xml_parse_status get_file_size(FILE* file, size_t& out_result) |
4830 | 0 | { |
4831 | 0 | #if defined(__linux__) || defined(__APPLE__) |
4832 | | // this simultaneously retrieves the file size and file mode (to guard against loading non-files) |
4833 | 0 | struct stat st; |
4834 | 0 | if (fstat(fileno(file), &st) != 0) return status_io_error; |
4835 | | |
4836 | | // anything that's not a regular file doesn't have a coherent length |
4837 | 0 | if (!S_ISREG(st.st_mode)) return status_io_error; |
4838 | | |
4839 | 0 | xml_parse_status status = convert_file_size(st.st_size, out_result); |
4840 | | #elif defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 |
4841 | | // there are 64-bit versions of fseek/ftell, let's use them |
4842 | | _fseeki64(file, 0, SEEK_END); |
4843 | | __int64 length = _ftelli64(file); |
4844 | | _fseeki64(file, 0, SEEK_SET); |
4845 | | |
4846 | | xml_parse_status status = convert_file_size(length, out_result); |
4847 | | #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)) |
4848 | | // there are 64-bit versions of fseek/ftell, let's use them |
4849 | | fseeko64(file, 0, SEEK_END); |
4850 | | off64_t length = ftello64(file); |
4851 | | fseeko64(file, 0, SEEK_SET); |
4852 | | |
4853 | | xml_parse_status status = convert_file_size(length, out_result); |
4854 | | #else |
4855 | | // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. |
4856 | | fseek(file, 0, SEEK_END); |
4857 | | long length = ftell(file); |
4858 | | fseek(file, 0, SEEK_SET); |
4859 | | |
4860 | | xml_parse_status status = convert_file_size(length, out_result); |
4861 | | #endif |
4862 | |
|
4863 | 0 | return status; |
4864 | 0 | } |
4865 | | |
4866 | | // This function assumes that buffer has extra sizeof(char_t) writable bytes after size |
4867 | | PUGI_IMPL_FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) |
4868 | 0 | { |
4869 | | // We only need to zero-terminate if encoding conversion does not do it for us |
4870 | | #ifdef PUGIXML_WCHAR_MODE |
4871 | | xml_encoding wchar_encoding = get_wchar_encoding(); |
4872 | | |
4873 | | if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) |
4874 | | { |
4875 | | size_t length = size / sizeof(char_t); |
4876 | | |
4877 | | static_cast<char_t*>(buffer)[length] = 0; |
4878 | | return (length + 1) * sizeof(char_t); |
4879 | | } |
4880 | | #else |
4881 | 0 | if (encoding == encoding_utf8) |
4882 | 0 | { |
4883 | 0 | static_cast<char*>(buffer)[size] = 0; |
4884 | 0 | return size + 1; |
4885 | 0 | } |
4886 | 0 | #endif |
4887 | | |
4888 | 0 | return size; |
4889 | 0 | } |
4890 | | |
4891 | | PUGI_IMPL_FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) |
4892 | 0 | { |
4893 | 0 | if (!file) return make_parse_result(status_file_not_found); |
4894 | | |
4895 | | // get file size (can result in I/O errors) |
4896 | 0 | size_t size = 0; |
4897 | 0 | xml_parse_status size_status = get_file_size(file, size); |
4898 | 0 | if (size_status != status_ok) return make_parse_result(size_status); |
4899 | | |
4900 | 0 | size_t max_suffix_size = sizeof(char_t); |
4901 | | |
4902 | | // allocate buffer for the whole file |
4903 | 0 | char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size)); |
4904 | 0 | if (!contents) return make_parse_result(status_out_of_memory); |
4905 | | |
4906 | | // read file in memory |
4907 | 0 | size_t read_size = fread(contents, 1, size, file); |
4908 | |
|
4909 | 0 | if (read_size != size) |
4910 | 0 | { |
4911 | 0 | xml_memory::deallocate(contents); |
4912 | 0 | return make_parse_result(status_io_error); |
4913 | 0 | } |
4914 | | |
4915 | 0 | xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); |
4916 | |
|
4917 | 0 | return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer); |
4918 | 0 | } |
4919 | | |
4920 | | PUGI_IMPL_FN void close_file(FILE* file) |
4921 | 0 | { |
4922 | 0 | fclose(file); |
4923 | 0 | } |
4924 | | |
4925 | | #ifndef PUGIXML_NO_STL |
4926 | | template <typename T> struct xml_stream_chunk |
4927 | | { |
4928 | | static xml_stream_chunk* create() |
4929 | 0 | { |
4930 | 0 | void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); |
4931 | 0 | if (!memory) return NULL; |
4932 | | |
4933 | 0 | return new (memory) xml_stream_chunk(); |
4934 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_stream_chunk<char>::create() Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_stream_chunk<wchar_t>::create() |
4935 | | |
4936 | | static void destroy(xml_stream_chunk* chunk) |
4937 | 0 | { |
4938 | | // free chunk chain |
4939 | 0 | while (chunk) |
4940 | 0 | { |
4941 | 0 | xml_stream_chunk* next_ = chunk->next; |
4942 | |
|
4943 | 0 | xml_memory::deallocate(chunk); |
4944 | |
|
4945 | 0 | chunk = next_; |
4946 | 0 | } |
4947 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_stream_chunk<char>::destroy(pugi::impl::(anonymous namespace)::xml_stream_chunk<char>*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_stream_chunk<wchar_t>::destroy(pugi::impl::(anonymous namespace)::xml_stream_chunk<wchar_t>*) |
4948 | | |
4949 | 0 | xml_stream_chunk(): next(NULL), size(0) |
4950 | 0 | { |
4951 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_stream_chunk<char>::xml_stream_chunk() Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xml_stream_chunk<wchar_t>::xml_stream_chunk() |
4952 | | |
4953 | | xml_stream_chunk* next; |
4954 | | size_t size; |
4955 | | |
4956 | | T data[xml_memory_page_size / sizeof(T)]; |
4957 | | }; |
4958 | | |
4959 | | template <typename T> PUGI_IMPL_FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size) |
4960 | 0 | { |
4961 | 0 | auto_deleter<xml_stream_chunk<T> > chunks(NULL, xml_stream_chunk<T>::destroy); |
4962 | | |
4963 | | // read file to a chunk list |
4964 | 0 | size_t total = 0; |
4965 | 0 | xml_stream_chunk<T>* last = NULL; |
4966 | |
|
4967 | 0 | while (!stream.eof()) |
4968 | 0 | { |
4969 | | // allocate new chunk |
4970 | 0 | xml_stream_chunk<T>* chunk = xml_stream_chunk<T>::create(); |
4971 | 0 | if (!chunk) return status_out_of_memory; |
4972 | | |
4973 | | // append chunk to list |
4974 | 0 | if (last) last = last->next = chunk; |
4975 | 0 | else chunks.data = last = chunk; |
4976 | | |
4977 | | // read data to chunk |
4978 | 0 | stream.read(chunk->data, static_cast<std::streamsize>(sizeof(chunk->data) / sizeof(T))); |
4979 | 0 | chunk->size = static_cast<size_t>(stream.gcount()) * sizeof(T); |
4980 | | |
4981 | | // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors |
4982 | 0 | if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; |
4983 | | |
4984 | | // guard against huge files (chunk size is small enough to make this overflow check work) |
4985 | 0 | if (total + chunk->size < total) return status_out_of_memory; |
4986 | 0 | total += chunk->size; |
4987 | 0 | } |
4988 | | |
4989 | 0 | size_t max_suffix_size = sizeof(char_t); |
4990 | | |
4991 | | // copy chunk list to a contiguous buffer |
4992 | 0 | char* buffer = static_cast<char*>(xml_memory::allocate(total + max_suffix_size)); |
4993 | 0 | if (!buffer) return status_out_of_memory; |
4994 | | |
4995 | 0 | char* write = buffer; |
4996 | |
|
4997 | 0 | for (xml_stream_chunk<T>* chunk = chunks.data; chunk; chunk = chunk->next) |
4998 | 0 | { |
4999 | 0 | assert(write + chunk->size <= buffer + total); |
5000 | 0 | memcpy(write, chunk->data, chunk->size); |
5001 | 0 | write += chunk->size; |
5002 | 0 | } |
5003 | | |
5004 | 0 | assert(write == buffer + total); |
5005 | | |
5006 | | // return buffer |
5007 | 0 | *out_buffer = buffer; |
5008 | 0 | *out_size = total; |
5009 | |
|
5010 | 0 | return status_ok; |
5011 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::xml_parse_status pugi::impl::(anonymous namespace)::load_stream_data_noseek<char>(std::__1::basic_istream<char, std::__1::char_traits<char> >&, void**, unsigned long*) Unexecuted instantiation: pugixml.cpp:pugi::xml_parse_status pugi::impl::(anonymous namespace)::load_stream_data_noseek<wchar_t>(std::__1::basic_istream<wchar_t, std::__1::char_traits<wchar_t> >&, void**, unsigned long*) |
5012 | | |
5013 | | template <typename T> PUGI_IMPL_FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size) |
5014 | 0 | { |
5015 | | // get length of remaining data in stream |
5016 | 0 | typename std::basic_istream<T>::pos_type pos = stream.tellg(); |
5017 | 0 | stream.seekg(0, std::ios::end); |
5018 | 0 | std::streamoff length = stream.tellg() - pos; |
5019 | 0 | stream.seekg(pos); |
5020 | |
|
5021 | 0 | if (stream.fail() || pos < 0) return status_io_error; |
5022 | | |
5023 | | // guard against huge files |
5024 | 0 | size_t read_length = static_cast<size_t>(length); |
5025 | |
|
5026 | 0 | if (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory; |
5027 | | |
5028 | 0 | size_t max_suffix_size = sizeof(char_t); |
5029 | | |
5030 | | // read stream data into memory (guard against stream exceptions with buffer holder) |
5031 | 0 | auto_deleter<void> buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); |
5032 | 0 | if (!buffer.data) return status_out_of_memory; |
5033 | | |
5034 | 0 | stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length)); |
5035 | | |
5036 | | // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors |
5037 | 0 | if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; |
5038 | | |
5039 | | // return buffer |
5040 | 0 | size_t actual_length = static_cast<size_t>(stream.gcount()); |
5041 | 0 | assert(actual_length <= read_length); |
5042 | | |
5043 | 0 | *out_buffer = buffer.release(); |
5044 | 0 | *out_size = actual_length * sizeof(T); |
5045 | |
|
5046 | 0 | return status_ok; |
5047 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::xml_parse_status pugi::impl::(anonymous namespace)::load_stream_data_seek<char>(std::__1::basic_istream<char, std::__1::char_traits<char> >&, void**, unsigned long*) Unexecuted instantiation: pugixml.cpp:pugi::xml_parse_status pugi::impl::(anonymous namespace)::load_stream_data_seek<wchar_t>(std::__1::basic_istream<wchar_t, std::__1::char_traits<wchar_t> >&, void**, unsigned long*) |
5048 | | |
5049 | | template <typename T> PUGI_IMPL_FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) |
5050 | 0 | { |
5051 | 0 | void* buffer = NULL; |
5052 | 0 | size_t size = 0; |
5053 | 0 | xml_parse_status status = status_ok; |
5054 | | |
5055 | | // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) |
5056 | 0 | if (stream.fail()) return make_parse_result(status_io_error); |
5057 | | |
5058 | | // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) |
5059 | 0 | if (stream.tellg() < 0) |
5060 | 0 | { |
5061 | 0 | stream.clear(); // clear error flags that could be set by a failing tellg |
5062 | 0 | status = load_stream_data_noseek(stream, &buffer, &size); |
5063 | 0 | } |
5064 | 0 | else |
5065 | 0 | status = load_stream_data_seek(stream, &buffer, &size); |
5066 | |
|
5067 | 0 | if (status != status_ok) return make_parse_result(status); |
5068 | | |
5069 | 0 | xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); |
5070 | |
|
5071 | 0 | return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer); |
5072 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::xml_parse_result pugi::impl::(anonymous namespace)::load_stream_impl<char>(pugi::impl::(anonymous namespace)::xml_document_struct*, std::__1::basic_istream<char, std::__1::char_traits<char> >&, unsigned int, pugi::xml_encoding, char**) Unexecuted instantiation: pugixml.cpp:pugi::xml_parse_result pugi::impl::(anonymous namespace)::load_stream_impl<wchar_t>(pugi::impl::(anonymous namespace)::xml_document_struct*, std::__1::basic_istream<wchar_t, std::__1::char_traits<wchar_t> >&, unsigned int, pugi::xml_encoding, char**) |
5073 | | #endif |
5074 | | |
5075 | | #if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) |
5076 | | PUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) |
5077 | | { |
5078 | | #ifdef PUGIXML_NO_STL |
5079 | | // ensure these symbols are consistently referenced to avoid 'unreferenced function' warnings |
5080 | | // note that generally these functions are used in STL builds, but PUGIXML_NO_STL leaves the only usage in convert_path_heap |
5081 | | (void)&as_utf8_begin; |
5082 | | (void)&as_utf8_end; |
5083 | | (void)&strlength_wide; |
5084 | | #endif |
5085 | | |
5086 | | #if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 |
5087 | | FILE* file = NULL; |
5088 | | return _wfopen_s(&file, path, mode) == 0 ? file : NULL; |
5089 | | #else |
5090 | | return _wfopen(path, mode); |
5091 | | #endif |
5092 | | } |
5093 | | #else |
5094 | | PUGI_IMPL_FN char* convert_path_heap(const wchar_t* str) |
5095 | 0 | { |
5096 | 0 | assert(str); |
5097 | | |
5098 | | // first pass: get length in utf8 characters |
5099 | 0 | size_t length = strlength_wide(str); |
5100 | 0 | size_t size = as_utf8_begin(str, length); |
5101 | | |
5102 | | // allocate resulting string |
5103 | 0 | char* result = static_cast<char*>(xml_memory::allocate(size + 1)); |
5104 | 0 | if (!result) return NULL; |
5105 | | |
5106 | | // second pass: convert to utf8 |
5107 | 0 | as_utf8_end(result, size, str, length); |
5108 | | |
5109 | | // zero-terminate |
5110 | 0 | result[size] = 0; |
5111 | |
|
5112 | 0 | return result; |
5113 | 0 | } |
5114 | | |
5115 | | PUGI_IMPL_FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) |
5116 | 0 | { |
5117 | | // there is no standard function to open wide paths, so our best bet is to try utf8 path |
5118 | 0 | char* path_utf8 = convert_path_heap(path); |
5119 | 0 | if (!path_utf8) return NULL; |
5120 | | |
5121 | | // convert mode to ASCII (we mirror _wfopen interface) |
5122 | 0 | char mode_ascii[4] = {0}; |
5123 | 0 | for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]); |
5124 | | |
5125 | | // try to open the utf8 path |
5126 | 0 | FILE* result = fopen(path_utf8, mode_ascii); |
5127 | | |
5128 | | // free dummy buffer |
5129 | 0 | xml_memory::deallocate(path_utf8); |
5130 | |
|
5131 | 0 | return result; |
5132 | 0 | } |
5133 | | #endif |
5134 | | |
5135 | | PUGI_IMPL_FN FILE* open_file(const char* path, const char* mode) |
5136 | 0 | { |
5137 | | #if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 |
5138 | | FILE* file = NULL; |
5139 | | return fopen_s(&file, path, mode) == 0 ? file : NULL; |
5140 | | #else |
5141 | 0 | return fopen(path, mode); |
5142 | 0 | #endif |
5143 | 0 | } |
5144 | | |
5145 | | PUGI_IMPL_FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) |
5146 | 0 | { |
5147 | 0 | if (!file) return false; |
5148 | | |
5149 | 0 | xml_writer_file writer(file); |
5150 | 0 | doc.save(writer, indent, flags, encoding); |
5151 | |
|
5152 | 0 | return fflush(file) == 0 && ferror(file) == 0; |
5153 | 0 | } |
5154 | | |
5155 | | struct name_null_sentry |
5156 | | { |
5157 | | xml_node_struct* node; |
5158 | | char_t* name; |
5159 | | |
5160 | 0 | name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name) |
5161 | 0 | { |
5162 | 0 | node->name = NULL; |
5163 | 0 | } |
5164 | | |
5165 | | ~name_null_sentry() |
5166 | 0 | { |
5167 | 0 | node->name = name; |
5168 | 0 | } |
5169 | | }; |
5170 | | PUGI_IMPL_NS_END |
5171 | | |
5172 | | namespace pugi |
5173 | | { |
5174 | | PUGI_IMPL_FN xml_writer::~xml_writer() |
5175 | 0 | { |
5176 | 0 | } |
5177 | | |
5178 | 0 | PUGI_IMPL_FN xml_writer_file::xml_writer_file(void* file_): file(file_) |
5179 | 0 | { |
5180 | 0 | } |
5181 | | |
5182 | | PUGI_IMPL_FN void xml_writer_file::write(const void* data, size_t size) |
5183 | 0 | { |
5184 | 0 | size_t result = fwrite(data, 1, size, static_cast<FILE*>(file)); |
5185 | 0 | (void)!result; // unfortunately we can't do proper error handling here |
5186 | 0 | } |
5187 | | |
5188 | | #ifndef PUGIXML_NO_STL |
5189 | 0 | PUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char>& stream): narrow_stream(&stream), wide_stream(NULL) |
5190 | 0 | { |
5191 | 0 | } |
5192 | | |
5193 | 0 | PUGI_IMPL_FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t>& stream): narrow_stream(NULL), wide_stream(&stream) |
5194 | 0 | { |
5195 | 0 | } |
5196 | | |
5197 | | PUGI_IMPL_FN void xml_writer_stream::write(const void* data, size_t size) |
5198 | 0 | { |
5199 | 0 | if (narrow_stream) |
5200 | 0 | { |
5201 | 0 | assert(!wide_stream); |
5202 | 0 | narrow_stream->write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size)); |
5203 | 0 | } |
5204 | 0 | else |
5205 | 0 | { |
5206 | 0 | assert(wide_stream); |
5207 | 0 | assert(size % sizeof(wchar_t) == 0); |
5208 | | |
5209 | 0 | wide_stream->write(reinterpret_cast<const wchar_t*>(data), static_cast<std::streamsize>(size / sizeof(wchar_t))); |
5210 | 0 | } |
5211 | 0 | } |
5212 | | #endif |
5213 | | |
5214 | 0 | PUGI_IMPL_FN xml_tree_walker::xml_tree_walker(): _depth(0) |
5215 | 0 | { |
5216 | 0 | } |
5217 | | |
5218 | | PUGI_IMPL_FN xml_tree_walker::~xml_tree_walker() |
5219 | 0 | { |
5220 | 0 | } |
5221 | | |
5222 | | PUGI_IMPL_FN int xml_tree_walker::depth() const |
5223 | 0 | { |
5224 | 0 | return _depth; |
5225 | 0 | } |
5226 | | |
5227 | | PUGI_IMPL_FN bool xml_tree_walker::begin(xml_node&) |
5228 | 0 | { |
5229 | 0 | return true; |
5230 | 0 | } |
5231 | | |
5232 | | PUGI_IMPL_FN bool xml_tree_walker::end(xml_node&) |
5233 | 0 | { |
5234 | 0 | return true; |
5235 | 0 | } |
5236 | | |
5237 | 0 | PUGI_IMPL_FN xml_attribute::xml_attribute(): _attr(NULL) |
5238 | 0 | { |
5239 | 0 | } |
5240 | | |
5241 | 0 | PUGI_IMPL_FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) |
5242 | 0 | { |
5243 | 0 | } |
5244 | | |
5245 | | PUGI_IMPL_FN static void unspecified_bool_xml_attribute(xml_attribute***) |
5246 | 0 | { |
5247 | 0 | } |
5248 | | |
5249 | | PUGI_IMPL_FN xml_attribute::operator xml_attribute::unspecified_bool_type() const |
5250 | 0 | { |
5251 | 0 | return _attr ? unspecified_bool_xml_attribute : NULL; |
5252 | 0 | } |
5253 | | |
5254 | | PUGI_IMPL_FN bool xml_attribute::operator!() const |
5255 | 0 | { |
5256 | 0 | return !_attr; |
5257 | 0 | } |
5258 | | |
5259 | | PUGI_IMPL_FN bool xml_attribute::operator==(const xml_attribute& r) const |
5260 | 0 | { |
5261 | 0 | return (_attr == r._attr); |
5262 | 0 | } |
5263 | | |
5264 | | PUGI_IMPL_FN bool xml_attribute::operator!=(const xml_attribute& r) const |
5265 | 0 | { |
5266 | 0 | return (_attr != r._attr); |
5267 | 0 | } |
5268 | | |
5269 | | PUGI_IMPL_FN bool xml_attribute::operator<(const xml_attribute& r) const |
5270 | 0 | { |
5271 | 0 | return (_attr < r._attr); |
5272 | 0 | } |
5273 | | |
5274 | | PUGI_IMPL_FN bool xml_attribute::operator>(const xml_attribute& r) const |
5275 | 0 | { |
5276 | 0 | return (_attr > r._attr); |
5277 | 0 | } |
5278 | | |
5279 | | PUGI_IMPL_FN bool xml_attribute::operator<=(const xml_attribute& r) const |
5280 | 0 | { |
5281 | 0 | return (_attr <= r._attr); |
5282 | 0 | } |
5283 | | |
5284 | | PUGI_IMPL_FN bool xml_attribute::operator>=(const xml_attribute& r) const |
5285 | 0 | { |
5286 | 0 | return (_attr >= r._attr); |
5287 | 0 | } |
5288 | | |
5289 | | PUGI_IMPL_FN xml_attribute xml_attribute::next_attribute() const |
5290 | 0 | { |
5291 | 0 | if (!_attr) return xml_attribute(); |
5292 | 0 | return xml_attribute(_attr->next_attribute); |
5293 | 0 | } |
5294 | | |
5295 | | PUGI_IMPL_FN xml_attribute xml_attribute::previous_attribute() const |
5296 | 0 | { |
5297 | 0 | if (!_attr) return xml_attribute(); |
5298 | 0 | xml_attribute_struct* prev = _attr->prev_attribute_c; |
5299 | 0 | return prev->next_attribute ? xml_attribute(prev) : xml_attribute(); |
5300 | 0 | } |
5301 | | |
5302 | | PUGI_IMPL_FN const char_t* xml_attribute::as_string(const char_t* def) const |
5303 | 0 | { |
5304 | 0 | if (!_attr) return def; |
5305 | 0 | const char_t* value = _attr->value; |
5306 | 0 | return value ? value : def; |
5307 | 0 | } |
5308 | | |
5309 | | PUGI_IMPL_FN int xml_attribute::as_int(int def) const |
5310 | 0 | { |
5311 | 0 | if (!_attr) return def; |
5312 | 0 | const char_t* value = _attr->value; |
5313 | 0 | return value ? impl::get_value_int(value) : def; |
5314 | 0 | } |
5315 | | |
5316 | | PUGI_IMPL_FN unsigned int xml_attribute::as_uint(unsigned int def) const |
5317 | 0 | { |
5318 | 0 | if (!_attr) return def; |
5319 | 0 | const char_t* value = _attr->value; |
5320 | 0 | return value ? impl::get_value_uint(value) : def; |
5321 | 0 | } |
5322 | | |
5323 | | PUGI_IMPL_FN double xml_attribute::as_double(double def) const |
5324 | 0 | { |
5325 | 0 | if (!_attr) return def; |
5326 | 0 | const char_t* value = _attr->value; |
5327 | 0 | return value ? impl::get_value_double(value) : def; |
5328 | 0 | } |
5329 | | |
5330 | | PUGI_IMPL_FN float xml_attribute::as_float(float def) const |
5331 | 0 | { |
5332 | 0 | if (!_attr) return def; |
5333 | 0 | const char_t* value = _attr->value; |
5334 | 0 | return value ? impl::get_value_float(value) : def; |
5335 | 0 | } |
5336 | | |
5337 | | PUGI_IMPL_FN bool xml_attribute::as_bool(bool def) const |
5338 | 0 | { |
5339 | 0 | if (!_attr) return def; |
5340 | 0 | const char_t* value = _attr->value; |
5341 | 0 | return value ? impl::get_value_bool(value) : def; |
5342 | 0 | } |
5343 | | |
5344 | | #ifdef PUGIXML_HAS_LONG_LONG |
5345 | | PUGI_IMPL_FN long long xml_attribute::as_llong(long long def) const |
5346 | 0 | { |
5347 | 0 | if (!_attr) return def; |
5348 | 0 | const char_t* value = _attr->value; |
5349 | 0 | return value ? impl::get_value_llong(value) : def; |
5350 | 0 | } |
5351 | | |
5352 | | PUGI_IMPL_FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const |
5353 | 0 | { |
5354 | 0 | if (!_attr) return def; |
5355 | 0 | const char_t* value = _attr->value; |
5356 | 0 | return value ? impl::get_value_ullong(value) : def; |
5357 | 0 | } |
5358 | | #endif |
5359 | | |
5360 | | PUGI_IMPL_FN bool xml_attribute::empty() const |
5361 | 0 | { |
5362 | 0 | return !_attr; |
5363 | 0 | } |
5364 | | |
5365 | | PUGI_IMPL_FN const char_t* xml_attribute::name() const |
5366 | 0 | { |
5367 | 0 | if (!_attr) return PUGIXML_TEXT(""); |
5368 | 0 | const char_t* name = _attr->name; |
5369 | 0 | return name ? name : PUGIXML_TEXT(""); |
5370 | 0 | } |
5371 | | |
5372 | | PUGI_IMPL_FN const char_t* xml_attribute::value() const |
5373 | 0 | { |
5374 | 0 | if (!_attr) return PUGIXML_TEXT(""); |
5375 | 0 | const char_t* value = _attr->value; |
5376 | 0 | return value ? value : PUGIXML_TEXT(""); |
5377 | 0 | } |
5378 | | |
5379 | | PUGI_IMPL_FN size_t xml_attribute::hash_value() const |
5380 | 0 | { |
5381 | 0 | return reinterpret_cast<uintptr_t>(_attr) / sizeof(xml_attribute_struct); |
5382 | 0 | } |
5383 | | |
5384 | | PUGI_IMPL_FN xml_attribute_struct* xml_attribute::internal_object() const |
5385 | 0 | { |
5386 | 0 | return _attr; |
5387 | 0 | } |
5388 | | |
5389 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(const char_t* rhs) |
5390 | 0 | { |
5391 | 0 | set_value(rhs); |
5392 | 0 | return *this; |
5393 | 0 | } |
5394 | | |
5395 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(int rhs) |
5396 | 0 | { |
5397 | 0 | set_value(rhs); |
5398 | 0 | return *this; |
5399 | 0 | } |
5400 | | |
5401 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned int rhs) |
5402 | 0 | { |
5403 | 0 | set_value(rhs); |
5404 | 0 | return *this; |
5405 | 0 | } |
5406 | | |
5407 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long rhs) |
5408 | 0 | { |
5409 | 0 | set_value(rhs); |
5410 | 0 | return *this; |
5411 | 0 | } |
5412 | | |
5413 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long rhs) |
5414 | 0 | { |
5415 | 0 | set_value(rhs); |
5416 | 0 | return *this; |
5417 | 0 | } |
5418 | | |
5419 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(double rhs) |
5420 | 0 | { |
5421 | 0 | set_value(rhs); |
5422 | 0 | return *this; |
5423 | 0 | } |
5424 | | |
5425 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(float rhs) |
5426 | 0 | { |
5427 | 0 | set_value(rhs); |
5428 | 0 | return *this; |
5429 | 0 | } |
5430 | | |
5431 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(bool rhs) |
5432 | 0 | { |
5433 | 0 | set_value(rhs); |
5434 | 0 | return *this; |
5435 | 0 | } |
5436 | | |
5437 | | #ifdef PUGIXML_HAS_STRING_VIEW |
5438 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(string_view_t rhs) |
5439 | 0 | { |
5440 | 0 | set_value(rhs); |
5441 | 0 | return *this; |
5442 | 0 | } |
5443 | | #endif |
5444 | | |
5445 | | #ifdef PUGIXML_HAS_LONG_LONG |
5446 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(long long rhs) |
5447 | 0 | { |
5448 | 0 | set_value(rhs); |
5449 | 0 | return *this; |
5450 | 0 | } |
5451 | | |
5452 | | PUGI_IMPL_FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) |
5453 | 0 | { |
5454 | 0 | set_value(rhs); |
5455 | 0 | return *this; |
5456 | 0 | } |
5457 | | #endif |
5458 | | |
5459 | | PUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs) |
5460 | 0 | { |
5461 | 0 | if (!_attr) return false; |
5462 | | |
5463 | 0 | return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); |
5464 | 0 | } |
5465 | | |
5466 | | PUGI_IMPL_FN bool xml_attribute::set_name(const char_t* rhs, size_t size) |
5467 | 0 | { |
5468 | 0 | if (!_attr) return false; |
5469 | | |
5470 | 0 | return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, size); |
5471 | 0 | } |
5472 | | |
5473 | | #ifdef PUGIXML_HAS_STRING_VIEW |
5474 | | PUGI_IMPL_FN bool xml_attribute::set_name(string_view_t rhs) |
5475 | 0 | { |
5476 | 0 | if (!_attr) return false; |
5477 | | |
5478 | 0 | return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs.data(), rhs.size()); |
5479 | 0 | } |
5480 | | #endif |
5481 | | |
5482 | | PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs) |
5483 | 0 | { |
5484 | 0 | if (!_attr) return false; |
5485 | | |
5486 | 0 | return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); |
5487 | 0 | } |
5488 | | |
5489 | | PUGI_IMPL_FN bool xml_attribute::set_value(const char_t* rhs, size_t size) |
5490 | 0 | { |
5491 | 0 | if (!_attr) return false; |
5492 | | |
5493 | 0 | return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, size); |
5494 | 0 | } |
5495 | | |
5496 | | #ifdef PUGIXML_HAS_STRING_VIEW |
5497 | | PUGI_IMPL_FN bool xml_attribute::set_value(string_view_t rhs) |
5498 | 0 | { |
5499 | 0 | if (!_attr) return false; |
5500 | | |
5501 | 0 | return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size()); |
5502 | 0 | } |
5503 | | #endif |
5504 | | |
5505 | | PUGI_IMPL_FN bool xml_attribute::set_value(int rhs) |
5506 | 0 | { |
5507 | 0 | if (!_attr) return false; |
5508 | | |
5509 | 0 | return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); |
5510 | 0 | } |
5511 | | |
5512 | | PUGI_IMPL_FN bool xml_attribute::set_value(unsigned int rhs) |
5513 | 0 | { |
5514 | 0 | if (!_attr) return false; |
5515 | | |
5516 | 0 | return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); |
5517 | 0 | } |
5518 | | |
5519 | | PUGI_IMPL_FN bool xml_attribute::set_value(long rhs) |
5520 | 0 | { |
5521 | 0 | if (!_attr) return false; |
5522 | | |
5523 | 0 | return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); |
5524 | 0 | } |
5525 | | |
5526 | | PUGI_IMPL_FN bool xml_attribute::set_value(unsigned long rhs) |
5527 | 0 | { |
5528 | 0 | if (!_attr) return false; |
5529 | | |
5530 | 0 | return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); |
5531 | 0 | } |
5532 | | |
5533 | | PUGI_IMPL_FN bool xml_attribute::set_value(double rhs) |
5534 | 0 | { |
5535 | 0 | if (!_attr) return false; |
5536 | | |
5537 | 0 | return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision); |
5538 | 0 | } |
5539 | | |
5540 | | PUGI_IMPL_FN bool xml_attribute::set_value(double rhs, int precision) |
5541 | 0 | { |
5542 | 0 | if (!_attr) return false; |
5543 | | |
5544 | 0 | return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); |
5545 | 0 | } |
5546 | | |
5547 | | PUGI_IMPL_FN bool xml_attribute::set_value(float rhs) |
5548 | 0 | { |
5549 | 0 | if (!_attr) return false; |
5550 | | |
5551 | 0 | return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision); |
5552 | 0 | } |
5553 | | |
5554 | | PUGI_IMPL_FN bool xml_attribute::set_value(float rhs, int precision) |
5555 | 0 | { |
5556 | 0 | if (!_attr) return false; |
5557 | | |
5558 | 0 | return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); |
5559 | 0 | } |
5560 | | |
5561 | | PUGI_IMPL_FN bool xml_attribute::set_value(bool rhs) |
5562 | 0 | { |
5563 | 0 | if (!_attr) return false; |
5564 | | |
5565 | 0 | return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); |
5566 | 0 | } |
5567 | | |
5568 | | #ifdef PUGIXML_HAS_LONG_LONG |
5569 | | PUGI_IMPL_FN bool xml_attribute::set_value(long long rhs) |
5570 | 0 | { |
5571 | 0 | if (!_attr) return false; |
5572 | | |
5573 | 0 | return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); |
5574 | 0 | } |
5575 | | |
5576 | | PUGI_IMPL_FN bool xml_attribute::set_value(unsigned long long rhs) |
5577 | 0 | { |
5578 | 0 | if (!_attr) return false; |
5579 | | |
5580 | 0 | return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); |
5581 | 0 | } |
5582 | | #endif |
5583 | | |
5584 | | #ifdef __BORLANDC__ |
5585 | | PUGI_IMPL_FN bool operator&&(const xml_attribute& lhs, bool rhs) |
5586 | | { |
5587 | | return (bool)lhs && rhs; |
5588 | | } |
5589 | | |
5590 | | PUGI_IMPL_FN bool operator||(const xml_attribute& lhs, bool rhs) |
5591 | | { |
5592 | | return (bool)lhs || rhs; |
5593 | | } |
5594 | | #endif |
5595 | | |
5596 | 3.81k | PUGI_IMPL_FN xml_node::xml_node(): _root(NULL) |
5597 | 3.81k | { |
5598 | 3.81k | } |
5599 | | |
5600 | 0 | PUGI_IMPL_FN xml_node::xml_node(xml_node_struct* p): _root(p) |
5601 | 0 | { |
5602 | 0 | } |
5603 | | |
5604 | | PUGI_IMPL_FN static void unspecified_bool_xml_node(xml_node***) |
5605 | 0 | { |
5606 | 0 | } |
5607 | | |
5608 | | PUGI_IMPL_FN xml_node::operator xml_node::unspecified_bool_type() const |
5609 | 0 | { |
5610 | 0 | return _root ? unspecified_bool_xml_node : NULL; |
5611 | 0 | } |
5612 | | |
5613 | | PUGI_IMPL_FN bool xml_node::operator!() const |
5614 | 0 | { |
5615 | 0 | return !_root; |
5616 | 0 | } |
5617 | | |
5618 | | PUGI_IMPL_FN xml_node::iterator xml_node::begin() const |
5619 | 0 | { |
5620 | 0 | return iterator(_root ? _root->first_child + 0 : NULL, _root); |
5621 | 0 | } |
5622 | | |
5623 | | PUGI_IMPL_FN xml_node::iterator xml_node::end() const |
5624 | 0 | { |
5625 | 0 | return iterator(NULL, _root); |
5626 | 0 | } |
5627 | | |
5628 | | PUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_begin() const |
5629 | 0 | { |
5630 | 0 | return attribute_iterator(_root ? _root->first_attribute + 0 : NULL, _root); |
5631 | 0 | } |
5632 | | |
5633 | | PUGI_IMPL_FN xml_node::attribute_iterator xml_node::attributes_end() const |
5634 | 0 | { |
5635 | 0 | return attribute_iterator(NULL, _root); |
5636 | 0 | } |
5637 | | |
5638 | | PUGI_IMPL_FN xml_object_range<xml_node_iterator> xml_node::children() const |
5639 | 0 | { |
5640 | 0 | return xml_object_range<xml_node_iterator>(begin(), end()); |
5641 | 0 | } |
5642 | | |
5643 | | PUGI_IMPL_FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const |
5644 | 0 | { |
5645 | 0 | return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(NULL, _root, name_)); |
5646 | 0 | } |
5647 | | |
5648 | | PUGI_IMPL_FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const |
5649 | 0 | { |
5650 | 0 | return xml_object_range<xml_attribute_iterator>(attributes_begin(), attributes_end()); |
5651 | 0 | } |
5652 | | |
5653 | | PUGI_IMPL_FN bool xml_node::operator==(const xml_node& r) const |
5654 | 0 | { |
5655 | 0 | return (_root == r._root); |
5656 | 0 | } |
5657 | | |
5658 | | PUGI_IMPL_FN bool xml_node::operator!=(const xml_node& r) const |
5659 | 0 | { |
5660 | 0 | return (_root != r._root); |
5661 | 0 | } |
5662 | | |
5663 | | PUGI_IMPL_FN bool xml_node::operator<(const xml_node& r) const |
5664 | 0 | { |
5665 | 0 | return (_root < r._root); |
5666 | 0 | } |
5667 | | |
5668 | | PUGI_IMPL_FN bool xml_node::operator>(const xml_node& r) const |
5669 | 0 | { |
5670 | 0 | return (_root > r._root); |
5671 | 0 | } |
5672 | | |
5673 | | PUGI_IMPL_FN bool xml_node::operator<=(const xml_node& r) const |
5674 | 0 | { |
5675 | 0 | return (_root <= r._root); |
5676 | 0 | } |
5677 | | |
5678 | | PUGI_IMPL_FN bool xml_node::operator>=(const xml_node& r) const |
5679 | 0 | { |
5680 | 0 | return (_root >= r._root); |
5681 | 0 | } |
5682 | | |
5683 | | PUGI_IMPL_FN bool xml_node::empty() const |
5684 | 0 | { |
5685 | 0 | return !_root; |
5686 | 0 | } |
5687 | | |
5688 | | PUGI_IMPL_FN const char_t* xml_node::name() const |
5689 | 0 | { |
5690 | 0 | if (!_root) return PUGIXML_TEXT(""); |
5691 | 0 | const char_t* name = _root->name; |
5692 | 0 | return name ? name : PUGIXML_TEXT(""); |
5693 | 0 | } |
5694 | | |
5695 | | PUGI_IMPL_FN xml_node_type xml_node::type() const |
5696 | 0 | { |
5697 | 0 | return _root ? PUGI_IMPL_NODETYPE(_root) : node_null; |
5698 | 0 | } |
5699 | | |
5700 | | PUGI_IMPL_FN const char_t* xml_node::value() const |
5701 | 0 | { |
5702 | 0 | if (!_root) return PUGIXML_TEXT(""); |
5703 | 0 | const char_t* value = _root->value; |
5704 | 0 | return value ? value : PUGIXML_TEXT(""); |
5705 | 0 | } |
5706 | | |
5707 | | PUGI_IMPL_FN xml_node xml_node::child(const char_t* name_) const |
5708 | 0 | { |
5709 | 0 | if (!_root) return xml_node(); |
5710 | | |
5711 | 0 | for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) |
5712 | 0 | { |
5713 | 0 | const char_t* iname = i->name; |
5714 | 0 | if (iname && impl::strequal(name_, iname)) |
5715 | 0 | return xml_node(i); |
5716 | 0 | } |
5717 | | |
5718 | 0 | return xml_node(); |
5719 | 0 | } |
5720 | | |
5721 | | PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_) const |
5722 | 0 | { |
5723 | 0 | if (!_root) return xml_attribute(); |
5724 | | |
5725 | 0 | for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) |
5726 | 0 | { |
5727 | 0 | const char_t* iname = i->name; |
5728 | 0 | if (iname && impl::strequal(name_, iname)) |
5729 | 0 | return xml_attribute(i); |
5730 | 0 | } |
5731 | | |
5732 | 0 | return xml_attribute(); |
5733 | 0 | } |
5734 | | |
5735 | | PUGI_IMPL_FN xml_node xml_node::next_sibling(const char_t* name_) const |
5736 | 0 | { |
5737 | 0 | if (!_root) return xml_node(); |
5738 | | |
5739 | 0 | for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) |
5740 | 0 | { |
5741 | 0 | const char_t* iname = i->name; |
5742 | 0 | if (iname && impl::strequal(name_, iname)) |
5743 | 0 | return xml_node(i); |
5744 | 0 | } |
5745 | | |
5746 | 0 | return xml_node(); |
5747 | 0 | } |
5748 | | |
5749 | | PUGI_IMPL_FN xml_node xml_node::next_sibling() const |
5750 | 0 | { |
5751 | 0 | return _root ? xml_node(_root->next_sibling) : xml_node(); |
5752 | 0 | } |
5753 | | |
5754 | | PUGI_IMPL_FN xml_node xml_node::previous_sibling(const char_t* name_) const |
5755 | 0 | { |
5756 | 0 | if (!_root) return xml_node(); |
5757 | | |
5758 | 0 | for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) |
5759 | 0 | { |
5760 | 0 | const char_t* iname = i->name; |
5761 | 0 | if (iname && impl::strequal(name_, iname)) |
5762 | 0 | return xml_node(i); |
5763 | 0 | } |
5764 | | |
5765 | 0 | return xml_node(); |
5766 | 0 | } |
5767 | | |
5768 | | #ifdef PUGIXML_HAS_STRING_VIEW |
5769 | | PUGI_IMPL_FN xml_node xml_node::child(string_view_t name_) const |
5770 | 0 | { |
5771 | 0 | if (!_root) return xml_node(); |
5772 | | |
5773 | 0 | for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) |
5774 | 0 | { |
5775 | 0 | const char_t* iname = i->name; |
5776 | 0 | if (iname && impl::stringview_equal(name_, iname)) |
5777 | 0 | return xml_node(i); |
5778 | 0 | } |
5779 | | |
5780 | 0 | return xml_node(); |
5781 | 0 | } |
5782 | | |
5783 | | PUGI_IMPL_FN xml_attribute xml_node::attribute(string_view_t name_) const |
5784 | 0 | { |
5785 | 0 | if (!_root) return xml_attribute(); |
5786 | | |
5787 | 0 | for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) |
5788 | 0 | { |
5789 | 0 | const char_t* iname = i->name; |
5790 | 0 | if (iname && impl::stringview_equal(name_, iname)) |
5791 | 0 | return xml_attribute(i); |
5792 | 0 | } |
5793 | | |
5794 | 0 | return xml_attribute(); |
5795 | 0 | } |
5796 | | |
5797 | | PUGI_IMPL_FN xml_node xml_node::next_sibling(string_view_t name_) const |
5798 | 0 | { |
5799 | 0 | if (!_root) return xml_node(); |
5800 | | |
5801 | 0 | for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) |
5802 | 0 | { |
5803 | 0 | const char_t* iname = i->name; |
5804 | 0 | if (iname && impl::stringview_equal(name_, iname)) |
5805 | 0 | return xml_node(i); |
5806 | 0 | } |
5807 | | |
5808 | 0 | return xml_node(); |
5809 | 0 | } |
5810 | | |
5811 | | PUGI_IMPL_FN xml_node xml_node::previous_sibling(string_view_t name_) const |
5812 | 0 | { |
5813 | 0 | if (!_root) return xml_node(); |
5814 | | |
5815 | 0 | for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) |
5816 | 0 | { |
5817 | 0 | const char_t* iname = i->name; |
5818 | 0 | if (iname && impl::stringview_equal(name_, iname)) |
5819 | 0 | return xml_node(i); |
5820 | 0 | } |
5821 | | |
5822 | 0 | return xml_node(); |
5823 | 0 | } |
5824 | | #endif |
5825 | | |
5826 | | PUGI_IMPL_FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const |
5827 | 0 | { |
5828 | 0 | xml_attribute_struct* hint = hint_._attr; |
5829 | | |
5830 | | // if hint is not an attribute of node, behavior is not defined |
5831 | 0 | assert(!hint || (_root && impl::is_attribute_of(hint, _root))); |
5832 | | |
5833 | 0 | if (!_root) return xml_attribute(); |
5834 | | |
5835 | | // optimistically search from hint up until the end |
5836 | 0 | for (xml_attribute_struct* i = hint; i; i = i->next_attribute) |
5837 | 0 | { |
5838 | 0 | const char_t* iname = i->name; |
5839 | 0 | if (iname && impl::strequal(name_, iname)) |
5840 | 0 | { |
5841 | | // update hint to maximize efficiency of searching for consecutive attributes |
5842 | 0 | hint_._attr = i->next_attribute; |
5843 | |
|
5844 | 0 | return xml_attribute(i); |
5845 | 0 | } |
5846 | 0 | } |
5847 | | |
5848 | | // wrap around and search from the first attribute until the hint |
5849 | | // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails |
5850 | 0 | for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) |
5851 | 0 | { |
5852 | 0 | const char_t* jname = j->name; |
5853 | 0 | if (jname && impl::strequal(name_, jname)) |
5854 | 0 | { |
5855 | | // update hint to maximize efficiency of searching for consecutive attributes |
5856 | 0 | hint_._attr = j->next_attribute; |
5857 | |
|
5858 | 0 | return xml_attribute(j); |
5859 | 0 | } |
5860 | 0 | } |
5861 | | |
5862 | 0 | return xml_attribute(); |
5863 | 0 | } |
5864 | | |
5865 | | #ifdef PUGIXML_HAS_STRING_VIEW |
5866 | | PUGI_IMPL_FN xml_attribute xml_node::attribute(string_view_t name_, xml_attribute& hint_) const |
5867 | 0 | { |
5868 | 0 | xml_attribute_struct* hint = hint_._attr; |
5869 | | |
5870 | | // if hint is not an attribute of node, behavior is not defined |
5871 | 0 | assert(!hint || (_root && impl::is_attribute_of(hint, _root))); |
5872 | | |
5873 | 0 | if (!_root) return xml_attribute(); |
5874 | | |
5875 | | // optimistically search from hint up until the end |
5876 | 0 | for (xml_attribute_struct* i = hint; i; i = i->next_attribute) |
5877 | 0 | { |
5878 | 0 | const char_t* iname = i->name; |
5879 | 0 | if (iname && impl::stringview_equal(name_, iname)) |
5880 | 0 | { |
5881 | | // update hint to maximize efficiency of searching for consecutive attributes |
5882 | 0 | hint_._attr = i->next_attribute; |
5883 | |
|
5884 | 0 | return xml_attribute(i); |
5885 | 0 | } |
5886 | 0 | } |
5887 | | |
5888 | | // wrap around and search from the first attribute until the hint |
5889 | | // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails |
5890 | 0 | for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) |
5891 | 0 | { |
5892 | 0 | const char_t* jname = j->name; |
5893 | 0 | if (jname && impl::stringview_equal(name_, jname)) |
5894 | 0 | { |
5895 | | // update hint to maximize efficiency of searching for consecutive attributes |
5896 | 0 | hint_._attr = j->next_attribute; |
5897 | |
|
5898 | 0 | return xml_attribute(j); |
5899 | 0 | } |
5900 | 0 | } |
5901 | | |
5902 | 0 | return xml_attribute(); |
5903 | 0 | } |
5904 | | #endif |
5905 | | |
5906 | | PUGI_IMPL_FN xml_node xml_node::previous_sibling() const |
5907 | 0 | { |
5908 | 0 | if (!_root) return xml_node(); |
5909 | 0 | xml_node_struct* prev = _root->prev_sibling_c; |
5910 | 0 | return prev->next_sibling ? xml_node(prev) : xml_node(); |
5911 | 0 | } |
5912 | | |
5913 | | PUGI_IMPL_FN xml_node xml_node::parent() const |
5914 | 0 | { |
5915 | 0 | return _root ? xml_node(_root->parent) : xml_node(); |
5916 | 0 | } |
5917 | | |
5918 | | PUGI_IMPL_FN xml_node xml_node::root() const |
5919 | 0 | { |
5920 | 0 | return _root ? xml_node(&impl::get_document(_root)) : xml_node(); |
5921 | 0 | } |
5922 | | |
5923 | | PUGI_IMPL_FN xml_text xml_node::text() const |
5924 | 0 | { |
5925 | 0 | return xml_text(_root); |
5926 | 0 | } |
5927 | | |
5928 | | PUGI_IMPL_FN const char_t* xml_node::child_value() const |
5929 | 0 | { |
5930 | 0 | if (!_root) return PUGIXML_TEXT(""); |
5931 | | |
5932 | | // element nodes can have value if parse_embed_pcdata was used |
5933 | 0 | if (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value) |
5934 | 0 | return _root->value; |
5935 | | |
5936 | 0 | for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) |
5937 | 0 | { |
5938 | 0 | const char_t* ivalue = i->value; |
5939 | 0 | if (impl::is_text_node(i) && ivalue) |
5940 | 0 | return ivalue; |
5941 | 0 | } |
5942 | | |
5943 | 0 | return PUGIXML_TEXT(""); |
5944 | 0 | } |
5945 | | |
5946 | | PUGI_IMPL_FN const char_t* xml_node::child_value(const char_t* name_) const |
5947 | 0 | { |
5948 | 0 | return child(name_).child_value(); |
5949 | 0 | } |
5950 | | |
5951 | | PUGI_IMPL_FN xml_attribute xml_node::first_attribute() const |
5952 | 0 | { |
5953 | 0 | if (!_root) return xml_attribute(); |
5954 | 0 | return xml_attribute(_root->first_attribute); |
5955 | 0 | } |
5956 | | |
5957 | | PUGI_IMPL_FN xml_attribute xml_node::last_attribute() const |
5958 | 0 | { |
5959 | 0 | if (!_root) return xml_attribute(); |
5960 | 0 | xml_attribute_struct* first = _root->first_attribute; |
5961 | 0 | return first ? xml_attribute(first->prev_attribute_c) : xml_attribute(); |
5962 | 0 | } |
5963 | | |
5964 | | PUGI_IMPL_FN xml_node xml_node::first_child() const |
5965 | 0 | { |
5966 | 0 | if (!_root) return xml_node(); |
5967 | 0 | return xml_node(_root->first_child); |
5968 | 0 | } |
5969 | | |
5970 | | PUGI_IMPL_FN xml_node xml_node::last_child() const |
5971 | 0 | { |
5972 | 0 | if (!_root) return xml_node(); |
5973 | 0 | xml_node_struct* first = _root->first_child; |
5974 | 0 | return first ? xml_node(first->prev_sibling_c) : xml_node(); |
5975 | 0 | } |
5976 | | |
5977 | | PUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs) |
5978 | 0 | { |
5979 | 0 | xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; |
5980 | |
|
5981 | 0 | if (type_ != node_element && type_ != node_pi && type_ != node_declaration) |
5982 | 0 | return false; |
5983 | | |
5984 | 0 | return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); |
5985 | 0 | } |
5986 | | |
5987 | | PUGI_IMPL_FN bool xml_node::set_name(const char_t* rhs, size_t size) |
5988 | 0 | { |
5989 | 0 | xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; |
5990 | |
|
5991 | 0 | if (type_ != node_element && type_ != node_pi && type_ != node_declaration) |
5992 | 0 | return false; |
5993 | | |
5994 | 0 | return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, size); |
5995 | 0 | } |
5996 | | |
5997 | | #ifdef PUGIXML_HAS_STRING_VIEW |
5998 | | PUGI_IMPL_FN bool xml_node::set_name(string_view_t rhs) |
5999 | 0 | { |
6000 | 0 | xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; |
6001 | |
|
6002 | 0 | if (type_ != node_element && type_ != node_pi && type_ != node_declaration) |
6003 | 0 | return false; |
6004 | | |
6005 | 0 | return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs.data(), rhs.size()); |
6006 | 0 | } |
6007 | | #endif |
6008 | | |
6009 | | PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs) |
6010 | 0 | { |
6011 | 0 | xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; |
6012 | |
|
6013 | 0 | if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) |
6014 | 0 | return false; |
6015 | | |
6016 | 0 | return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); |
6017 | 0 | } |
6018 | | |
6019 | | PUGI_IMPL_FN bool xml_node::set_value(const char_t* rhs, size_t size) |
6020 | 0 | { |
6021 | 0 | xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; |
6022 | |
|
6023 | 0 | if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) |
6024 | 0 | return false; |
6025 | | |
6026 | 0 | return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, size); |
6027 | 0 | } |
6028 | | |
6029 | | #ifdef PUGIXML_HAS_STRING_VIEW |
6030 | | PUGI_IMPL_FN bool xml_node::set_value(string_view_t rhs) |
6031 | 0 | { |
6032 | 0 | xml_node_type type_ = _root ? PUGI_IMPL_NODETYPE(_root) : node_null; |
6033 | |
|
6034 | 0 | if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) |
6035 | 0 | return false; |
6036 | | |
6037 | 0 | return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size()); |
6038 | 0 | } |
6039 | | #endif |
6040 | | |
6041 | | PUGI_IMPL_FN xml_attribute xml_node::append_attribute(const char_t* name_) |
6042 | 0 | { |
6043 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6044 | | |
6045 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6046 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6047 | | |
6048 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6049 | 0 | if (!a) return xml_attribute(); |
6050 | | |
6051 | 0 | impl::append_attribute(a._attr, _root); |
6052 | |
|
6053 | 0 | a.set_name(name_); |
6054 | |
|
6055 | 0 | return a; |
6056 | 0 | } |
6057 | | |
6058 | | PUGI_IMPL_FN xml_attribute xml_node::prepend_attribute(const char_t* name_) |
6059 | 0 | { |
6060 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6061 | | |
6062 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6063 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6064 | | |
6065 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6066 | 0 | if (!a) return xml_attribute(); |
6067 | | |
6068 | 0 | impl::prepend_attribute(a._attr, _root); |
6069 | |
|
6070 | 0 | a.set_name(name_); |
6071 | |
|
6072 | 0 | return a; |
6073 | 0 | } |
6074 | | |
6075 | | PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) |
6076 | 0 | { |
6077 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6078 | 0 | if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); |
6079 | | |
6080 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6081 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6082 | | |
6083 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6084 | 0 | if (!a) return xml_attribute(); |
6085 | | |
6086 | 0 | impl::insert_attribute_after(a._attr, attr._attr, _root); |
6087 | |
|
6088 | 0 | a.set_name(name_); |
6089 | |
|
6090 | 0 | return a; |
6091 | 0 | } |
6092 | | |
6093 | | PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) |
6094 | 0 | { |
6095 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6096 | 0 | if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); |
6097 | | |
6098 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6099 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6100 | | |
6101 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6102 | 0 | if (!a) return xml_attribute(); |
6103 | | |
6104 | 0 | impl::insert_attribute_before(a._attr, attr._attr, _root); |
6105 | |
|
6106 | 0 | a.set_name(name_); |
6107 | |
|
6108 | 0 | return a; |
6109 | 0 | } |
6110 | | |
6111 | | #ifdef PUGIXML_HAS_STRING_VIEW |
6112 | | PUGI_IMPL_FN xml_attribute xml_node::append_attribute(string_view_t name_) |
6113 | 0 | { |
6114 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6115 | | |
6116 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6117 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6118 | | |
6119 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6120 | 0 | if (!a) return xml_attribute(); |
6121 | | |
6122 | 0 | impl::append_attribute(a._attr, _root); |
6123 | |
|
6124 | 0 | a.set_name(name_); |
6125 | |
|
6126 | 0 | return a; |
6127 | 0 | } |
6128 | | |
6129 | | PUGI_IMPL_FN xml_attribute xml_node::prepend_attribute(string_view_t name_) |
6130 | 0 | { |
6131 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6132 | | |
6133 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6134 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6135 | | |
6136 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6137 | 0 | if (!a) return xml_attribute(); |
6138 | | |
6139 | 0 | impl::prepend_attribute(a._attr, _root); |
6140 | |
|
6141 | 0 | a.set_name(name_); |
6142 | |
|
6143 | 0 | return a; |
6144 | 0 | } |
6145 | | |
6146 | | PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_after(string_view_t name_, const xml_attribute& attr) |
6147 | 0 | { |
6148 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6149 | 0 | if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); |
6150 | | |
6151 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6152 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6153 | | |
6154 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6155 | 0 | if (!a) return xml_attribute(); |
6156 | | |
6157 | 0 | impl::insert_attribute_after(a._attr, attr._attr, _root); |
6158 | |
|
6159 | 0 | a.set_name(name_); |
6160 | |
|
6161 | 0 | return a; |
6162 | 0 | } |
6163 | | |
6164 | | PUGI_IMPL_FN xml_attribute xml_node::insert_attribute_before(string_view_t name_, const xml_attribute& attr) |
6165 | 0 | { |
6166 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6167 | 0 | if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); |
6168 | | |
6169 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6170 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6171 | | |
6172 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6173 | 0 | if (!a) return xml_attribute(); |
6174 | | |
6175 | 0 | impl::insert_attribute_before(a._attr, attr._attr, _root); |
6176 | |
|
6177 | 0 | a.set_name(name_); |
6178 | |
|
6179 | 0 | return a; |
6180 | 0 | } |
6181 | | #endif |
6182 | | |
6183 | | PUGI_IMPL_FN xml_attribute xml_node::append_copy(const xml_attribute& proto) |
6184 | 0 | { |
6185 | 0 | if (!proto) return xml_attribute(); |
6186 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6187 | | |
6188 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6189 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6190 | | |
6191 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6192 | 0 | if (!a) return xml_attribute(); |
6193 | | |
6194 | 0 | impl::append_attribute(a._attr, _root); |
6195 | 0 | impl::node_copy_attribute(a._attr, proto._attr); |
6196 | |
|
6197 | 0 | return a; |
6198 | 0 | } |
6199 | | |
6200 | | PUGI_IMPL_FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) |
6201 | 0 | { |
6202 | 0 | if (!proto) return xml_attribute(); |
6203 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6204 | | |
6205 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6206 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6207 | | |
6208 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6209 | 0 | if (!a) return xml_attribute(); |
6210 | | |
6211 | 0 | impl::prepend_attribute(a._attr, _root); |
6212 | 0 | impl::node_copy_attribute(a._attr, proto._attr); |
6213 | |
|
6214 | 0 | return a; |
6215 | 0 | } |
6216 | | |
6217 | | PUGI_IMPL_FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) |
6218 | 0 | { |
6219 | 0 | if (!proto) return xml_attribute(); |
6220 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6221 | 0 | if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); |
6222 | | |
6223 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6224 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6225 | | |
6226 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6227 | 0 | if (!a) return xml_attribute(); |
6228 | | |
6229 | 0 | impl::insert_attribute_after(a._attr, attr._attr, _root); |
6230 | 0 | impl::node_copy_attribute(a._attr, proto._attr); |
6231 | |
|
6232 | 0 | return a; |
6233 | 0 | } |
6234 | | |
6235 | | PUGI_IMPL_FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) |
6236 | 0 | { |
6237 | 0 | if (!proto) return xml_attribute(); |
6238 | 0 | if (!impl::allow_insert_attribute(type())) return xml_attribute(); |
6239 | 0 | if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); |
6240 | | |
6241 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6242 | 0 | if (!alloc.reserve()) return xml_attribute(); |
6243 | | |
6244 | 0 | xml_attribute a(impl::allocate_attribute(alloc)); |
6245 | 0 | if (!a) return xml_attribute(); |
6246 | | |
6247 | 0 | impl::insert_attribute_before(a._attr, attr._attr, _root); |
6248 | 0 | impl::node_copy_attribute(a._attr, proto._attr); |
6249 | |
|
6250 | 0 | return a; |
6251 | 0 | } |
6252 | | |
6253 | | PUGI_IMPL_FN xml_node xml_node::append_child(xml_node_type type_) |
6254 | 0 | { |
6255 | 0 | if (!impl::allow_insert_child(type(), type_)) return xml_node(); |
6256 | | |
6257 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6258 | 0 | if (!alloc.reserve()) return xml_node(); |
6259 | | |
6260 | 0 | xml_node n(impl::allocate_node(alloc, type_)); |
6261 | 0 | if (!n) return xml_node(); |
6262 | | |
6263 | 0 | impl::append_node(n._root, _root); |
6264 | |
|
6265 | 0 | if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); |
6266 | |
|
6267 | 0 | return n; |
6268 | 0 | } |
6269 | | |
6270 | | PUGI_IMPL_FN xml_node xml_node::prepend_child(xml_node_type type_) |
6271 | 0 | { |
6272 | 0 | if (!impl::allow_insert_child(type(), type_)) return xml_node(); |
6273 | | |
6274 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6275 | 0 | if (!alloc.reserve()) return xml_node(); |
6276 | | |
6277 | 0 | xml_node n(impl::allocate_node(alloc, type_)); |
6278 | 0 | if (!n) return xml_node(); |
6279 | | |
6280 | 0 | impl::prepend_node(n._root, _root); |
6281 | |
|
6282 | 0 | if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); |
6283 | |
|
6284 | 0 | return n; |
6285 | 0 | } |
6286 | | |
6287 | | PUGI_IMPL_FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) |
6288 | 0 | { |
6289 | 0 | if (!impl::allow_insert_child(type(), type_)) return xml_node(); |
6290 | 0 | if (!node._root || node._root->parent != _root) return xml_node(); |
6291 | | |
6292 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6293 | 0 | if (!alloc.reserve()) return xml_node(); |
6294 | | |
6295 | 0 | xml_node n(impl::allocate_node(alloc, type_)); |
6296 | 0 | if (!n) return xml_node(); |
6297 | | |
6298 | 0 | impl::insert_node_before(n._root, node._root); |
6299 | |
|
6300 | 0 | if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); |
6301 | |
|
6302 | 0 | return n; |
6303 | 0 | } |
6304 | | |
6305 | | PUGI_IMPL_FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) |
6306 | 0 | { |
6307 | 0 | if (!impl::allow_insert_child(type(), type_)) return xml_node(); |
6308 | 0 | if (!node._root || node._root->parent != _root) return xml_node(); |
6309 | | |
6310 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6311 | 0 | if (!alloc.reserve()) return xml_node(); |
6312 | | |
6313 | 0 | xml_node n(impl::allocate_node(alloc, type_)); |
6314 | 0 | if (!n) return xml_node(); |
6315 | | |
6316 | 0 | impl::insert_node_after(n._root, node._root); |
6317 | |
|
6318 | 0 | if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); |
6319 | |
|
6320 | 0 | return n; |
6321 | 0 | } |
6322 | | |
6323 | | PUGI_IMPL_FN xml_node xml_node::append_child(const char_t* name_) |
6324 | 0 | { |
6325 | 0 | xml_node result = append_child(node_element); |
6326 | |
|
6327 | 0 | result.set_name(name_); |
6328 | |
|
6329 | 0 | return result; |
6330 | 0 | } |
6331 | | |
6332 | | PUGI_IMPL_FN xml_node xml_node::prepend_child(const char_t* name_) |
6333 | 0 | { |
6334 | 0 | xml_node result = prepend_child(node_element); |
6335 | |
|
6336 | 0 | result.set_name(name_); |
6337 | |
|
6338 | 0 | return result; |
6339 | 0 | } |
6340 | | |
6341 | | PUGI_IMPL_FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) |
6342 | 0 | { |
6343 | 0 | xml_node result = insert_child_after(node_element, node); |
6344 | |
|
6345 | 0 | result.set_name(name_); |
6346 | |
|
6347 | 0 | return result; |
6348 | 0 | } |
6349 | | |
6350 | | PUGI_IMPL_FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) |
6351 | 0 | { |
6352 | 0 | xml_node result = insert_child_before(node_element, node); |
6353 | |
|
6354 | 0 | result.set_name(name_); |
6355 | |
|
6356 | 0 | return result; |
6357 | 0 | } |
6358 | | |
6359 | | #ifdef PUGIXML_HAS_STRING_VIEW |
6360 | | PUGI_IMPL_FN xml_node xml_node::append_child(string_view_t name_) |
6361 | 0 | { |
6362 | 0 | xml_node result = append_child(node_element); |
6363 | |
|
6364 | 0 | result.set_name(name_); |
6365 | |
|
6366 | 0 | return result; |
6367 | 0 | } |
6368 | | |
6369 | | PUGI_IMPL_FN xml_node xml_node::prepend_child(string_view_t name_) |
6370 | 0 | { |
6371 | 0 | xml_node result = prepend_child(node_element); |
6372 | |
|
6373 | 0 | result.set_name(name_); |
6374 | |
|
6375 | 0 | return result; |
6376 | 0 | } |
6377 | | |
6378 | | PUGI_IMPL_FN xml_node xml_node::insert_child_after(string_view_t name_, const xml_node& node) |
6379 | 0 | { |
6380 | 0 | xml_node result = insert_child_after(node_element, node); |
6381 | |
|
6382 | 0 | result.set_name(name_); |
6383 | |
|
6384 | 0 | return result; |
6385 | 0 | } |
6386 | | |
6387 | | PUGI_IMPL_FN xml_node xml_node::insert_child_before(string_view_t name_, const xml_node& node) |
6388 | 0 | { |
6389 | 0 | xml_node result = insert_child_before(node_element, node); |
6390 | |
|
6391 | 0 | result.set_name(name_); |
6392 | |
|
6393 | 0 | return result; |
6394 | 0 | } |
6395 | | #endif |
6396 | | |
6397 | | PUGI_IMPL_FN xml_node xml_node::append_copy(const xml_node& proto) |
6398 | 0 | { |
6399 | 0 | xml_node_type type_ = proto.type(); |
6400 | 0 | if (!impl::allow_insert_child(type(), type_)) return xml_node(); |
6401 | | |
6402 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6403 | 0 | if (!alloc.reserve()) return xml_node(); |
6404 | | |
6405 | 0 | xml_node n(impl::allocate_node(alloc, type_)); |
6406 | 0 | if (!n) return xml_node(); |
6407 | | |
6408 | 0 | impl::append_node(n._root, _root); |
6409 | 0 | impl::node_copy_tree(n._root, proto._root); |
6410 | |
|
6411 | 0 | return n; |
6412 | 0 | } |
6413 | | |
6414 | | PUGI_IMPL_FN xml_node xml_node::prepend_copy(const xml_node& proto) |
6415 | 0 | { |
6416 | 0 | xml_node_type type_ = proto.type(); |
6417 | 0 | if (!impl::allow_insert_child(type(), type_)) return xml_node(); |
6418 | | |
6419 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6420 | 0 | if (!alloc.reserve()) return xml_node(); |
6421 | | |
6422 | 0 | xml_node n(impl::allocate_node(alloc, type_)); |
6423 | 0 | if (!n) return xml_node(); |
6424 | | |
6425 | 0 | impl::prepend_node(n._root, _root); |
6426 | 0 | impl::node_copy_tree(n._root, proto._root); |
6427 | |
|
6428 | 0 | return n; |
6429 | 0 | } |
6430 | | |
6431 | | PUGI_IMPL_FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) |
6432 | 0 | { |
6433 | 0 | xml_node_type type_ = proto.type(); |
6434 | 0 | if (!impl::allow_insert_child(type(), type_)) return xml_node(); |
6435 | 0 | if (!node._root || node._root->parent != _root) return xml_node(); |
6436 | | |
6437 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6438 | 0 | if (!alloc.reserve()) return xml_node(); |
6439 | | |
6440 | 0 | xml_node n(impl::allocate_node(alloc, type_)); |
6441 | 0 | if (!n) return xml_node(); |
6442 | | |
6443 | 0 | impl::insert_node_after(n._root, node._root); |
6444 | 0 | impl::node_copy_tree(n._root, proto._root); |
6445 | |
|
6446 | 0 | return n; |
6447 | 0 | } |
6448 | | |
6449 | | PUGI_IMPL_FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) |
6450 | 0 | { |
6451 | 0 | xml_node_type type_ = proto.type(); |
6452 | 0 | if (!impl::allow_insert_child(type(), type_)) return xml_node(); |
6453 | 0 | if (!node._root || node._root->parent != _root) return xml_node(); |
6454 | | |
6455 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6456 | 0 | if (!alloc.reserve()) return xml_node(); |
6457 | | |
6458 | 0 | xml_node n(impl::allocate_node(alloc, type_)); |
6459 | 0 | if (!n) return xml_node(); |
6460 | | |
6461 | 0 | impl::insert_node_before(n._root, node._root); |
6462 | 0 | impl::node_copy_tree(n._root, proto._root); |
6463 | |
|
6464 | 0 | return n; |
6465 | 0 | } |
6466 | | |
6467 | | PUGI_IMPL_FN xml_node xml_node::append_move(const xml_node& moved) |
6468 | 0 | { |
6469 | 0 | if (!impl::allow_move(*this, moved)) return xml_node(); |
6470 | | |
6471 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6472 | 0 | if (!alloc.reserve()) return xml_node(); |
6473 | | |
6474 | | // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers |
6475 | 0 | impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; |
6476 | |
|
6477 | 0 | impl::remove_node(moved._root); |
6478 | 0 | impl::append_node(moved._root, _root); |
6479 | |
|
6480 | 0 | return moved; |
6481 | 0 | } |
6482 | | |
6483 | | PUGI_IMPL_FN xml_node xml_node::prepend_move(const xml_node& moved) |
6484 | 0 | { |
6485 | 0 | if (!impl::allow_move(*this, moved)) return xml_node(); |
6486 | | |
6487 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6488 | 0 | if (!alloc.reserve()) return xml_node(); |
6489 | | |
6490 | | // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers |
6491 | 0 | impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; |
6492 | |
|
6493 | 0 | impl::remove_node(moved._root); |
6494 | 0 | impl::prepend_node(moved._root, _root); |
6495 | |
|
6496 | 0 | return moved; |
6497 | 0 | } |
6498 | | |
6499 | | PUGI_IMPL_FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) |
6500 | 0 | { |
6501 | 0 | if (!impl::allow_move(*this, moved)) return xml_node(); |
6502 | 0 | if (!node._root || node._root->parent != _root) return xml_node(); |
6503 | 0 | if (moved._root == node._root) return xml_node(); |
6504 | | |
6505 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6506 | 0 | if (!alloc.reserve()) return xml_node(); |
6507 | | |
6508 | | // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers |
6509 | 0 | impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; |
6510 | |
|
6511 | 0 | impl::remove_node(moved._root); |
6512 | 0 | impl::insert_node_after(moved._root, node._root); |
6513 | |
|
6514 | 0 | return moved; |
6515 | 0 | } |
6516 | | |
6517 | | PUGI_IMPL_FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) |
6518 | 0 | { |
6519 | 0 | if (!impl::allow_move(*this, moved)) return xml_node(); |
6520 | 0 | if (!node._root || node._root->parent != _root) return xml_node(); |
6521 | 0 | if (moved._root == node._root) return xml_node(); |
6522 | | |
6523 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6524 | 0 | if (!alloc.reserve()) return xml_node(); |
6525 | | |
6526 | | // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers |
6527 | 0 | impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; |
6528 | |
|
6529 | 0 | impl::remove_node(moved._root); |
6530 | 0 | impl::insert_node_before(moved._root, node._root); |
6531 | |
|
6532 | 0 | return moved; |
6533 | 0 | } |
6534 | | |
6535 | | PUGI_IMPL_FN bool xml_node::remove_attribute(const char_t* name_) |
6536 | 0 | { |
6537 | 0 | return remove_attribute(attribute(name_)); |
6538 | 0 | } |
6539 | | |
6540 | | #ifdef PUGIXML_HAS_STRING_VIEW |
6541 | | PUGI_IMPL_FN bool xml_node::remove_attribute(string_view_t name_) |
6542 | 0 | { |
6543 | 0 | return remove_attribute(attribute(name_)); |
6544 | 0 | } |
6545 | | #endif |
6546 | | |
6547 | | PUGI_IMPL_FN bool xml_node::remove_attribute(const xml_attribute& a) |
6548 | 0 | { |
6549 | 0 | if (!_root || !a._attr) return false; |
6550 | 0 | if (!impl::is_attribute_of(a._attr, _root)) return false; |
6551 | | |
6552 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6553 | 0 | if (!alloc.reserve()) return false; |
6554 | | |
6555 | 0 | impl::remove_attribute(a._attr, _root); |
6556 | 0 | impl::destroy_attribute(a._attr, alloc); |
6557 | |
|
6558 | 0 | return true; |
6559 | 0 | } |
6560 | | |
6561 | | PUGI_IMPL_FN bool xml_node::remove_attributes() |
6562 | 0 | { |
6563 | 0 | if (!_root) return false; |
6564 | | |
6565 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6566 | 0 | if (!alloc.reserve()) return false; |
6567 | | |
6568 | 0 | for (xml_attribute_struct* attr = _root->first_attribute; attr; ) |
6569 | 0 | { |
6570 | 0 | xml_attribute_struct* next = attr->next_attribute; |
6571 | |
|
6572 | 0 | impl::destroy_attribute(attr, alloc); |
6573 | |
|
6574 | 0 | attr = next; |
6575 | 0 | } |
6576 | |
|
6577 | 0 | _root->first_attribute = NULL; |
6578 | |
|
6579 | 0 | return true; |
6580 | 0 | } |
6581 | | |
6582 | | PUGI_IMPL_FN bool xml_node::remove_child(const char_t* name_) |
6583 | 0 | { |
6584 | 0 | return remove_child(child(name_)); |
6585 | 0 | } |
6586 | | |
6587 | | #ifdef PUGIXML_HAS_STRING_VIEW |
6588 | | PUGI_IMPL_FN bool xml_node::remove_child(string_view_t name_) |
6589 | 0 | { |
6590 | 0 | return remove_child(child(name_)); |
6591 | 0 | } |
6592 | | #endif |
6593 | | |
6594 | | PUGI_IMPL_FN bool xml_node::remove_child(const xml_node& n) |
6595 | 0 | { |
6596 | 0 | if (!_root || !n._root || n._root->parent != _root) return false; |
6597 | | |
6598 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6599 | 0 | if (!alloc.reserve()) return false; |
6600 | | |
6601 | 0 | impl::remove_node(n._root); |
6602 | 0 | impl::destroy_node(n._root, alloc); |
6603 | |
|
6604 | 0 | return true; |
6605 | 0 | } |
6606 | | |
6607 | | PUGI_IMPL_FN bool xml_node::remove_children() |
6608 | 0 | { |
6609 | 0 | if (!_root) return false; |
6610 | | |
6611 | 0 | impl::xml_allocator& alloc = impl::get_allocator(_root); |
6612 | 0 | if (!alloc.reserve()) return false; |
6613 | | |
6614 | 0 | for (xml_node_struct* cur = _root->first_child; cur; ) |
6615 | 0 | { |
6616 | 0 | xml_node_struct* next = cur->next_sibling; |
6617 | |
|
6618 | 0 | impl::destroy_node(cur, alloc); |
6619 | |
|
6620 | 0 | cur = next; |
6621 | 0 | } |
6622 | |
|
6623 | 0 | _root->first_child = NULL; |
6624 | |
|
6625 | 0 | return true; |
6626 | 0 | } |
6627 | | |
6628 | | PUGI_IMPL_FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) |
6629 | 0 | { |
6630 | | // append_buffer is only valid for elements/documents |
6631 | 0 | if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); |
6632 | | |
6633 | | // append buffer can not merge PCDATA into existing PCDATA nodes |
6634 | 0 | if ((options & parse_merge_pcdata) != 0 && last_child().type() == node_pcdata) return impl::make_parse_result(status_append_invalid_root); |
6635 | | |
6636 | | // get document node |
6637 | 0 | impl::xml_document_struct* doc = &impl::get_document(_root); |
6638 | | |
6639 | | // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense |
6640 | 0 | doc->header |= impl::xml_memory_page_contents_shared_mask; |
6641 | | |
6642 | | // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) |
6643 | 0 | impl::xml_memory_page* page = NULL; |
6644 | 0 | impl::xml_extra_buffer* extra = static_cast<impl::xml_extra_buffer*>(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page)); |
6645 | 0 | (void)page; |
6646 | |
|
6647 | 0 | if (!extra) return impl::make_parse_result(status_out_of_memory); |
6648 | | |
6649 | | #ifdef PUGIXML_COMPACT |
6650 | | // align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned |
6651 | | // note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account |
6652 | | extra = reinterpret_cast<impl::xml_extra_buffer*>((reinterpret_cast<uintptr_t>(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1)); |
6653 | | #endif |
6654 | | |
6655 | | // add extra buffer to the list |
6656 | 0 | extra->buffer = NULL; |
6657 | 0 | extra->next = doc->extra_buffers; |
6658 | 0 | doc->extra_buffers = extra; |
6659 | | |
6660 | | // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level |
6661 | 0 | impl::name_null_sentry sentry(_root); |
6662 | |
|
6663 | 0 | return impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &extra->buffer); |
6664 | 0 | } |
6665 | | |
6666 | | PUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const |
6667 | 0 | { |
6668 | 0 | if (!_root) return xml_node(); |
6669 | | |
6670 | 0 | for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) |
6671 | 0 | { |
6672 | 0 | const char_t* iname = i->name; |
6673 | 0 | if (iname && impl::strequal(name_, iname)) |
6674 | 0 | { |
6675 | 0 | for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) |
6676 | 0 | { |
6677 | 0 | const char_t* aname = a->name; |
6678 | 0 | if (aname && impl::strequal(attr_name, aname)) |
6679 | 0 | { |
6680 | 0 | const char_t* avalue = a->value; |
6681 | 0 | if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(""))) |
6682 | 0 | return xml_node(i); |
6683 | 0 | } |
6684 | 0 | } |
6685 | 0 | } |
6686 | 0 | } |
6687 | | |
6688 | 0 | return xml_node(); |
6689 | 0 | } |
6690 | | |
6691 | | PUGI_IMPL_FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const |
6692 | 0 | { |
6693 | 0 | if (!_root) return xml_node(); |
6694 | | |
6695 | 0 | for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) |
6696 | 0 | for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) |
6697 | 0 | { |
6698 | 0 | const char_t* aname = a->name; |
6699 | 0 | if (aname && impl::strequal(attr_name, aname)) |
6700 | 0 | { |
6701 | 0 | const char_t* avalue = a->value; |
6702 | 0 | if (impl::strequal(attr_value, avalue ? avalue : PUGIXML_TEXT(""))) |
6703 | 0 | return xml_node(i); |
6704 | 0 | } |
6705 | 0 | } |
6706 | | |
6707 | 0 | return xml_node(); |
6708 | 0 | } |
6709 | | |
6710 | | #ifndef PUGIXML_NO_STL |
6711 | | PUGI_IMPL_FN string_t xml_node::path(char_t delimiter) const |
6712 | 0 | { |
6713 | 0 | if (!_root) return string_t(); |
6714 | | |
6715 | 0 | size_t offset = 0; |
6716 | |
|
6717 | 0 | for (xml_node_struct* i = _root; i; i = i->parent) |
6718 | 0 | { |
6719 | 0 | const char_t* iname = i->name; |
6720 | 0 | offset += (i != _root); |
6721 | 0 | offset += iname ? impl::strlength(iname) : 0; |
6722 | 0 | } |
6723 | |
|
6724 | 0 | string_t result; |
6725 | 0 | result.resize(offset); |
6726 | |
|
6727 | 0 | for (xml_node_struct* j = _root; j; j = j->parent) |
6728 | 0 | { |
6729 | 0 | if (j != _root) |
6730 | 0 | result[--offset] = delimiter; |
6731 | |
|
6732 | 0 | const char_t* jname = j->name; |
6733 | 0 | if (jname) |
6734 | 0 | { |
6735 | 0 | size_t length = impl::strlength(jname); |
6736 | |
|
6737 | 0 | offset -= length; |
6738 | 0 | memcpy(&result[offset], jname, length * sizeof(char_t)); |
6739 | 0 | } |
6740 | 0 | } |
6741 | |
|
6742 | 0 | assert(offset == 0); |
6743 | | |
6744 | 0 | return result; |
6745 | 0 | } |
6746 | | #endif |
6747 | | |
6748 | | PUGI_IMPL_FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const |
6749 | 0 | { |
6750 | 0 | xml_node context = path_[0] == delimiter ? root() : *this; |
6751 | |
|
6752 | 0 | if (!context._root) return xml_node(); |
6753 | | |
6754 | 0 | const char_t* path_segment = path_; |
6755 | |
|
6756 | 0 | while (*path_segment == delimiter) ++path_segment; |
6757 | |
|
6758 | 0 | const char_t* path_segment_end = path_segment; |
6759 | |
|
6760 | 0 | while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; |
6761 | |
|
6762 | 0 | if (path_segment == path_segment_end) return context; |
6763 | | |
6764 | 0 | const char_t* next_segment = path_segment_end; |
6765 | |
|
6766 | 0 | while (*next_segment == delimiter) ++next_segment; |
6767 | |
|
6768 | 0 | if (*path_segment == '.' && path_segment + 1 == path_segment_end) |
6769 | 0 | return context.first_element_by_path(next_segment, delimiter); |
6770 | 0 | else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) |
6771 | 0 | return context.parent().first_element_by_path(next_segment, delimiter); |
6772 | 0 | else |
6773 | 0 | { |
6774 | 0 | for (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling) |
6775 | 0 | { |
6776 | 0 | const char_t* jname = j->name; |
6777 | 0 | if (jname && impl::strequalrange(jname, path_segment, static_cast<size_t>(path_segment_end - path_segment))) |
6778 | 0 | { |
6779 | 0 | xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); |
6780 | |
|
6781 | 0 | if (subsearch) return subsearch; |
6782 | 0 | } |
6783 | 0 | } |
6784 | | |
6785 | 0 | return xml_node(); |
6786 | 0 | } |
6787 | 0 | } |
6788 | | |
6789 | | PUGI_IMPL_FN bool xml_node::traverse(xml_tree_walker& walker) |
6790 | 0 | { |
6791 | 0 | walker._depth = -1; |
6792 | |
|
6793 | 0 | xml_node arg_begin(_root); |
6794 | 0 | if (!walker.begin(arg_begin)) return false; |
6795 | | |
6796 | 0 | xml_node_struct* cur = _root ? _root->first_child + 0 : NULL; |
6797 | |
|
6798 | 0 | if (cur) |
6799 | 0 | { |
6800 | 0 | ++walker._depth; |
6801 | |
|
6802 | 0 | do |
6803 | 0 | { |
6804 | 0 | xml_node arg_for_each(cur); |
6805 | 0 | if (!walker.for_each(arg_for_each)) |
6806 | 0 | return false; |
6807 | | |
6808 | 0 | if (cur->first_child) |
6809 | 0 | { |
6810 | 0 | ++walker._depth; |
6811 | 0 | cur = cur->first_child; |
6812 | 0 | } |
6813 | 0 | else if (cur->next_sibling) |
6814 | 0 | cur = cur->next_sibling; |
6815 | 0 | else |
6816 | 0 | { |
6817 | 0 | while (!cur->next_sibling && cur != _root && cur->parent) |
6818 | 0 | { |
6819 | 0 | --walker._depth; |
6820 | 0 | cur = cur->parent; |
6821 | 0 | } |
6822 | |
|
6823 | 0 | if (cur != _root) |
6824 | 0 | cur = cur->next_sibling; |
6825 | 0 | } |
6826 | 0 | } |
6827 | 0 | while (cur && cur != _root); |
6828 | 0 | } |
6829 | | |
6830 | 0 | assert(walker._depth == -1); |
6831 | | |
6832 | 0 | xml_node arg_end(_root); |
6833 | 0 | return walker.end(arg_end); |
6834 | 0 | } |
6835 | | |
6836 | | PUGI_IMPL_FN size_t xml_node::hash_value() const |
6837 | 0 | { |
6838 | 0 | return reinterpret_cast<uintptr_t>(_root) / sizeof(xml_node_struct); |
6839 | 0 | } |
6840 | | |
6841 | | PUGI_IMPL_FN xml_node_struct* xml_node::internal_object() const |
6842 | 0 | { |
6843 | 0 | return _root; |
6844 | 0 | } |
6845 | | |
6846 | | PUGI_IMPL_FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const |
6847 | 0 | { |
6848 | 0 | if (!_root) return; |
6849 | | |
6850 | 0 | impl::xml_buffered_writer buffered_writer(writer, encoding); |
6851 | |
|
6852 | 0 | impl::node_output(buffered_writer, _root, indent, flags, depth); |
6853 | |
|
6854 | 0 | buffered_writer.flush(); |
6855 | 0 | } |
6856 | | |
6857 | | #ifndef PUGIXML_NO_STL |
6858 | | PUGI_IMPL_FN void xml_node::print(std::basic_ostream<char>& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const |
6859 | 0 | { |
6860 | 0 | xml_writer_stream writer(stream); |
6861 | |
|
6862 | 0 | print(writer, indent, flags, encoding, depth); |
6863 | 0 | } |
6864 | | |
6865 | | PUGI_IMPL_FN void xml_node::print(std::basic_ostream<wchar_t>& stream, const char_t* indent, unsigned int flags, unsigned int depth) const |
6866 | 0 | { |
6867 | 0 | xml_writer_stream writer(stream); |
6868 | |
|
6869 | 0 | print(writer, indent, flags, encoding_wchar, depth); |
6870 | 0 | } |
6871 | | #endif |
6872 | | |
6873 | | PUGI_IMPL_FN ptrdiff_t xml_node::offset_debug() const |
6874 | 0 | { |
6875 | 0 | if (!_root) return -1; |
6876 | | |
6877 | 0 | impl::xml_document_struct& doc = impl::get_document(_root); |
6878 | | |
6879 | | // we can determine the offset reliably only if there is exactly once parse buffer |
6880 | 0 | if (!doc.buffer || doc.extra_buffers) return -1; |
6881 | | |
6882 | 0 | switch (type()) |
6883 | 0 | { |
6884 | 0 | case node_document: |
6885 | 0 | return 0; |
6886 | | |
6887 | 0 | case node_element: |
6888 | 0 | case node_declaration: |
6889 | 0 | case node_pi: |
6890 | 0 | return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1; |
6891 | | |
6892 | 0 | case node_pcdata: |
6893 | 0 | case node_cdata: |
6894 | 0 | case node_comment: |
6895 | 0 | case node_doctype: |
6896 | 0 | return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; |
6897 | | |
6898 | 0 | default: |
6899 | 0 | assert(false && "Invalid node type"); // unreachable |
6900 | 0 | return -1; |
6901 | 0 | } |
6902 | 0 | } |
6903 | | |
6904 | | #ifdef __BORLANDC__ |
6905 | | PUGI_IMPL_FN bool operator&&(const xml_node& lhs, bool rhs) |
6906 | | { |
6907 | | return (bool)lhs && rhs; |
6908 | | } |
6909 | | |
6910 | | PUGI_IMPL_FN bool operator||(const xml_node& lhs, bool rhs) |
6911 | | { |
6912 | | return (bool)lhs || rhs; |
6913 | | } |
6914 | | #endif |
6915 | | |
6916 | 0 | PUGI_IMPL_FN xml_text::xml_text(xml_node_struct* root): _root(root) |
6917 | 0 | { |
6918 | 0 | } |
6919 | | |
6920 | | PUGI_IMPL_FN xml_node_struct* xml_text::_data() const |
6921 | 0 | { |
6922 | 0 | if (!_root || impl::is_text_node(_root)) return _root; |
6923 | | |
6924 | | // element nodes can have value if parse_embed_pcdata was used |
6925 | 0 | if (PUGI_IMPL_NODETYPE(_root) == node_element && _root->value) |
6926 | 0 | return _root; |
6927 | | |
6928 | 0 | for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) |
6929 | 0 | if (impl::is_text_node(node)) |
6930 | 0 | return node; |
6931 | | |
6932 | 0 | return NULL; |
6933 | 0 | } |
6934 | | |
6935 | | PUGI_IMPL_FN xml_node_struct* xml_text::_data_new() |
6936 | 0 | { |
6937 | 0 | xml_node_struct* d = _data(); |
6938 | 0 | if (d) return d; |
6939 | | |
6940 | 0 | return xml_node(_root).append_child(node_pcdata).internal_object(); |
6941 | 0 | } |
6942 | | |
6943 | 0 | PUGI_IMPL_FN xml_text::xml_text(): _root(NULL) |
6944 | 0 | { |
6945 | 0 | } |
6946 | | |
6947 | | PUGI_IMPL_FN static void unspecified_bool_xml_text(xml_text***) |
6948 | 0 | { |
6949 | 0 | } |
6950 | | |
6951 | | PUGI_IMPL_FN xml_text::operator xml_text::unspecified_bool_type() const |
6952 | 0 | { |
6953 | 0 | return _data() ? unspecified_bool_xml_text : NULL; |
6954 | 0 | } |
6955 | | |
6956 | | PUGI_IMPL_FN bool xml_text::operator!() const |
6957 | 0 | { |
6958 | 0 | return !_data(); |
6959 | 0 | } |
6960 | | |
6961 | | PUGI_IMPL_FN bool xml_text::empty() const |
6962 | 0 | { |
6963 | 0 | return _data() == NULL; |
6964 | 0 | } |
6965 | | |
6966 | | PUGI_IMPL_FN const char_t* xml_text::get() const |
6967 | 0 | { |
6968 | 0 | xml_node_struct* d = _data(); |
6969 | 0 | if (!d) return PUGIXML_TEXT(""); |
6970 | 0 | const char_t* value = d->value; |
6971 | 0 | return value ? value : PUGIXML_TEXT(""); |
6972 | 0 | } |
6973 | | |
6974 | | PUGI_IMPL_FN const char_t* xml_text::as_string(const char_t* def) const |
6975 | 0 | { |
6976 | 0 | xml_node_struct* d = _data(); |
6977 | 0 | if (!d) return def; |
6978 | 0 | const char_t* value = d->value; |
6979 | 0 | return value ? value : def; |
6980 | 0 | } |
6981 | | |
6982 | | PUGI_IMPL_FN int xml_text::as_int(int def) const |
6983 | 0 | { |
6984 | 0 | xml_node_struct* d = _data(); |
6985 | 0 | if (!d) return def; |
6986 | 0 | const char_t* value = d->value; |
6987 | 0 | return value ? impl::get_value_int(value) : def; |
6988 | 0 | } |
6989 | | |
6990 | | PUGI_IMPL_FN unsigned int xml_text::as_uint(unsigned int def) const |
6991 | 0 | { |
6992 | 0 | xml_node_struct* d = _data(); |
6993 | 0 | if (!d) return def; |
6994 | 0 | const char_t* value = d->value; |
6995 | 0 | return value ? impl::get_value_uint(value) : def; |
6996 | 0 | } |
6997 | | |
6998 | | PUGI_IMPL_FN double xml_text::as_double(double def) const |
6999 | 0 | { |
7000 | 0 | xml_node_struct* d = _data(); |
7001 | 0 | if (!d) return def; |
7002 | 0 | const char_t* value = d->value; |
7003 | 0 | return value ? impl::get_value_double(value) : def; |
7004 | 0 | } |
7005 | | |
7006 | | PUGI_IMPL_FN float xml_text::as_float(float def) const |
7007 | 0 | { |
7008 | 0 | xml_node_struct* d = _data(); |
7009 | 0 | if (!d) return def; |
7010 | 0 | const char_t* value = d->value; |
7011 | 0 | return value ? impl::get_value_float(value) : def; |
7012 | 0 | } |
7013 | | |
7014 | | PUGI_IMPL_FN bool xml_text::as_bool(bool def) const |
7015 | 0 | { |
7016 | 0 | xml_node_struct* d = _data(); |
7017 | 0 | if (!d) return def; |
7018 | 0 | const char_t* value = d->value; |
7019 | 0 | return value ? impl::get_value_bool(value) : def; |
7020 | 0 | } |
7021 | | |
7022 | | #ifdef PUGIXML_HAS_LONG_LONG |
7023 | | PUGI_IMPL_FN long long xml_text::as_llong(long long def) const |
7024 | 0 | { |
7025 | 0 | xml_node_struct* d = _data(); |
7026 | 0 | if (!d) return def; |
7027 | 0 | const char_t* value = d->value; |
7028 | 0 | return value ? impl::get_value_llong(value) : def; |
7029 | 0 | } |
7030 | | |
7031 | | PUGI_IMPL_FN unsigned long long xml_text::as_ullong(unsigned long long def) const |
7032 | 0 | { |
7033 | 0 | xml_node_struct* d = _data(); |
7034 | 0 | if (!d) return def; |
7035 | 0 | const char_t* value = d->value; |
7036 | 0 | return value ? impl::get_value_ullong(value) : def; |
7037 | 0 | } |
7038 | | #endif |
7039 | | |
7040 | | PUGI_IMPL_FN bool xml_text::set(const char_t* rhs) |
7041 | 0 | { |
7042 | 0 | xml_node_struct* dn = _data_new(); |
7043 | |
|
7044 | 0 | return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false; |
7045 | 0 | } |
7046 | | |
7047 | | PUGI_IMPL_FN bool xml_text::set(const char_t* rhs, size_t size) |
7048 | 0 | { |
7049 | 0 | xml_node_struct* dn = _data_new(); |
7050 | |
|
7051 | 0 | return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, size) : false; |
7052 | 0 | } |
7053 | | |
7054 | | #ifdef PUGIXML_HAS_STRING_VIEW |
7055 | | PUGI_IMPL_FN bool xml_text::set(string_view_t rhs) |
7056 | 0 | { |
7057 | 0 | xml_node_struct* dn = _data_new(); |
7058 | |
|
7059 | 0 | return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs.data(), rhs.size()) : false; |
7060 | 0 | } |
7061 | | #endif |
7062 | | |
7063 | | PUGI_IMPL_FN bool xml_text::set(int rhs) |
7064 | 0 | { |
7065 | 0 | xml_node_struct* dn = _data_new(); |
7066 | |
|
7067 | 0 | return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; |
7068 | 0 | } |
7069 | | |
7070 | | PUGI_IMPL_FN bool xml_text::set(unsigned int rhs) |
7071 | 0 | { |
7072 | 0 | xml_node_struct* dn = _data_new(); |
7073 | |
|
7074 | 0 | return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; |
7075 | 0 | } |
7076 | | |
7077 | | PUGI_IMPL_FN bool xml_text::set(long rhs) |
7078 | 0 | { |
7079 | 0 | xml_node_struct* dn = _data_new(); |
7080 | |
|
7081 | 0 | return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; |
7082 | 0 | } |
7083 | | |
7084 | | PUGI_IMPL_FN bool xml_text::set(unsigned long rhs) |
7085 | 0 | { |
7086 | 0 | xml_node_struct* dn = _data_new(); |
7087 | |
|
7088 | 0 | return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; |
7089 | 0 | } |
7090 | | |
7091 | | PUGI_IMPL_FN bool xml_text::set(float rhs) |
7092 | 0 | { |
7093 | 0 | xml_node_struct* dn = _data_new(); |
7094 | |
|
7095 | 0 | return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision) : false; |
7096 | 0 | } |
7097 | | |
7098 | | PUGI_IMPL_FN bool xml_text::set(float rhs, int precision) |
7099 | 0 | { |
7100 | 0 | xml_node_struct* dn = _data_new(); |
7101 | |
|
7102 | 0 | return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; |
7103 | 0 | } |
7104 | | |
7105 | | PUGI_IMPL_FN bool xml_text::set(double rhs) |
7106 | 0 | { |
7107 | 0 | xml_node_struct* dn = _data_new(); |
7108 | |
|
7109 | 0 | return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision) : false; |
7110 | 0 | } |
7111 | | |
7112 | | PUGI_IMPL_FN bool xml_text::set(double rhs, int precision) |
7113 | 0 | { |
7114 | 0 | xml_node_struct* dn = _data_new(); |
7115 | |
|
7116 | 0 | return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; |
7117 | 0 | } |
7118 | | |
7119 | | PUGI_IMPL_FN bool xml_text::set(bool rhs) |
7120 | 0 | { |
7121 | 0 | xml_node_struct* dn = _data_new(); |
7122 | |
|
7123 | 0 | return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; |
7124 | 0 | } |
7125 | | |
7126 | | #ifdef PUGIXML_HAS_LONG_LONG |
7127 | | PUGI_IMPL_FN bool xml_text::set(long long rhs) |
7128 | 0 | { |
7129 | 0 | xml_node_struct* dn = _data_new(); |
7130 | |
|
7131 | 0 | return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; |
7132 | 0 | } |
7133 | | |
7134 | | PUGI_IMPL_FN bool xml_text::set(unsigned long long rhs) |
7135 | 0 | { |
7136 | 0 | xml_node_struct* dn = _data_new(); |
7137 | |
|
7138 | 0 | return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; |
7139 | 0 | } |
7140 | | #endif |
7141 | | |
7142 | | PUGI_IMPL_FN xml_text& xml_text::operator=(const char_t* rhs) |
7143 | 0 | { |
7144 | 0 | set(rhs); |
7145 | 0 | return *this; |
7146 | 0 | } |
7147 | | |
7148 | | PUGI_IMPL_FN xml_text& xml_text::operator=(int rhs) |
7149 | 0 | { |
7150 | 0 | set(rhs); |
7151 | 0 | return *this; |
7152 | 0 | } |
7153 | | |
7154 | | PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned int rhs) |
7155 | 0 | { |
7156 | 0 | set(rhs); |
7157 | 0 | return *this; |
7158 | 0 | } |
7159 | | |
7160 | | PUGI_IMPL_FN xml_text& xml_text::operator=(long rhs) |
7161 | 0 | { |
7162 | 0 | set(rhs); |
7163 | 0 | return *this; |
7164 | 0 | } |
7165 | | |
7166 | | PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long rhs) |
7167 | 0 | { |
7168 | 0 | set(rhs); |
7169 | 0 | return *this; |
7170 | 0 | } |
7171 | | |
7172 | | PUGI_IMPL_FN xml_text& xml_text::operator=(double rhs) |
7173 | 0 | { |
7174 | 0 | set(rhs); |
7175 | 0 | return *this; |
7176 | 0 | } |
7177 | | |
7178 | | PUGI_IMPL_FN xml_text& xml_text::operator=(float rhs) |
7179 | 0 | { |
7180 | 0 | set(rhs); |
7181 | 0 | return *this; |
7182 | 0 | } |
7183 | | |
7184 | | PUGI_IMPL_FN xml_text& xml_text::operator=(bool rhs) |
7185 | 0 | { |
7186 | 0 | set(rhs); |
7187 | 0 | return *this; |
7188 | 0 | } |
7189 | | |
7190 | | #ifdef PUGIXML_HAS_STRING_VIEW |
7191 | | PUGI_IMPL_FN xml_text& xml_text::operator=(string_view_t rhs) |
7192 | 0 | { |
7193 | 0 | set(rhs); |
7194 | 0 | return *this; |
7195 | 0 | } |
7196 | | #endif |
7197 | | |
7198 | | #ifdef PUGIXML_HAS_LONG_LONG |
7199 | | PUGI_IMPL_FN xml_text& xml_text::operator=(long long rhs) |
7200 | 0 | { |
7201 | 0 | set(rhs); |
7202 | 0 | return *this; |
7203 | 0 | } |
7204 | | |
7205 | | PUGI_IMPL_FN xml_text& xml_text::operator=(unsigned long long rhs) |
7206 | 0 | { |
7207 | 0 | set(rhs); |
7208 | 0 | return *this; |
7209 | 0 | } |
7210 | | #endif |
7211 | | |
7212 | | PUGI_IMPL_FN xml_node xml_text::data() const |
7213 | 0 | { |
7214 | 0 | return xml_node(_data()); |
7215 | 0 | } |
7216 | | |
7217 | | #ifdef __BORLANDC__ |
7218 | | PUGI_IMPL_FN bool operator&&(const xml_text& lhs, bool rhs) |
7219 | | { |
7220 | | return (bool)lhs && rhs; |
7221 | | } |
7222 | | |
7223 | | PUGI_IMPL_FN bool operator||(const xml_text& lhs, bool rhs) |
7224 | | { |
7225 | | return (bool)lhs || rhs; |
7226 | | } |
7227 | | #endif |
7228 | | |
7229 | | PUGI_IMPL_FN xml_node_iterator::xml_node_iterator() |
7230 | 0 | { |
7231 | 0 | } |
7232 | | |
7233 | 0 | PUGI_IMPL_FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) |
7234 | 0 | { |
7235 | 0 | } |
7236 | | |
7237 | 0 | PUGI_IMPL_FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) |
7238 | 0 | { |
7239 | 0 | } |
7240 | | |
7241 | | PUGI_IMPL_FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const |
7242 | 0 | { |
7243 | 0 | return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; |
7244 | 0 | } |
7245 | | |
7246 | | PUGI_IMPL_FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const |
7247 | 0 | { |
7248 | 0 | return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; |
7249 | 0 | } |
7250 | | |
7251 | | PUGI_IMPL_FN xml_node& xml_node_iterator::operator*() const |
7252 | 0 | { |
7253 | 0 | assert(_wrap._root); |
7254 | 0 | return _wrap; |
7255 | 0 | } |
7256 | | |
7257 | | PUGI_IMPL_FN xml_node* xml_node_iterator::operator->() const |
7258 | 0 | { |
7259 | 0 | assert(_wrap._root); |
7260 | 0 | return &_wrap; |
7261 | 0 | } |
7262 | | |
7263 | | PUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator++() |
7264 | 0 | { |
7265 | 0 | assert(_wrap._root); |
7266 | 0 | _wrap._root = _wrap._root->next_sibling; |
7267 | 0 | return *this; |
7268 | 0 | } |
7269 | | |
7270 | | PUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator++(int) |
7271 | 0 | { |
7272 | 0 | xml_node_iterator temp = *this; |
7273 | 0 | ++*this; |
7274 | 0 | return temp; |
7275 | 0 | } |
7276 | | |
7277 | | PUGI_IMPL_FN xml_node_iterator& xml_node_iterator::operator--() |
7278 | 0 | { |
7279 | 0 | _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); |
7280 | 0 | return *this; |
7281 | 0 | } |
7282 | | |
7283 | | PUGI_IMPL_FN xml_node_iterator xml_node_iterator::operator--(int) |
7284 | 0 | { |
7285 | 0 | xml_node_iterator temp = *this; |
7286 | 0 | --*this; |
7287 | 0 | return temp; |
7288 | 0 | } |
7289 | | |
7290 | | PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator() |
7291 | 0 | { |
7292 | 0 | } |
7293 | | |
7294 | 0 | PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) |
7295 | 0 | { |
7296 | 0 | } |
7297 | | |
7298 | 0 | PUGI_IMPL_FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) |
7299 | 0 | { |
7300 | 0 | } |
7301 | | |
7302 | | PUGI_IMPL_FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const |
7303 | 0 | { |
7304 | 0 | return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; |
7305 | 0 | } |
7306 | | |
7307 | | PUGI_IMPL_FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const |
7308 | 0 | { |
7309 | 0 | return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; |
7310 | 0 | } |
7311 | | |
7312 | | PUGI_IMPL_FN xml_attribute& xml_attribute_iterator::operator*() const |
7313 | 0 | { |
7314 | 0 | assert(_wrap._attr); |
7315 | 0 | return _wrap; |
7316 | 0 | } |
7317 | | |
7318 | | PUGI_IMPL_FN xml_attribute* xml_attribute_iterator::operator->() const |
7319 | 0 | { |
7320 | 0 | assert(_wrap._attr); |
7321 | 0 | return &_wrap; |
7322 | 0 | } |
7323 | | |
7324 | | PUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator++() |
7325 | 0 | { |
7326 | 0 | assert(_wrap._attr); |
7327 | 0 | _wrap._attr = _wrap._attr->next_attribute; |
7328 | 0 | return *this; |
7329 | 0 | } |
7330 | | |
7331 | | PUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator++(int) |
7332 | 0 | { |
7333 | 0 | xml_attribute_iterator temp = *this; |
7334 | 0 | ++*this; |
7335 | 0 | return temp; |
7336 | 0 | } |
7337 | | |
7338 | | PUGI_IMPL_FN xml_attribute_iterator& xml_attribute_iterator::operator--() |
7339 | 0 | { |
7340 | 0 | _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); |
7341 | 0 | return *this; |
7342 | 0 | } |
7343 | | |
7344 | | PUGI_IMPL_FN xml_attribute_iterator xml_attribute_iterator::operator--(int) |
7345 | 0 | { |
7346 | 0 | xml_attribute_iterator temp = *this; |
7347 | 0 | --*this; |
7348 | 0 | return temp; |
7349 | 0 | } |
7350 | | |
7351 | 0 | PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(): _name(NULL) |
7352 | 0 | { |
7353 | 0 | } |
7354 | | |
7355 | 0 | PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) |
7356 | 0 | { |
7357 | 0 | } |
7358 | | |
7359 | 0 | PUGI_IMPL_FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) |
7360 | 0 | { |
7361 | 0 | } |
7362 | | |
7363 | | PUGI_IMPL_FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const |
7364 | 0 | { |
7365 | 0 | return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; |
7366 | 0 | } |
7367 | | |
7368 | | PUGI_IMPL_FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const |
7369 | 0 | { |
7370 | 0 | return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; |
7371 | 0 | } |
7372 | | |
7373 | | PUGI_IMPL_FN xml_node& xml_named_node_iterator::operator*() const |
7374 | 0 | { |
7375 | 0 | assert(_wrap._root); |
7376 | 0 | return _wrap; |
7377 | 0 | } |
7378 | | |
7379 | | PUGI_IMPL_FN xml_node* xml_named_node_iterator::operator->() const |
7380 | 0 | { |
7381 | 0 | assert(_wrap._root); |
7382 | 0 | return &_wrap; |
7383 | 0 | } |
7384 | | |
7385 | | PUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator++() |
7386 | 0 | { |
7387 | 0 | assert(_wrap._root); |
7388 | 0 | _wrap = _wrap.next_sibling(_name); |
7389 | 0 | return *this; |
7390 | 0 | } |
7391 | | |
7392 | | PUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator++(int) |
7393 | 0 | { |
7394 | 0 | xml_named_node_iterator temp = *this; |
7395 | 0 | ++*this; |
7396 | 0 | return temp; |
7397 | 0 | } |
7398 | | |
7399 | | PUGI_IMPL_FN xml_named_node_iterator& xml_named_node_iterator::operator--() |
7400 | 0 | { |
7401 | 0 | if (_wrap._root) |
7402 | 0 | _wrap = _wrap.previous_sibling(_name); |
7403 | 0 | else |
7404 | 0 | { |
7405 | 0 | _wrap = _parent.last_child(); |
7406 | |
|
7407 | 0 | if (!impl::strequal(_wrap.name(), _name)) |
7408 | 0 | _wrap = _wrap.previous_sibling(_name); |
7409 | 0 | } |
7410 | |
|
7411 | 0 | return *this; |
7412 | 0 | } |
7413 | | |
7414 | | PUGI_IMPL_FN xml_named_node_iterator xml_named_node_iterator::operator--(int) |
7415 | 0 | { |
7416 | 0 | xml_named_node_iterator temp = *this; |
7417 | 0 | --*this; |
7418 | 0 | return temp; |
7419 | 0 | } |
7420 | | |
7421 | 12.9k | PUGI_IMPL_FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) |
7422 | 12.9k | { |
7423 | 12.9k | } |
7424 | | |
7425 | | PUGI_IMPL_FN xml_parse_result::operator bool() const |
7426 | 11.4k | { |
7427 | 11.4k | return status == status_ok; |
7428 | 11.4k | } |
7429 | | |
7430 | | PUGI_IMPL_FN const char* xml_parse_result::description() const |
7431 | 0 | { |
7432 | 0 | switch (status) |
7433 | 0 | { |
7434 | 0 | case status_ok: return "No error"; |
7435 | | |
7436 | 0 | case status_file_not_found: return "File was not found"; |
7437 | 0 | case status_io_error: return "Error reading from file/stream"; |
7438 | 0 | case status_out_of_memory: return "Could not allocate memory"; |
7439 | 0 | case status_internal_error: return "Internal error occurred"; |
7440 | | |
7441 | 0 | case status_unrecognized_tag: return "Could not determine tag type"; |
7442 | | |
7443 | 0 | case status_bad_pi: return "Error parsing document declaration/processing instruction"; |
7444 | 0 | case status_bad_comment: return "Error parsing comment"; |
7445 | 0 | case status_bad_cdata: return "Error parsing CDATA section"; |
7446 | 0 | case status_bad_doctype: return "Error parsing document type declaration"; |
7447 | 0 | case status_bad_pcdata: return "Error parsing PCDATA section"; |
7448 | 0 | case status_bad_start_element: return "Error parsing start element tag"; |
7449 | 0 | case status_bad_attribute: return "Error parsing element attribute"; |
7450 | 0 | case status_bad_end_element: return "Error parsing end element tag"; |
7451 | 0 | case status_end_element_mismatch: return "Start-end tags mismatch"; |
7452 | | |
7453 | 0 | case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; |
7454 | | |
7455 | 0 | case status_no_document_element: return "No document element found"; |
7456 | | |
7457 | 0 | default: return "Unknown error"; |
7458 | 0 | } |
7459 | 0 | } |
7460 | | |
7461 | 3.81k | PUGI_IMPL_FN xml_document::xml_document(): _buffer(NULL) |
7462 | 3.81k | { |
7463 | 3.81k | _create(); |
7464 | 3.81k | } |
7465 | | |
7466 | | PUGI_IMPL_FN xml_document::~xml_document() |
7467 | 3.81k | { |
7468 | 3.81k | _destroy(); |
7469 | 3.81k | } |
7470 | | |
7471 | | #ifdef PUGIXML_HAS_MOVE |
7472 | 0 | PUGI_IMPL_FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(NULL) |
7473 | 0 | { |
7474 | 0 | _create(); |
7475 | 0 | _move(rhs); |
7476 | 0 | } |
7477 | | |
7478 | | PUGI_IMPL_FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT |
7479 | 0 | { |
7480 | 0 | if (this == &rhs) return *this; |
7481 | | |
7482 | 0 | _destroy(); |
7483 | 0 | _create(); |
7484 | 0 | _move(rhs); |
7485 | |
|
7486 | 0 | return *this; |
7487 | 0 | } |
7488 | | #endif |
7489 | | |
7490 | | PUGI_IMPL_FN void xml_document::reset() |
7491 | 11.4k | { |
7492 | 11.4k | _destroy(); |
7493 | 11.4k | _create(); |
7494 | 11.4k | } |
7495 | | |
7496 | | PUGI_IMPL_FN void xml_document::reset(const xml_document& proto) |
7497 | 0 | { |
7498 | 0 | reset(); |
7499 | |
|
7500 | 0 | impl::node_copy_tree(_root, proto._root); |
7501 | 0 | } |
7502 | | |
7503 | | PUGI_IMPL_FN void xml_document::_create() |
7504 | 15.2k | { |
7505 | 15.2k | assert(!_root); |
7506 | | |
7507 | | #ifdef PUGIXML_COMPACT |
7508 | | // space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit |
7509 | | const size_t page_offset = sizeof(void*); |
7510 | | #else |
7511 | 15.2k | const size_t page_offset = 0; |
7512 | 15.2k | #endif |
7513 | | |
7514 | | // initialize sentinel page |
7515 | 15.2k | PUGI_IMPL_STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory)); |
7516 | | |
7517 | | // prepare page structure |
7518 | 15.2k | impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory); |
7519 | 15.2k | assert(page); |
7520 | | |
7521 | 15.2k | page->busy_size = impl::xml_memory_page_size; |
7522 | | |
7523 | | // setup first page marker |
7524 | | #ifdef PUGIXML_COMPACT |
7525 | | // round-trip through void* to avoid 'cast increases required alignment of target type' warning |
7526 | | page->compact_page_marker = reinterpret_cast<uint32_t*>(static_cast<void*>(reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page))); |
7527 | | *page->compact_page_marker = sizeof(impl::xml_memory_page); |
7528 | | #endif |
7529 | | |
7530 | | // allocate new root |
7531 | 15.2k | _root = new (reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page); |
7532 | 15.2k | _root->prev_sibling_c = _root; |
7533 | | |
7534 | | // setup sentinel page |
7535 | 15.2k | page->allocator = static_cast<impl::xml_document_struct*>(_root); |
7536 | | |
7537 | | // setup hash table pointer in allocator |
7538 | | #ifdef PUGIXML_COMPACT |
7539 | | page->allocator->_hash = &static_cast<impl::xml_document_struct*>(_root)->hash; |
7540 | | #endif |
7541 | | |
7542 | | // verify the document allocation |
7543 | 15.2k | assert(reinterpret_cast<char*>(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); |
7544 | 15.2k | } |
7545 | | |
7546 | | PUGI_IMPL_FN void xml_document::_destroy() |
7547 | 15.2k | { |
7548 | 15.2k | assert(_root); |
7549 | | |
7550 | | // destroy static storage |
7551 | 15.2k | if (_buffer) |
7552 | 11.4k | { |
7553 | 11.4k | impl::xml_memory::deallocate(_buffer); |
7554 | 11.4k | _buffer = NULL; |
7555 | 11.4k | } |
7556 | | |
7557 | | // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) |
7558 | 15.2k | for (impl::xml_extra_buffer* extra = static_cast<impl::xml_document_struct*>(_root)->extra_buffers; extra; extra = extra->next) |
7559 | 0 | { |
7560 | 0 | if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); |
7561 | 0 | } |
7562 | | |
7563 | | // destroy dynamic storage, leave sentinel page (it's in static memory) |
7564 | 15.2k | impl::xml_memory_page* root_page = PUGI_IMPL_GETPAGE(_root); |
7565 | 15.2k | assert(root_page && !root_page->prev); |
7566 | 15.2k | assert(reinterpret_cast<char*>(root_page) >= _memory && reinterpret_cast<char*>(root_page) < _memory + sizeof(_memory)); |
7567 | | |
7568 | 22.9k | for (impl::xml_memory_page* page = root_page->next; page; ) |
7569 | 7.70k | { |
7570 | 7.70k | impl::xml_memory_page* next = page->next; |
7571 | | |
7572 | 7.70k | impl::xml_allocator::deallocate_page(page); |
7573 | | |
7574 | 7.70k | page = next; |
7575 | 7.70k | } |
7576 | | |
7577 | | #ifdef PUGIXML_COMPACT |
7578 | | // destroy hash table |
7579 | | static_cast<impl::xml_document_struct*>(_root)->hash.clear(); |
7580 | | #endif |
7581 | | |
7582 | 15.2k | _root = NULL; |
7583 | 15.2k | } |
7584 | | |
7585 | | #ifdef PUGIXML_HAS_MOVE |
7586 | | PUGI_IMPL_FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT |
7587 | 0 | { |
7588 | 0 | impl::xml_document_struct* doc = static_cast<impl::xml_document_struct*>(_root); |
7589 | 0 | impl::xml_document_struct* other = static_cast<impl::xml_document_struct*>(rhs._root); |
7590 | | |
7591 | | // save first child pointer for later; this needs hash access |
7592 | 0 | xml_node_struct* other_first_child = other->first_child; |
7593 | |
|
7594 | | #ifdef PUGIXML_COMPACT |
7595 | | // reserve space for the hash table up front; this is the only operation that can fail |
7596 | | // if it does, we have no choice but to throw (if we have exceptions) |
7597 | | if (other_first_child) |
7598 | | { |
7599 | | size_t other_children = 0; |
7600 | | for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) |
7601 | | other_children++; |
7602 | | |
7603 | | // in compact mode, each pointer assignment could result in a hash table request |
7604 | | // during move, we have to relocate document first_child and parents of all children |
7605 | | // normally there's just one child and its parent has a pointerless encoding but |
7606 | | // we assume the worst here |
7607 | | if (!other->_hash->reserve(other_children + 1)) |
7608 | | { |
7609 | | #ifdef PUGIXML_NO_EXCEPTIONS |
7610 | | return; |
7611 | | #else |
7612 | | throw std::bad_alloc(); |
7613 | | #endif |
7614 | | } |
7615 | | } |
7616 | | #endif |
7617 | | |
7618 | | // move allocation state |
7619 | | // note that other->_root may point to the embedded document page, in which case we should keep original (empty) state |
7620 | 0 | if (other->_root != PUGI_IMPL_GETPAGE(other)) |
7621 | 0 | { |
7622 | 0 | doc->_root = other->_root; |
7623 | 0 | doc->_busy_size = other->_busy_size; |
7624 | 0 | } |
7625 | | |
7626 | | // move buffer state |
7627 | 0 | doc->buffer = other->buffer; |
7628 | 0 | doc->extra_buffers = other->extra_buffers; |
7629 | 0 | _buffer = rhs._buffer; |
7630 | |
|
7631 | | #ifdef PUGIXML_COMPACT |
7632 | | // move compact hash; note that the hash table can have pointers to other but they will be "inactive", similarly to nodes removed with remove_child |
7633 | | doc->hash = other->hash; |
7634 | | doc->_hash = &doc->hash; |
7635 | | |
7636 | | // make sure we don't access other hash up until the end when we reinitialize other document |
7637 | | other->_hash = NULL; |
7638 | | #endif |
7639 | | |
7640 | | // move page structure |
7641 | 0 | impl::xml_memory_page* doc_page = PUGI_IMPL_GETPAGE(doc); |
7642 | 0 | assert(doc_page && !doc_page->prev && !doc_page->next); |
7643 | | |
7644 | 0 | impl::xml_memory_page* other_page = PUGI_IMPL_GETPAGE(other); |
7645 | 0 | assert(other_page && !other_page->prev); |
7646 | | |
7647 | | // relink pages since root page is embedded into xml_document |
7648 | 0 | if (impl::xml_memory_page* page = other_page->next) |
7649 | 0 | { |
7650 | 0 | assert(page->prev == other_page); |
7651 | | |
7652 | 0 | page->prev = doc_page; |
7653 | |
|
7654 | 0 | doc_page->next = page; |
7655 | 0 | other_page->next = NULL; |
7656 | 0 | } |
7657 | | |
7658 | | // make sure pages point to the correct document state |
7659 | 0 | for (impl::xml_memory_page* page = doc_page->next; page; page = page->next) |
7660 | 0 | { |
7661 | 0 | assert(page->allocator == other); |
7662 | | |
7663 | 0 | page->allocator = doc; |
7664 | |
|
7665 | | #ifdef PUGIXML_COMPACT |
7666 | | // this automatically migrates most children between documents and prevents ->parent assignment from allocating |
7667 | | if (page->compact_shared_parent == other) |
7668 | | page->compact_shared_parent = doc; |
7669 | | #endif |
7670 | 0 | } |
7671 | | |
7672 | | // move tree structure |
7673 | 0 | assert(!doc->first_child); |
7674 | | |
7675 | 0 | doc->first_child = other_first_child; |
7676 | |
|
7677 | 0 | for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) |
7678 | 0 | { |
7679 | | #ifdef PUGIXML_COMPACT |
7680 | | // most children will have migrated when we reassigned compact_shared_parent |
7681 | | assert(node->parent == other || node->parent == doc); |
7682 | | |
7683 | | node->parent = doc; |
7684 | | #else |
7685 | 0 | assert(node->parent == other); |
7686 | 0 | node->parent = doc; |
7687 | 0 | #endif |
7688 | 0 | } |
7689 | | |
7690 | | // reset other document |
7691 | 0 | new (other) impl::xml_document_struct(PUGI_IMPL_GETPAGE(other)); |
7692 | 0 | rhs._buffer = NULL; |
7693 | 0 | } |
7694 | | #endif |
7695 | | |
7696 | | #ifndef PUGIXML_NO_STL |
7697 | | PUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream<char>& stream, unsigned int options, xml_encoding encoding) |
7698 | 0 | { |
7699 | 0 | reset(); |
7700 | |
|
7701 | 0 | return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding, &_buffer); |
7702 | 0 | } |
7703 | | |
7704 | | PUGI_IMPL_FN xml_parse_result xml_document::load(std::basic_istream<wchar_t>& stream, unsigned int options) |
7705 | 0 | { |
7706 | 0 | reset(); |
7707 | |
|
7708 | 0 | return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding_wchar, &_buffer); |
7709 | 0 | } |
7710 | | #endif |
7711 | | |
7712 | | PUGI_IMPL_FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) |
7713 | 0 | { |
7714 | | // Force native encoding (skip autodetection) |
7715 | | #ifdef PUGIXML_WCHAR_MODE |
7716 | | xml_encoding encoding = encoding_wchar; |
7717 | | #else |
7718 | 0 | xml_encoding encoding = encoding_utf8; |
7719 | 0 | #endif |
7720 | |
|
7721 | 0 | return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); |
7722 | 0 | } |
7723 | | |
7724 | | PUGI_IMPL_FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) |
7725 | 0 | { |
7726 | 0 | return load_string(contents, options); |
7727 | 0 | } |
7728 | | |
7729 | | PUGI_IMPL_FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) |
7730 | 0 | { |
7731 | 0 | reset(); |
7732 | |
|
7733 | 0 | using impl::auto_deleter; // MSVC7 workaround |
7734 | 0 | auto_deleter<FILE> file(impl::open_file(path_, "rb"), impl::close_file); |
7735 | |
|
7736 | 0 | return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer); |
7737 | 0 | } |
7738 | | |
7739 | | PUGI_IMPL_FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) |
7740 | 0 | { |
7741 | 0 | reset(); |
7742 | |
|
7743 | 0 | using impl::auto_deleter; // MSVC7 workaround |
7744 | 0 | auto_deleter<FILE> file(impl::open_file_wide(path_, L"rb"), impl::close_file); |
7745 | |
|
7746 | 0 | return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer); |
7747 | 0 | } |
7748 | | |
7749 | | PUGI_IMPL_FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) |
7750 | 11.4k | { |
7751 | 11.4k | reset(); |
7752 | | |
7753 | 11.4k | return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, const_cast<void*>(contents), size, options, encoding, false, false, &_buffer); |
7754 | 11.4k | } |
7755 | | |
7756 | | PUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) |
7757 | 0 | { |
7758 | 0 | reset(); |
7759 | |
|
7760 | 0 | return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, false, &_buffer); |
7761 | 0 | } |
7762 | | |
7763 | | PUGI_IMPL_FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) |
7764 | 0 | { |
7765 | 0 | reset(); |
7766 | |
|
7767 | 0 | return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, true, &_buffer); |
7768 | 0 | } |
7769 | | |
7770 | | PUGI_IMPL_FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const |
7771 | 0 | { |
7772 | 0 | impl::xml_buffered_writer buffered_writer(writer, encoding); |
7773 | |
|
7774 | 0 | if ((flags & format_write_bom) && buffered_writer.encoding != encoding_latin1) |
7775 | 0 | { |
7776 | | // BOM always represents the codepoint U+FEFF, so just write it in native encoding |
7777 | | #ifdef PUGIXML_WCHAR_MODE |
7778 | | unsigned int bom = 0xfeff; |
7779 | | buffered_writer.write(static_cast<wchar_t>(bom)); |
7780 | | #else |
7781 | 0 | buffered_writer.write('\xef', '\xbb', '\xbf'); |
7782 | 0 | #endif |
7783 | 0 | } |
7784 | |
|
7785 | 0 | if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) |
7786 | 0 | { |
7787 | 0 | buffered_writer.write_string(PUGIXML_TEXT("<?xml version=\"1.0\"")); |
7788 | 0 | if (buffered_writer.encoding == encoding_latin1) buffered_writer.write_string(PUGIXML_TEXT(" encoding=\"ISO-8859-1\"")); |
7789 | 0 | buffered_writer.write('?', '>'); |
7790 | 0 | if (!(flags & format_raw)) buffered_writer.write('\n'); |
7791 | 0 | } |
7792 | |
|
7793 | 0 | impl::node_output(buffered_writer, _root, indent, flags, 0); |
7794 | |
|
7795 | 0 | buffered_writer.flush(); |
7796 | 0 | } |
7797 | | |
7798 | | #ifndef PUGIXML_NO_STL |
7799 | | PUGI_IMPL_FN void xml_document::save(std::basic_ostream<char>& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const |
7800 | 0 | { |
7801 | 0 | xml_writer_stream writer(stream); |
7802 | |
|
7803 | 0 | save(writer, indent, flags, encoding); |
7804 | 0 | } |
7805 | | |
7806 | | PUGI_IMPL_FN void xml_document::save(std::basic_ostream<wchar_t>& stream, const char_t* indent, unsigned int flags) const |
7807 | 0 | { |
7808 | 0 | xml_writer_stream writer(stream); |
7809 | |
|
7810 | 0 | save(writer, indent, flags, encoding_wchar); |
7811 | 0 | } |
7812 | | #endif |
7813 | | |
7814 | | PUGI_IMPL_FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const |
7815 | 0 | { |
7816 | 0 | using impl::auto_deleter; // MSVC7 workaround |
7817 | 0 | auto_deleter<FILE> file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); |
7818 | |
|
7819 | 0 | return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0; |
7820 | 0 | } |
7821 | | |
7822 | | PUGI_IMPL_FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const |
7823 | 0 | { |
7824 | 0 | using impl::auto_deleter; // MSVC7 workaround |
7825 | 0 | auto_deleter<FILE> file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file); |
7826 | |
|
7827 | 0 | return impl::save_file_impl(*this, file.data, indent, flags, encoding) && fclose(file.release()) == 0; |
7828 | 0 | } |
7829 | | |
7830 | | PUGI_IMPL_FN xml_node xml_document::document_element() const |
7831 | 0 | { |
7832 | 0 | assert(_root); |
7833 | | |
7834 | 0 | for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) |
7835 | 0 | if (PUGI_IMPL_NODETYPE(i) == node_element) |
7836 | 0 | return xml_node(i); |
7837 | | |
7838 | 0 | return xml_node(); |
7839 | 0 | } |
7840 | | |
7841 | | #ifndef PUGIXML_NO_STL |
7842 | | PUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) |
7843 | 0 | { |
7844 | 0 | assert(str); |
7845 | | |
7846 | 0 | return impl::as_utf8_impl(str, impl::strlength_wide(str)); |
7847 | 0 | } |
7848 | | |
7849 | | PUGI_IMPL_FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t>& str) |
7850 | 0 | { |
7851 | 0 | return impl::as_utf8_impl(str.c_str(), str.size()); |
7852 | 0 | } |
7853 | | |
7854 | | PUGI_IMPL_FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str) |
7855 | 0 | { |
7856 | 0 | assert(str); |
7857 | | |
7858 | 0 | return impl::as_wide_impl(str, strlen(str)); |
7859 | 0 | } |
7860 | | |
7861 | | PUGI_IMPL_FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const std::string& str) |
7862 | 0 | { |
7863 | 0 | return impl::as_wide_impl(str.c_str(), str.size()); |
7864 | 0 | } |
7865 | | #endif |
7866 | | |
7867 | | PUGI_IMPL_FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) |
7868 | 0 | { |
7869 | 0 | impl::xml_memory::allocate = allocate; |
7870 | 0 | impl::xml_memory::deallocate = deallocate; |
7871 | 0 | } |
7872 | | |
7873 | | PUGI_IMPL_FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() |
7874 | 0 | { |
7875 | 0 | return impl::xml_memory::allocate; |
7876 | 0 | } |
7877 | | |
7878 | | PUGI_IMPL_FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() |
7879 | 0 | { |
7880 | 0 | return impl::xml_memory::deallocate; |
7881 | 0 | } |
7882 | | } |
7883 | | |
7884 | | #if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) |
7885 | | namespace std |
7886 | | { |
7887 | | // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) |
7888 | | PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) |
7889 | | { |
7890 | | return std::bidirectional_iterator_tag(); |
7891 | | } |
7892 | | |
7893 | | PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) |
7894 | | { |
7895 | | return std::bidirectional_iterator_tag(); |
7896 | | } |
7897 | | |
7898 | | PUGI_IMPL_FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) |
7899 | | { |
7900 | | return std::bidirectional_iterator_tag(); |
7901 | | } |
7902 | | } |
7903 | | #endif |
7904 | | |
7905 | | #if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) |
7906 | | namespace std |
7907 | | { |
7908 | | // Workarounds for (non-standard) iterator category detection |
7909 | | PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) |
7910 | | { |
7911 | | return std::bidirectional_iterator_tag(); |
7912 | | } |
7913 | | |
7914 | | PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) |
7915 | | { |
7916 | | return std::bidirectional_iterator_tag(); |
7917 | | } |
7918 | | |
7919 | | PUGI_IMPL_FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) |
7920 | | { |
7921 | | return std::bidirectional_iterator_tag(); |
7922 | | } |
7923 | | } |
7924 | | #endif |
7925 | | |
7926 | | #ifndef PUGIXML_NO_XPATH |
7927 | | // STL replacements |
7928 | | PUGI_IMPL_NS_BEGIN |
7929 | | struct equal_to |
7930 | | { |
7931 | | template <typename T> bool operator()(const T& lhs, const T& rhs) const |
7932 | 0 | { |
7933 | 0 | return lhs == rhs; |
7934 | 0 | } Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::equal_to::operator()<bool>(bool const&, bool const&) const Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::equal_to::operator()<double>(double const&, double const&) const Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::equal_to::operator()<pugi::impl::(anonymous namespace)::xpath_string>(pugi::impl::(anonymous namespace)::xpath_string const&, pugi::impl::(anonymous namespace)::xpath_string const&) const |
7935 | | }; |
7936 | | |
7937 | | struct not_equal_to |
7938 | | { |
7939 | | template <typename T> bool operator()(const T& lhs, const T& rhs) const |
7940 | 0 | { |
7941 | 0 | return lhs != rhs; |
7942 | 0 | } Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::not_equal_to::operator()<bool>(bool const&, bool const&) const Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::not_equal_to::operator()<double>(double const&, double const&) const Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::not_equal_to::operator()<pugi::impl::(anonymous namespace)::xpath_string>(pugi::impl::(anonymous namespace)::xpath_string const&, pugi::impl::(anonymous namespace)::xpath_string const&) const |
7943 | | }; |
7944 | | |
7945 | | struct less |
7946 | | { |
7947 | | template <typename T> bool operator()(const T& lhs, const T& rhs) const |
7948 | 0 | { |
7949 | 0 | return lhs < rhs; |
7950 | 0 | } |
7951 | | }; |
7952 | | |
7953 | | struct less_equal |
7954 | | { |
7955 | | template <typename T> bool operator()(const T& lhs, const T& rhs) const |
7956 | 0 | { |
7957 | 0 | return lhs <= rhs; |
7958 | 0 | } |
7959 | | }; |
7960 | | |
7961 | | template <typename T> inline void swap(T& lhs, T& rhs) |
7962 | 0 | { |
7963 | 0 | T temp = lhs; |
7964 | 0 | lhs = rhs; |
7965 | 0 | rhs = temp; |
7966 | 0 | } Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::swap<pugi::xpath_node*>(pugi::xpath_node*&, pugi::xpath_node*&) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::swap<pugi::xpath_node>(pugi::xpath_node&, pugi::xpath_node&) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::swap<pugi::impl::(anonymous namespace)::xpath_ast_node*>(pugi::impl::(anonymous namespace)::xpath_ast_node*&, pugi::impl::(anonymous namespace)::xpath_ast_node*&) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::swap<pugi::xpath_value_type>(pugi::xpath_value_type&, pugi::xpath_value_type&) |
7967 | | |
7968 | | template <typename I, typename Pred> PUGI_IMPL_FN I min_element(I begin, I end, const Pred& pred) |
7969 | 0 | { |
7970 | 0 | I result = begin; |
7971 | |
|
7972 | 0 | for (I it = begin + 1; it != end; ++it) |
7973 | 0 | if (pred(*it, *result)) |
7974 | 0 | result = it; |
7975 | |
|
7976 | 0 | return result; |
7977 | 0 | } |
7978 | | |
7979 | | template <typename I> PUGI_IMPL_FN void reverse(I begin, I end) |
7980 | 0 | { |
7981 | 0 | while (end - begin > 1) |
7982 | 0 | swap(*begin++, *--end); |
7983 | 0 | } |
7984 | | |
7985 | | template <typename I> PUGI_IMPL_FN I unique(I begin, I end) |
7986 | 0 | { |
7987 | | // fast skip head |
7988 | 0 | while (end - begin > 1 && *begin != *(begin + 1)) |
7989 | 0 | begin++; |
7990 | |
|
7991 | 0 | if (begin == end) |
7992 | 0 | return begin; |
7993 | | |
7994 | | // last written element |
7995 | 0 | I write = begin++; |
7996 | | |
7997 | | // merge unique elements |
7998 | 0 | while (begin != end) |
7999 | 0 | { |
8000 | 0 | if (*begin != *write) |
8001 | 0 | *++write = *begin++; |
8002 | 0 | else |
8003 | 0 | begin++; |
8004 | 0 | } |
8005 | | |
8006 | | // past-the-end (write points to live element) |
8007 | 0 | return write + 1; |
8008 | 0 | } |
8009 | | |
8010 | | template <typename T, typename Pred> PUGI_IMPL_FN void insertion_sort(T* begin, T* end, const Pred& pred) |
8011 | 0 | { |
8012 | 0 | if (begin == end) |
8013 | 0 | return; |
8014 | | |
8015 | 0 | for (T* it = begin + 1; it != end; ++it) |
8016 | 0 | { |
8017 | 0 | T val = *it; |
8018 | 0 | T* hole = it; |
8019 | | |
8020 | | // move hole backwards |
8021 | 0 | while (hole > begin && pred(val, *(hole - 1))) |
8022 | 0 | { |
8023 | 0 | *hole = *(hole - 1); |
8024 | 0 | hole--; |
8025 | 0 | } |
8026 | | |
8027 | | // fill hole with element |
8028 | 0 | *hole = val; |
8029 | 0 | } |
8030 | 0 | } |
8031 | | |
8032 | | template <typename I, typename Pred> inline I median3(I first, I middle, I last, const Pred& pred) |
8033 | 0 | { |
8034 | 0 | if (pred(*middle, *first)) |
8035 | 0 | swap(middle, first); |
8036 | 0 | if (pred(*last, *middle)) |
8037 | 0 | swap(last, middle); |
8038 | 0 | if (pred(*middle, *first)) |
8039 | 0 | swap(middle, first); |
8040 | |
|
8041 | 0 | return middle; |
8042 | 0 | } |
8043 | | |
8044 | | template <typename T, typename Pred> PUGI_IMPL_FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) |
8045 | 0 | { |
8046 | | // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups) |
8047 | 0 | T* eq = begin; |
8048 | 0 | T* lt = begin; |
8049 | 0 | T* gt = end; |
8050 | |
|
8051 | 0 | while (lt < gt) |
8052 | 0 | { |
8053 | 0 | if (pred(*lt, pivot)) |
8054 | 0 | lt++; |
8055 | 0 | else if (*lt == pivot) |
8056 | 0 | swap(*eq++, *lt++); |
8057 | 0 | else |
8058 | 0 | swap(*lt, *--gt); |
8059 | 0 | } |
8060 | | |
8061 | | // we now have just 4 groups: = < >; move equal elements to the middle |
8062 | 0 | T* eqbeg = gt; |
8063 | |
|
8064 | 0 | for (T* it = begin; it != eq; ++it) |
8065 | 0 | swap(*it, *--eqbeg); |
8066 | |
|
8067 | 0 | *out_eqbeg = eqbeg; |
8068 | 0 | *out_eqend = gt; |
8069 | 0 | } |
8070 | | |
8071 | | template <typename I, typename Pred> PUGI_IMPL_FN void sort(I begin, I end, const Pred& pred) |
8072 | 0 | { |
8073 | | // sort large chunks |
8074 | 0 | while (end - begin > 16) |
8075 | 0 | { |
8076 | | // find median element |
8077 | 0 | I middle = begin + (end - begin) / 2; |
8078 | 0 | I median = median3(begin, middle, end - 1, pred); |
8079 | | |
8080 | | // partition in three chunks (< = >) |
8081 | 0 | I eqbeg, eqend; |
8082 | 0 | partition3(begin, end, *median, pred, &eqbeg, &eqend); |
8083 | | |
8084 | | // loop on larger half |
8085 | 0 | if (eqbeg - begin > end - eqend) |
8086 | 0 | { |
8087 | 0 | sort(eqend, end, pred); |
8088 | 0 | end = eqbeg; |
8089 | 0 | } |
8090 | 0 | else |
8091 | 0 | { |
8092 | 0 | sort(begin, eqbeg, pred); |
8093 | 0 | begin = eqend; |
8094 | 0 | } |
8095 | 0 | } |
8096 | | |
8097 | | // insertion sort small chunk |
8098 | 0 | insertion_sort(begin, end, pred); |
8099 | 0 | } |
8100 | | |
8101 | | PUGI_IMPL_FN bool hash_insert(const void** table, size_t size, const void* key) |
8102 | 0 | { |
8103 | 0 | assert(key); |
8104 | | |
8105 | 0 | unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key)); |
8106 | | |
8107 | | // MurmurHash3 32-bit finalizer |
8108 | 0 | h ^= h >> 16; |
8109 | 0 | h *= 0x85ebca6bu; |
8110 | 0 | h ^= h >> 13; |
8111 | 0 | h *= 0xc2b2ae35u; |
8112 | 0 | h ^= h >> 16; |
8113 | |
|
8114 | 0 | size_t hashmod = size - 1; |
8115 | 0 | size_t bucket = h & hashmod; |
8116 | |
|
8117 | 0 | for (size_t probe = 0; probe <= hashmod; ++probe) |
8118 | 0 | { |
8119 | 0 | if (table[bucket] == NULL) |
8120 | 0 | { |
8121 | 0 | table[bucket] = key; |
8122 | 0 | return true; |
8123 | 0 | } |
8124 | | |
8125 | 0 | if (table[bucket] == key) |
8126 | 0 | return false; |
8127 | | |
8128 | | // hash collision, quadratic probing |
8129 | 0 | bucket = (bucket + probe + 1) & hashmod; |
8130 | 0 | } |
8131 | | |
8132 | 0 | assert(false && "Hash table is full"); // unreachable |
8133 | 0 | return false; |
8134 | 0 | } |
8135 | | PUGI_IMPL_NS_END |
8136 | | |
8137 | | // Allocator used for AST and evaluation stacks |
8138 | | PUGI_IMPL_NS_BEGIN |
8139 | | static const size_t xpath_memory_page_size = |
8140 | | #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE |
8141 | | PUGIXML_MEMORY_XPATH_PAGE_SIZE |
8142 | | #else |
8143 | | 4096 |
8144 | | #endif |
8145 | | ; |
8146 | | |
8147 | | static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*); |
8148 | | |
8149 | | struct xpath_memory_block |
8150 | | { |
8151 | | xpath_memory_block* next; |
8152 | | size_t capacity; |
8153 | | |
8154 | | union |
8155 | | { |
8156 | | char data[xpath_memory_page_size]; |
8157 | | double alignment; |
8158 | | }; |
8159 | | }; |
8160 | | |
8161 | | struct xpath_allocator |
8162 | | { |
8163 | | xpath_memory_block* _root; |
8164 | | size_t _root_size; |
8165 | | bool* _error; |
8166 | | |
8167 | 0 | xpath_allocator(xpath_memory_block* root, bool* error = NULL): _root(root), _root_size(0), _error(error) |
8168 | 0 | { |
8169 | 0 | } |
8170 | | |
8171 | | void* allocate(size_t size) |
8172 | 0 | { |
8173 | | // round size up to block alignment boundary |
8174 | 0 | size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); |
8175 | |
|
8176 | 0 | if (_root_size + size <= _root->capacity) |
8177 | 0 | { |
8178 | 0 | void* buf = &_root->data[0] + _root_size; |
8179 | 0 | _root_size += size; |
8180 | 0 | return buf; |
8181 | 0 | } |
8182 | 0 | else |
8183 | 0 | { |
8184 | | // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests |
8185 | 0 | size_t block_capacity_base = sizeof(_root->data); |
8186 | 0 | size_t block_capacity_req = size + block_capacity_base / 4; |
8187 | 0 | size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req; |
8188 | |
|
8189 | 0 | size_t block_size = block_capacity + offsetof(xpath_memory_block, data); |
8190 | |
|
8191 | 0 | xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size)); |
8192 | 0 | if (!block) |
8193 | 0 | { |
8194 | 0 | if (_error) *_error = true; |
8195 | 0 | return NULL; |
8196 | 0 | } |
8197 | | |
8198 | 0 | block->next = _root; |
8199 | 0 | block->capacity = block_capacity; |
8200 | |
|
8201 | 0 | _root = block; |
8202 | 0 | _root_size = size; |
8203 | |
|
8204 | 0 | return block->data; |
8205 | 0 | } |
8206 | 0 | } |
8207 | | |
8208 | | void* reallocate(void* ptr, size_t old_size, size_t new_size) |
8209 | 0 | { |
8210 | | // round size up to block alignment boundary |
8211 | 0 | old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); |
8212 | 0 | new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); |
8213 | | |
8214 | | // we can only reallocate the last object |
8215 | 0 | assert(ptr == NULL || static_cast<char*>(ptr) + old_size == &_root->data[0] + _root_size); |
8216 | | |
8217 | | // try to reallocate the object inplace |
8218 | 0 | if (ptr && _root_size - old_size + new_size <= _root->capacity) |
8219 | 0 | { |
8220 | 0 | _root_size = _root_size - old_size + new_size; |
8221 | 0 | return ptr; |
8222 | 0 | } |
8223 | | |
8224 | | // allocate a new block |
8225 | 0 | void* result = allocate(new_size); |
8226 | 0 | if (!result) return NULL; |
8227 | | |
8228 | | // we have a new block |
8229 | 0 | if (ptr) |
8230 | 0 | { |
8231 | | // copy old data (we only support growing) |
8232 | 0 | assert(new_size >= old_size); |
8233 | 0 | memcpy(result, ptr, old_size); |
8234 | | |
8235 | | // free the previous page if it had no other objects |
8236 | 0 | assert(_root->data == result); |
8237 | 0 | assert(_root->next); |
8238 | | |
8239 | 0 | if (_root->next->data == ptr) |
8240 | 0 | { |
8241 | | // deallocate the whole page, unless it was the first one |
8242 | 0 | xpath_memory_block* next = _root->next->next; |
8243 | |
|
8244 | 0 | if (next) |
8245 | 0 | { |
8246 | 0 | xml_memory::deallocate(_root->next); |
8247 | 0 | _root->next = next; |
8248 | 0 | } |
8249 | 0 | } |
8250 | 0 | } |
8251 | | |
8252 | 0 | return result; |
8253 | 0 | } |
8254 | | |
8255 | | void revert(const xpath_allocator& state) |
8256 | 0 | { |
8257 | | // free all new pages |
8258 | 0 | xpath_memory_block* cur = _root; |
8259 | |
|
8260 | 0 | while (cur != state._root) |
8261 | 0 | { |
8262 | 0 | xpath_memory_block* next = cur->next; |
8263 | |
|
8264 | 0 | xml_memory::deallocate(cur); |
8265 | |
|
8266 | 0 | cur = next; |
8267 | 0 | } |
8268 | | |
8269 | | // restore state |
8270 | 0 | _root = state._root; |
8271 | 0 | _root_size = state._root_size; |
8272 | 0 | } |
8273 | | |
8274 | | void release() |
8275 | 0 | { |
8276 | 0 | xpath_memory_block* cur = _root; |
8277 | 0 | assert(cur); |
8278 | | |
8279 | 0 | while (cur->next) |
8280 | 0 | { |
8281 | 0 | xpath_memory_block* next = cur->next; |
8282 | |
|
8283 | 0 | xml_memory::deallocate(cur); |
8284 | |
|
8285 | 0 | cur = next; |
8286 | 0 | } |
8287 | 0 | } |
8288 | | }; |
8289 | | |
8290 | | struct xpath_allocator_capture |
8291 | | { |
8292 | 0 | xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) |
8293 | 0 | { |
8294 | 0 | } |
8295 | | |
8296 | | ~xpath_allocator_capture() |
8297 | 0 | { |
8298 | 0 | _target->revert(_state); |
8299 | 0 | } |
8300 | | |
8301 | | xpath_allocator* _target; |
8302 | | xpath_allocator _state; |
8303 | | }; |
8304 | | |
8305 | | struct xpath_stack |
8306 | | { |
8307 | | xpath_allocator* result; |
8308 | | xpath_allocator* temp; |
8309 | | }; |
8310 | | |
8311 | | struct xpath_stack_data |
8312 | | { |
8313 | | xpath_memory_block blocks[2]; |
8314 | | xpath_allocator result; |
8315 | | xpath_allocator temp; |
8316 | | xpath_stack stack; |
8317 | | bool oom; |
8318 | | |
8319 | 0 | xpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false) |
8320 | 0 | { |
8321 | 0 | blocks[0].next = blocks[1].next = NULL; |
8322 | 0 | blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); |
8323 | |
|
8324 | 0 | stack.result = &result; |
8325 | 0 | stack.temp = &temp; |
8326 | 0 | } |
8327 | | |
8328 | | ~xpath_stack_data() |
8329 | 0 | { |
8330 | 0 | result.release(); |
8331 | 0 | temp.release(); |
8332 | 0 | } |
8333 | | }; |
8334 | | PUGI_IMPL_NS_END |
8335 | | |
8336 | | // String class |
8337 | | PUGI_IMPL_NS_BEGIN |
8338 | | class xpath_string |
8339 | | { |
8340 | | const char_t* _buffer; |
8341 | | bool _uses_heap; |
8342 | | size_t _length_heap; |
8343 | | |
8344 | | static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) |
8345 | 0 | { |
8346 | 0 | char_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t))); |
8347 | 0 | if (!result) return NULL; |
8348 | | |
8349 | 0 | memcpy(result, string, length * sizeof(char_t)); |
8350 | 0 | result[length] = 0; |
8351 | |
|
8352 | 0 | return result; |
8353 | 0 | } |
8354 | | |
8355 | 0 | xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap) |
8356 | 0 | { |
8357 | 0 | } |
8358 | | |
8359 | | public: |
8360 | | static xpath_string from_const(const char_t* str) |
8361 | 0 | { |
8362 | 0 | return xpath_string(str, false, 0); |
8363 | 0 | } |
8364 | | |
8365 | | static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end) |
8366 | 0 | { |
8367 | 0 | assert(begin <= end && *end == 0); |
8368 | | |
8369 | 0 | return xpath_string(begin, true, static_cast<size_t>(end - begin)); |
8370 | 0 | } |
8371 | | |
8372 | | static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc) |
8373 | 0 | { |
8374 | 0 | assert(begin <= end); |
8375 | | |
8376 | 0 | if (begin == end) |
8377 | 0 | return xpath_string(); |
8378 | | |
8379 | 0 | size_t length = static_cast<size_t>(end - begin); |
8380 | 0 | const char_t* data = duplicate_string(begin, length, alloc); |
8381 | |
|
8382 | 0 | return data ? xpath_string(data, true, length) : xpath_string(); |
8383 | 0 | } |
8384 | | |
8385 | 0 | xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) |
8386 | 0 | { |
8387 | 0 | } |
8388 | | |
8389 | | void append(const xpath_string& o, xpath_allocator* alloc) |
8390 | 0 | { |
8391 | | // skip empty sources |
8392 | 0 | if (!*o._buffer) return; |
8393 | | |
8394 | | // fast append for constant empty target and constant source |
8395 | 0 | if (!*_buffer && !_uses_heap && !o._uses_heap) |
8396 | 0 | { |
8397 | 0 | _buffer = o._buffer; |
8398 | 0 | } |
8399 | 0 | else |
8400 | 0 | { |
8401 | | // need to make heap copy |
8402 | 0 | size_t target_length = length(); |
8403 | 0 | size_t source_length = o.length(); |
8404 | 0 | size_t result_length = target_length + source_length; |
8405 | | |
8406 | | // allocate new buffer |
8407 | 0 | char_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_buffer) : NULL, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); |
8408 | 0 | if (!result) return; |
8409 | | |
8410 | | // append first string to the new buffer in case there was no reallocation |
8411 | 0 | if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); |
8412 | | |
8413 | | // append second string to the new buffer |
8414 | 0 | memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); |
8415 | 0 | result[result_length] = 0; |
8416 | | |
8417 | | // finalize |
8418 | 0 | _buffer = result; |
8419 | 0 | _uses_heap = true; |
8420 | 0 | _length_heap = result_length; |
8421 | 0 | } |
8422 | 0 | } |
8423 | | |
8424 | | const char_t* c_str() const |
8425 | 0 | { |
8426 | 0 | return _buffer; |
8427 | 0 | } |
8428 | | |
8429 | | size_t length() const |
8430 | 0 | { |
8431 | 0 | return _uses_heap ? _length_heap : strlength(_buffer); |
8432 | 0 | } |
8433 | | |
8434 | | char_t* data(xpath_allocator* alloc) |
8435 | 0 | { |
8436 | | // make private heap copy |
8437 | 0 | if (!_uses_heap) |
8438 | 0 | { |
8439 | 0 | size_t length_ = strlength(_buffer); |
8440 | 0 | const char_t* data_ = duplicate_string(_buffer, length_, alloc); |
8441 | |
|
8442 | 0 | if (!data_) return NULL; |
8443 | | |
8444 | 0 | _buffer = data_; |
8445 | 0 | _uses_heap = true; |
8446 | 0 | _length_heap = length_; |
8447 | 0 | } |
8448 | | |
8449 | 0 | return const_cast<char_t*>(_buffer); |
8450 | 0 | } |
8451 | | |
8452 | | bool empty() const |
8453 | 0 | { |
8454 | 0 | return *_buffer == 0; |
8455 | 0 | } |
8456 | | |
8457 | | bool operator==(const xpath_string& o) const |
8458 | 0 | { |
8459 | 0 | return strequal(_buffer, o._buffer); |
8460 | 0 | } |
8461 | | |
8462 | | bool operator!=(const xpath_string& o) const |
8463 | 0 | { |
8464 | 0 | return !strequal(_buffer, o._buffer); |
8465 | 0 | } |
8466 | | |
8467 | | bool uses_heap() const |
8468 | 0 | { |
8469 | 0 | return _uses_heap; |
8470 | 0 | } |
8471 | | }; |
8472 | | PUGI_IMPL_NS_END |
8473 | | |
8474 | | PUGI_IMPL_NS_BEGIN |
8475 | | PUGI_IMPL_FN bool starts_with(const char_t* string, const char_t* pattern) |
8476 | 0 | { |
8477 | 0 | while (*pattern && *string == *pattern) |
8478 | 0 | { |
8479 | 0 | string++; |
8480 | 0 | pattern++; |
8481 | 0 | } |
8482 | |
|
8483 | 0 | return *pattern == 0; |
8484 | 0 | } |
8485 | | |
8486 | | PUGI_IMPL_FN const char_t* find_char(const char_t* s, char_t c) |
8487 | 0 | { |
8488 | | #ifdef PUGIXML_WCHAR_MODE |
8489 | | return wcschr(s, c); |
8490 | | #else |
8491 | 0 | return strchr(s, c); |
8492 | 0 | #endif |
8493 | 0 | } |
8494 | | |
8495 | | PUGI_IMPL_FN const char_t* find_substring(const char_t* s, const char_t* p) |
8496 | 0 | { |
8497 | | #ifdef PUGIXML_WCHAR_MODE |
8498 | | // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) |
8499 | | return (*p == 0) ? s : wcsstr(s, p); |
8500 | | #else |
8501 | 0 | return strstr(s, p); |
8502 | 0 | #endif |
8503 | 0 | } |
8504 | | |
8505 | | // Converts symbol to lower case, if it is an ASCII one |
8506 | | PUGI_IMPL_FN char_t tolower_ascii(char_t ch) |
8507 | 0 | { |
8508 | 0 | return static_cast<unsigned int>(ch - 'A') < 26 ? static_cast<char_t>(ch | ' ') : ch; |
8509 | 0 | } |
8510 | | |
8511 | | PUGI_IMPL_FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) |
8512 | 0 | { |
8513 | 0 | if (na.attribute()) |
8514 | 0 | return xpath_string::from_const(na.attribute().value()); |
8515 | 0 | else |
8516 | 0 | { |
8517 | 0 | xml_node n = na.node(); |
8518 | |
|
8519 | 0 | switch (n.type()) |
8520 | 0 | { |
8521 | 0 | case node_pcdata: |
8522 | 0 | case node_cdata: |
8523 | 0 | case node_comment: |
8524 | 0 | case node_pi: |
8525 | 0 | return xpath_string::from_const(n.value()); |
8526 | | |
8527 | 0 | case node_document: |
8528 | 0 | case node_element: |
8529 | 0 | { |
8530 | 0 | xpath_string result; |
8531 | | |
8532 | | // element nodes can have value if parse_embed_pcdata was used |
8533 | 0 | if (n.value()[0]) |
8534 | 0 | result.append(xpath_string::from_const(n.value()), alloc); |
8535 | |
|
8536 | 0 | xml_node cur = n.first_child(); |
8537 | |
|
8538 | 0 | while (cur && cur != n) |
8539 | 0 | { |
8540 | 0 | if (cur.type() == node_pcdata || cur.type() == node_cdata) |
8541 | 0 | result.append(xpath_string::from_const(cur.value()), alloc); |
8542 | |
|
8543 | 0 | if (cur.first_child()) |
8544 | 0 | cur = cur.first_child(); |
8545 | 0 | else if (cur.next_sibling()) |
8546 | 0 | cur = cur.next_sibling(); |
8547 | 0 | else |
8548 | 0 | { |
8549 | 0 | while (!cur.next_sibling() && cur != n) |
8550 | 0 | cur = cur.parent(); |
8551 | |
|
8552 | 0 | if (cur != n) cur = cur.next_sibling(); |
8553 | 0 | } |
8554 | 0 | } |
8555 | |
|
8556 | 0 | return result; |
8557 | 0 | } |
8558 | | |
8559 | 0 | default: |
8560 | 0 | return xpath_string(); |
8561 | 0 | } |
8562 | 0 | } |
8563 | 0 | } |
8564 | | |
8565 | | PUGI_IMPL_FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) |
8566 | 0 | { |
8567 | 0 | assert(ln->parent == rn->parent); |
8568 | | |
8569 | | // there is no common ancestor (the shared parent is null), nodes are from different documents |
8570 | 0 | if (!ln->parent) return ln < rn; |
8571 | | |
8572 | | // determine sibling order |
8573 | 0 | xml_node_struct* ls = ln; |
8574 | 0 | xml_node_struct* rs = rn; |
8575 | |
|
8576 | 0 | while (ls && rs) |
8577 | 0 | { |
8578 | 0 | if (ls == rn) return true; |
8579 | 0 | if (rs == ln) return false; |
8580 | | |
8581 | 0 | ls = ls->next_sibling; |
8582 | 0 | rs = rs->next_sibling; |
8583 | 0 | } |
8584 | | |
8585 | | // if rn sibling chain ended ln must be before rn |
8586 | 0 | return !rs; |
8587 | 0 | } |
8588 | | |
8589 | | PUGI_IMPL_FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) |
8590 | 0 | { |
8591 | | // find common ancestor at the same depth, if any |
8592 | 0 | xml_node_struct* lp = ln; |
8593 | 0 | xml_node_struct* rp = rn; |
8594 | |
|
8595 | 0 | while (lp && rp && lp->parent != rp->parent) |
8596 | 0 | { |
8597 | 0 | lp = lp->parent; |
8598 | 0 | rp = rp->parent; |
8599 | 0 | } |
8600 | | |
8601 | | // parents are the same! |
8602 | 0 | if (lp && rp) return node_is_before_sibling(lp, rp); |
8603 | | |
8604 | | // nodes are at different depths, need to normalize heights |
8605 | 0 | bool left_higher = !lp; |
8606 | |
|
8607 | 0 | while (lp) |
8608 | 0 | { |
8609 | 0 | lp = lp->parent; |
8610 | 0 | ln = ln->parent; |
8611 | 0 | } |
8612 | |
|
8613 | 0 | while (rp) |
8614 | 0 | { |
8615 | 0 | rp = rp->parent; |
8616 | 0 | rn = rn->parent; |
8617 | 0 | } |
8618 | | |
8619 | | // one node is the ancestor of the other |
8620 | 0 | if (ln == rn) return left_higher; |
8621 | | |
8622 | | // find common ancestor... again |
8623 | 0 | while (ln->parent != rn->parent) |
8624 | 0 | { |
8625 | 0 | ln = ln->parent; |
8626 | 0 | rn = rn->parent; |
8627 | 0 | } |
8628 | |
|
8629 | 0 | return node_is_before_sibling(ln, rn); |
8630 | 0 | } |
8631 | | |
8632 | | PUGI_IMPL_FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) |
8633 | 0 | { |
8634 | 0 | while (node && node != parent) node = node->parent; |
8635 | |
|
8636 | 0 | return parent && node == parent; |
8637 | 0 | } |
8638 | | |
8639 | | PUGI_IMPL_FN const void* document_buffer_order(const xpath_node& xnode) |
8640 | 0 | { |
8641 | 0 | xml_node_struct* node = xnode.node().internal_object(); |
8642 | |
|
8643 | 0 | if (node) |
8644 | 0 | { |
8645 | 0 | if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0) |
8646 | 0 | { |
8647 | 0 | if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name; |
8648 | 0 | if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value; |
8649 | 0 | } |
8650 | | |
8651 | 0 | return NULL; |
8652 | 0 | } |
8653 | | |
8654 | 0 | xml_attribute_struct* attr = xnode.attribute().internal_object(); |
8655 | |
|
8656 | 0 | if (attr) |
8657 | 0 | { |
8658 | 0 | if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0) |
8659 | 0 | { |
8660 | 0 | if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name; |
8661 | 0 | if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value; |
8662 | 0 | } |
8663 | | |
8664 | 0 | return NULL; |
8665 | 0 | } |
8666 | | |
8667 | 0 | return NULL; |
8668 | 0 | } |
8669 | | |
8670 | | struct document_order_comparator |
8671 | | { |
8672 | | bool operator()(const xpath_node& lhs, const xpath_node& rhs) const |
8673 | 0 | { |
8674 | | // optimized document order based check |
8675 | 0 | const void* lo = document_buffer_order(lhs); |
8676 | 0 | const void* ro = document_buffer_order(rhs); |
8677 | |
|
8678 | 0 | if (lo && ro) return lo < ro; |
8679 | | |
8680 | | // slow comparison |
8681 | 0 | xml_node ln = lhs.node(), rn = rhs.node(); |
8682 | | |
8683 | | // compare attributes |
8684 | 0 | if (lhs.attribute() && rhs.attribute()) |
8685 | 0 | { |
8686 | | // shared parent |
8687 | 0 | if (lhs.parent() == rhs.parent()) |
8688 | 0 | { |
8689 | | // determine sibling order |
8690 | 0 | for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) |
8691 | 0 | if (a == rhs.attribute()) |
8692 | 0 | return true; |
8693 | | |
8694 | 0 | return false; |
8695 | 0 | } |
8696 | | |
8697 | | // compare attribute parents |
8698 | 0 | ln = lhs.parent(); |
8699 | 0 | rn = rhs.parent(); |
8700 | 0 | } |
8701 | 0 | else if (lhs.attribute()) |
8702 | 0 | { |
8703 | | // attributes go after the parent element |
8704 | 0 | if (lhs.parent() == rhs.node()) return false; |
8705 | | |
8706 | 0 | ln = lhs.parent(); |
8707 | 0 | } |
8708 | 0 | else if (rhs.attribute()) |
8709 | 0 | { |
8710 | | // attributes go after the parent element |
8711 | 0 | if (rhs.parent() == lhs.node()) return true; |
8712 | | |
8713 | 0 | rn = rhs.parent(); |
8714 | 0 | } |
8715 | | |
8716 | 0 | if (ln == rn) return false; |
8717 | | |
8718 | 0 | if (!ln || !rn) return ln < rn; |
8719 | | |
8720 | 0 | return node_is_before(ln.internal_object(), rn.internal_object()); |
8721 | 0 | } |
8722 | | }; |
8723 | | |
8724 | | PUGI_IMPL_FN double gen_nan() |
8725 | 0 | { |
8726 | 0 | #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) |
8727 | 0 | PUGI_IMPL_STATIC_ASSERT(sizeof(float) == sizeof(uint32_t)); |
8728 | 0 | typedef uint32_t UI; // BCC5 workaround |
8729 | 0 | union { float f; UI i; } u; |
8730 | 0 | u.i = 0x7fc00000; |
8731 | 0 | return double(u.f); |
8732 | | #else |
8733 | | // fallback |
8734 | | const volatile double zero = 0.0; |
8735 | | return zero / zero; |
8736 | | #endif |
8737 | 0 | } |
8738 | | |
8739 | | PUGI_IMPL_FN bool is_nan(double value) |
8740 | 0 | { |
8741 | | #if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) |
8742 | | return !!_isnan(value); |
8743 | | #elif defined(fpclassify) && defined(FP_NAN) |
8744 | | return fpclassify(value) == FP_NAN; |
8745 | | #else |
8746 | | // fallback |
8747 | 0 | const volatile double v = value; |
8748 | 0 | return v != v; |
8749 | 0 | #endif |
8750 | 0 | } |
8751 | | |
8752 | | PUGI_IMPL_FN const char_t* convert_number_to_string_special(double value) |
8753 | 0 | { |
8754 | | #if defined(PUGI_IMPL_MSVC_CRT_VERSION) || defined(__BORLANDC__) |
8755 | | if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; |
8756 | | if (_isnan(value)) return PUGIXML_TEXT("NaN"); |
8757 | | return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); |
8758 | | #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) |
8759 | | switch (fpclassify(value)) |
8760 | | { |
8761 | | case FP_NAN: |
8762 | | return PUGIXML_TEXT("NaN"); |
8763 | | |
8764 | | case FP_INFINITE: |
8765 | | return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); |
8766 | | |
8767 | | case FP_ZERO: |
8768 | | return PUGIXML_TEXT("0"); |
8769 | | |
8770 | | default: |
8771 | | return 0; |
8772 | | } |
8773 | | #else |
8774 | | // fallback |
8775 | 0 | const volatile double v = value; |
8776 | |
|
8777 | 0 | if (v == 0) return PUGIXML_TEXT("0"); |
8778 | 0 | if (v != v) return PUGIXML_TEXT("NaN"); |
8779 | 0 | if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); |
8780 | 0 | return NULL; |
8781 | 0 | #endif |
8782 | 0 | } |
8783 | | |
8784 | | PUGI_IMPL_FN bool convert_number_to_boolean(double value) |
8785 | 0 | { |
8786 | 0 | return (value != 0 && !is_nan(value)); |
8787 | 0 | } |
8788 | | |
8789 | | PUGI_IMPL_FN void truncate_zeros(char* begin, char* end) |
8790 | 0 | { |
8791 | 0 | while (begin != end && end[-1] == '0') end--; |
8792 | |
|
8793 | 0 | *end = 0; |
8794 | 0 | } |
8795 | | |
8796 | | // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent |
8797 | | #if defined(PUGI_IMPL_MSVC_CRT_VERSION) && PUGI_IMPL_MSVC_CRT_VERSION >= 1400 |
8798 | | PUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) |
8799 | | { |
8800 | | // get base values |
8801 | | int sign, exponent; |
8802 | | _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign); |
8803 | | |
8804 | | // truncate redundant zeros |
8805 | | truncate_zeros(buffer, buffer + strlen(buffer)); |
8806 | | |
8807 | | // fill results |
8808 | | *out_mantissa = buffer; |
8809 | | *out_exponent = exponent; |
8810 | | } |
8811 | | #else |
8812 | | PUGI_IMPL_FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) |
8813 | 0 | { |
8814 | | // get a scientific notation value with IEEE DBL_DIG decimals |
8815 | 0 | PUGI_IMPL_SNPRINTF(buffer, "%.*e", DBL_DIG, value); |
8816 | | |
8817 | | // get the exponent (possibly negative) |
8818 | 0 | char* exponent_string = strchr(buffer, 'e'); |
8819 | 0 | assert(exponent_string); |
8820 | | |
8821 | 0 | int exponent = atoi(exponent_string + 1); |
8822 | | |
8823 | | // extract mantissa string: skip sign |
8824 | 0 | char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; |
8825 | 0 | assert(mantissa[0] != '0' && (mantissa[1] == '.' || mantissa[1] == ',')); |
8826 | | |
8827 | | // divide mantissa by 10 to eliminate integer part |
8828 | 0 | mantissa[1] = mantissa[0]; |
8829 | 0 | mantissa++; |
8830 | 0 | exponent++; |
8831 | | |
8832 | | // remove extra mantissa digits and zero-terminate mantissa |
8833 | 0 | truncate_zeros(mantissa, exponent_string); |
8834 | | |
8835 | | // fill results |
8836 | 0 | *out_mantissa = mantissa; |
8837 | 0 | *out_exponent = exponent; |
8838 | 0 | } |
8839 | | #endif |
8840 | | |
8841 | | PUGI_IMPL_FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) |
8842 | 0 | { |
8843 | | // try special number conversion |
8844 | 0 | const char_t* special = convert_number_to_string_special(value); |
8845 | 0 | if (special) return xpath_string::from_const(special); |
8846 | | |
8847 | | // get mantissa + exponent form |
8848 | 0 | char mantissa_buffer[32]; |
8849 | |
|
8850 | 0 | char* mantissa; |
8851 | 0 | int exponent; |
8852 | 0 | convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent); |
8853 | | |
8854 | | // allocate a buffer of suitable length for the number |
8855 | 0 | size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; |
8856 | 0 | char_t* result = static_cast<char_t*>(alloc->allocate(sizeof(char_t) * result_size)); |
8857 | 0 | if (!result) return xpath_string(); |
8858 | | |
8859 | | // make the number! |
8860 | 0 | char_t* s = result; |
8861 | | |
8862 | | // sign |
8863 | 0 | if (value < 0) *s++ = '-'; |
8864 | | |
8865 | | // integer part |
8866 | 0 | if (exponent <= 0) |
8867 | 0 | { |
8868 | 0 | *s++ = '0'; |
8869 | 0 | } |
8870 | 0 | else |
8871 | 0 | { |
8872 | 0 | while (exponent > 0) |
8873 | 0 | { |
8874 | 0 | assert(*mantissa == 0 || static_cast<unsigned int>(*mantissa - '0') <= 9); |
8875 | 0 | *s++ = *mantissa ? *mantissa++ : '0'; |
8876 | 0 | exponent--; |
8877 | 0 | } |
8878 | 0 | } |
8879 | | |
8880 | | // fractional part |
8881 | 0 | if (*mantissa) |
8882 | 0 | { |
8883 | | // decimal point |
8884 | 0 | *s++ = '.'; |
8885 | | |
8886 | | // extra zeroes from negative exponent |
8887 | 0 | while (exponent < 0) |
8888 | 0 | { |
8889 | 0 | *s++ = '0'; |
8890 | 0 | exponent++; |
8891 | 0 | } |
8892 | | |
8893 | | // extra mantissa digits |
8894 | 0 | while (*mantissa) |
8895 | 0 | { |
8896 | 0 | assert(static_cast<unsigned int>(*mantissa - '0') <= 9); |
8897 | 0 | *s++ = *mantissa++; |
8898 | 0 | } |
8899 | 0 | } |
8900 | | |
8901 | | // zero-terminate |
8902 | 0 | assert(s < result + result_size); |
8903 | 0 | *s = 0; |
8904 | |
|
8905 | 0 | return xpath_string::from_heap_preallocated(result, s); |
8906 | 0 | } |
8907 | | |
8908 | | PUGI_IMPL_FN bool check_string_to_number_format(const char_t* string) |
8909 | 0 | { |
8910 | | // parse leading whitespace |
8911 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string; |
8912 | | |
8913 | | // parse sign |
8914 | 0 | if (*string == '-') ++string; |
8915 | |
|
8916 | 0 | if (!*string) return false; |
8917 | | |
8918 | | // if there is no integer part, there should be a decimal part with at least one digit |
8919 | 0 | if (!PUGI_IMPL_IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI_IMPL_IS_CHARTYPEX(string[1], ctx_digit))) return false; |
8920 | | |
8921 | | // parse integer part |
8922 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string; |
8923 | | |
8924 | | // parse decimal part |
8925 | 0 | if (*string == '.') |
8926 | 0 | { |
8927 | 0 | ++string; |
8928 | |
|
8929 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*string, ctx_digit)) ++string; |
8930 | 0 | } |
8931 | | |
8932 | | // parse trailing whitespace |
8933 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*string, ct_space)) ++string; |
8934 | |
|
8935 | 0 | return *string == 0; |
8936 | 0 | } |
8937 | | |
8938 | | PUGI_IMPL_FN double convert_string_to_number(const char_t* string) |
8939 | 0 | { |
8940 | | // check string format |
8941 | 0 | if (!check_string_to_number_format(string)) return gen_nan(); |
8942 | | |
8943 | | // parse string |
8944 | | #ifdef PUGIXML_WCHAR_MODE |
8945 | | return wcstod(string, NULL); |
8946 | | #else |
8947 | 0 | return strtod(string, NULL); |
8948 | 0 | #endif |
8949 | 0 | } |
8950 | | |
8951 | | PUGI_IMPL_FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) |
8952 | 0 | { |
8953 | 0 | size_t length = static_cast<size_t>(end - begin); |
8954 | 0 | char_t* scratch = buffer; |
8955 | |
|
8956 | 0 | if (length >= sizeof(buffer) / sizeof(buffer[0])) |
8957 | 0 | { |
8958 | | // need to make dummy on-heap copy |
8959 | 0 | scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); |
8960 | 0 | if (!scratch) return false; |
8961 | 0 | } |
8962 | | |
8963 | | // copy string to zero-terminated buffer and perform conversion |
8964 | 0 | memcpy(scratch, begin, length * sizeof(char_t)); |
8965 | 0 | scratch[length] = 0; |
8966 | |
|
8967 | 0 | *out_result = convert_string_to_number(scratch); |
8968 | | |
8969 | | // free dummy buffer |
8970 | 0 | if (scratch != buffer) xml_memory::deallocate(scratch); |
8971 | |
|
8972 | 0 | return true; |
8973 | 0 | } |
8974 | | |
8975 | | PUGI_IMPL_FN double round_nearest(double value) |
8976 | 0 | { |
8977 | 0 | return floor(value + 0.5); |
8978 | 0 | } |
8979 | | |
8980 | | PUGI_IMPL_FN double round_nearest_nzero(double value) |
8981 | 0 | { |
8982 | | // same as round_nearest, but returns -0 for [-0.5, -0] |
8983 | | // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) |
8984 | 0 | return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); |
8985 | 0 | } |
8986 | | |
8987 | | PUGI_IMPL_FN const char_t* qualified_name(const xpath_node& node) |
8988 | 0 | { |
8989 | 0 | return node.attribute() ? node.attribute().name() : node.node().name(); |
8990 | 0 | } |
8991 | | |
8992 | | PUGI_IMPL_FN const char_t* local_name(const xpath_node& node) |
8993 | 0 | { |
8994 | 0 | const char_t* name = qualified_name(node); |
8995 | 0 | const char_t* p = find_char(name, ':'); |
8996 | |
|
8997 | 0 | return p ? p + 1 : name; |
8998 | 0 | } |
8999 | | |
9000 | | struct namespace_uri_predicate |
9001 | | { |
9002 | | const char_t* prefix; |
9003 | | size_t prefix_length; |
9004 | | |
9005 | | namespace_uri_predicate(const char_t* name) |
9006 | 0 | { |
9007 | 0 | const char_t* pos = find_char(name, ':'); |
9008 | |
|
9009 | 0 | prefix = pos ? name : NULL; |
9010 | 0 | prefix_length = pos ? static_cast<size_t>(pos - name) : 0; |
9011 | 0 | } |
9012 | | |
9013 | | bool operator()(xml_attribute a) const |
9014 | 0 | { |
9015 | 0 | const char_t* name = a.name(); |
9016 | |
|
9017 | 0 | if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; |
9018 | | |
9019 | 0 | return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; |
9020 | 0 | } |
9021 | | }; |
9022 | | |
9023 | | PUGI_IMPL_FN const char_t* namespace_uri(xml_node node) |
9024 | 0 | { |
9025 | 0 | namespace_uri_predicate pred = node.name(); |
9026 | |
|
9027 | 0 | xml_node p = node; |
9028 | |
|
9029 | 0 | while (p) |
9030 | 0 | { |
9031 | 0 | xml_attribute a = p.find_attribute(pred); |
9032 | |
|
9033 | 0 | if (a) return a.value(); |
9034 | | |
9035 | 0 | p = p.parent(); |
9036 | 0 | } |
9037 | | |
9038 | 0 | return PUGIXML_TEXT(""); |
9039 | 0 | } |
9040 | | |
9041 | | PUGI_IMPL_FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) |
9042 | 0 | { |
9043 | 0 | namespace_uri_predicate pred = attr.name(); |
9044 | | |
9045 | | // Default namespace does not apply to attributes |
9046 | 0 | if (!pred.prefix) return PUGIXML_TEXT(""); |
9047 | | |
9048 | 0 | xml_node p = parent; |
9049 | |
|
9050 | 0 | while (p) |
9051 | 0 | { |
9052 | 0 | xml_attribute a = p.find_attribute(pred); |
9053 | |
|
9054 | 0 | if (a) return a.value(); |
9055 | | |
9056 | 0 | p = p.parent(); |
9057 | 0 | } |
9058 | | |
9059 | 0 | return PUGIXML_TEXT(""); |
9060 | 0 | } |
9061 | | |
9062 | | PUGI_IMPL_FN const char_t* namespace_uri(const xpath_node& node) |
9063 | 0 | { |
9064 | 0 | return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); |
9065 | 0 | } |
9066 | | |
9067 | | PUGI_IMPL_FN char_t* normalize_space(char_t* buffer) |
9068 | 0 | { |
9069 | 0 | char_t* write = buffer; |
9070 | |
|
9071 | 0 | for (char_t* it = buffer; *it; ) |
9072 | 0 | { |
9073 | 0 | char_t ch = *it++; |
9074 | |
|
9075 | 0 | if (PUGI_IMPL_IS_CHARTYPE(ch, ct_space)) |
9076 | 0 | { |
9077 | | // replace whitespace sequence with single space |
9078 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*it, ct_space)) it++; |
9079 | | |
9080 | | // avoid leading spaces |
9081 | 0 | if (write != buffer) *write++ = ' '; |
9082 | 0 | } |
9083 | 0 | else *write++ = ch; |
9084 | 0 | } |
9085 | | |
9086 | | // remove trailing space |
9087 | 0 | if (write != buffer && PUGI_IMPL_IS_CHARTYPE(write[-1], ct_space)) write--; |
9088 | | |
9089 | | // zero-terminate |
9090 | 0 | *write = 0; |
9091 | |
|
9092 | 0 | return write; |
9093 | 0 | } |
9094 | | |
9095 | | PUGI_IMPL_FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) |
9096 | 0 | { |
9097 | 0 | char_t* write = buffer; |
9098 | |
|
9099 | 0 | while (*buffer) |
9100 | 0 | { |
9101 | 0 | PUGI_IMPL_DMC_VOLATILE char_t ch = *buffer++; |
9102 | |
|
9103 | 0 | const char_t* pos = find_char(from, ch); |
9104 | |
|
9105 | 0 | if (!pos) |
9106 | 0 | *write++ = ch; // do not process |
9107 | 0 | else if (static_cast<size_t>(pos - from) < to_length) |
9108 | 0 | *write++ = to[pos - from]; // replace |
9109 | 0 | } |
9110 | | |
9111 | | // zero-terminate |
9112 | 0 | *write = 0; |
9113 | |
|
9114 | 0 | return write; |
9115 | 0 | } |
9116 | | |
9117 | | PUGI_IMPL_FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) |
9118 | 0 | { |
9119 | 0 | unsigned char table[128] = {0}; |
9120 | |
|
9121 | 0 | while (*from) |
9122 | 0 | { |
9123 | 0 | unsigned int fc = static_cast<unsigned int>(*from); |
9124 | 0 | unsigned int tc = static_cast<unsigned int>(*to); |
9125 | |
|
9126 | 0 | if (fc >= 128 || tc >= 128) |
9127 | 0 | return NULL; |
9128 | | |
9129 | | // code=128 means "skip character" |
9130 | 0 | if (!table[fc]) |
9131 | 0 | table[fc] = static_cast<unsigned char>(tc ? tc : 128); |
9132 | |
|
9133 | 0 | from++; |
9134 | 0 | if (tc) to++; |
9135 | 0 | } |
9136 | | |
9137 | 0 | for (int i = 0; i < 128; ++i) |
9138 | 0 | if (!table[i]) |
9139 | 0 | table[i] = static_cast<unsigned char>(i); |
9140 | |
|
9141 | 0 | void* result = alloc->allocate(sizeof(table)); |
9142 | 0 | if (!result) return NULL; |
9143 | | |
9144 | 0 | memcpy(result, table, sizeof(table)); |
9145 | |
|
9146 | 0 | return static_cast<unsigned char*>(result); |
9147 | 0 | } |
9148 | | |
9149 | | PUGI_IMPL_FN char_t* translate_table(char_t* buffer, const unsigned char* table) |
9150 | 0 | { |
9151 | 0 | char_t* write = buffer; |
9152 | |
|
9153 | 0 | while (*buffer) |
9154 | 0 | { |
9155 | 0 | char_t ch = *buffer++; |
9156 | 0 | unsigned int index = static_cast<unsigned int>(ch); |
9157 | |
|
9158 | 0 | if (index < 128) |
9159 | 0 | { |
9160 | 0 | unsigned char code = table[index]; |
9161 | | |
9162 | | // code=128 means "skip character" (table size is 128 so 128 can be a special value) |
9163 | | // this code skips these characters without extra branches |
9164 | 0 | *write = static_cast<char_t>(code); |
9165 | 0 | write += 1 - (code >> 7); |
9166 | 0 | } |
9167 | 0 | else |
9168 | 0 | { |
9169 | 0 | *write++ = ch; |
9170 | 0 | } |
9171 | 0 | } |
9172 | | |
9173 | | // zero-terminate |
9174 | 0 | *write = 0; |
9175 | |
|
9176 | 0 | return write; |
9177 | 0 | } |
9178 | | |
9179 | | inline bool is_xpath_attribute(const char_t* name) |
9180 | 0 | { |
9181 | 0 | return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')); |
9182 | 0 | } |
9183 | | |
9184 | | struct xpath_variable_boolean: xpath_variable |
9185 | | { |
9186 | 0 | xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false) |
9187 | 0 | { |
9188 | 0 | } |
9189 | | |
9190 | | bool value; |
9191 | | char_t name[1]; |
9192 | | }; |
9193 | | |
9194 | | struct xpath_variable_number: xpath_variable |
9195 | | { |
9196 | 0 | xpath_variable_number(): xpath_variable(xpath_type_number), value(0) |
9197 | 0 | { |
9198 | 0 | } |
9199 | | |
9200 | | double value; |
9201 | | char_t name[1]; |
9202 | | }; |
9203 | | |
9204 | | struct xpath_variable_string: xpath_variable |
9205 | | { |
9206 | 0 | xpath_variable_string(): xpath_variable(xpath_type_string), value(NULL) |
9207 | 0 | { |
9208 | 0 | } |
9209 | | |
9210 | | ~xpath_variable_string() |
9211 | 0 | { |
9212 | 0 | if (value) xml_memory::deallocate(value); |
9213 | 0 | } |
9214 | | |
9215 | | char_t* value; |
9216 | | char_t name[1]; |
9217 | | }; |
9218 | | |
9219 | | struct xpath_variable_node_set: xpath_variable |
9220 | | { |
9221 | 0 | xpath_variable_node_set(): xpath_variable(xpath_type_node_set) |
9222 | 0 | { |
9223 | 0 | } |
9224 | | |
9225 | | xpath_node_set value; |
9226 | | char_t name[1]; |
9227 | | }; |
9228 | | |
9229 | | PUGI_IMPL_FN PUGI_IMPL_UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str) |
9230 | 0 | { |
9231 | | // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) |
9232 | 0 | unsigned int result = 0; |
9233 | |
|
9234 | 0 | while (*str) |
9235 | 0 | { |
9236 | 0 | result += static_cast<unsigned int>(*str++); |
9237 | 0 | result += result << 10; |
9238 | 0 | result ^= result >> 6; |
9239 | 0 | } |
9240 | |
|
9241 | 0 | result += result << 3; |
9242 | 0 | result ^= result >> 11; |
9243 | 0 | result += result << 15; |
9244 | |
|
9245 | 0 | return result; |
9246 | 0 | } |
9247 | | |
9248 | | template <typename T> PUGI_IMPL_FN T* new_xpath_variable(const char_t* name) |
9249 | 0 | { |
9250 | 0 | size_t length = strlength(name); |
9251 | 0 | if (length == 0) return NULL; // empty variable names are invalid |
9252 | | |
9253 | | // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters |
9254 | 0 | void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); |
9255 | 0 | if (!memory) return NULL; |
9256 | | |
9257 | 0 | T* result = new (memory) T(); |
9258 | |
|
9259 | 0 | memcpy(result->name, name, (length + 1) * sizeof(char_t)); |
9260 | |
|
9261 | 0 | return result; |
9262 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_variable_node_set* pugi::impl::(anonymous namespace)::new_xpath_variable<pugi::impl::(anonymous namespace)::xpath_variable_node_set>(char const*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_variable_number* pugi::impl::(anonymous namespace)::new_xpath_variable<pugi::impl::(anonymous namespace)::xpath_variable_number>(char const*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_variable_string* pugi::impl::(anonymous namespace)::new_xpath_variable<pugi::impl::(anonymous namespace)::xpath_variable_string>(char const*) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_variable_boolean* pugi::impl::(anonymous namespace)::new_xpath_variable<pugi::impl::(anonymous namespace)::xpath_variable_boolean>(char const*) |
9263 | | |
9264 | | PUGI_IMPL_FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) |
9265 | 0 | { |
9266 | 0 | switch (type) |
9267 | 0 | { |
9268 | 0 | case xpath_type_node_set: |
9269 | 0 | return new_xpath_variable<xpath_variable_node_set>(name); |
9270 | | |
9271 | 0 | case xpath_type_number: |
9272 | 0 | return new_xpath_variable<xpath_variable_number>(name); |
9273 | | |
9274 | 0 | case xpath_type_string: |
9275 | 0 | return new_xpath_variable<xpath_variable_string>(name); |
9276 | | |
9277 | 0 | case xpath_type_boolean: |
9278 | 0 | return new_xpath_variable<xpath_variable_boolean>(name); |
9279 | | |
9280 | 0 | default: |
9281 | 0 | return NULL; |
9282 | 0 | } |
9283 | 0 | } |
9284 | | |
9285 | | template <typename T> PUGI_IMPL_FN void delete_xpath_variable(T* var) |
9286 | 0 | { |
9287 | 0 | var->~T(); |
9288 | 0 | xml_memory::deallocate(var); |
9289 | 0 | } Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::delete_xpath_variable<pugi::impl::(anonymous namespace)::xpath_variable_node_set>(pugi::impl::(anonymous namespace)::xpath_variable_node_set*) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::delete_xpath_variable<pugi::impl::(anonymous namespace)::xpath_variable_number>(pugi::impl::(anonymous namespace)::xpath_variable_number*) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::delete_xpath_variable<pugi::impl::(anonymous namespace)::xpath_variable_string>(pugi::impl::(anonymous namespace)::xpath_variable_string*) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::delete_xpath_variable<pugi::impl::(anonymous namespace)::xpath_variable_boolean>(pugi::impl::(anonymous namespace)::xpath_variable_boolean*) |
9290 | | |
9291 | | PUGI_IMPL_FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) |
9292 | 0 | { |
9293 | 0 | switch (type) |
9294 | 0 | { |
9295 | 0 | case xpath_type_node_set: |
9296 | 0 | delete_xpath_variable(static_cast<xpath_variable_node_set*>(var)); |
9297 | 0 | break; |
9298 | | |
9299 | 0 | case xpath_type_number: |
9300 | 0 | delete_xpath_variable(static_cast<xpath_variable_number*>(var)); |
9301 | 0 | break; |
9302 | | |
9303 | 0 | case xpath_type_string: |
9304 | 0 | delete_xpath_variable(static_cast<xpath_variable_string*>(var)); |
9305 | 0 | break; |
9306 | | |
9307 | 0 | case xpath_type_boolean: |
9308 | 0 | delete_xpath_variable(static_cast<xpath_variable_boolean*>(var)); |
9309 | 0 | break; |
9310 | | |
9311 | 0 | default: |
9312 | 0 | assert(false && "Invalid variable type"); // unreachable |
9313 | 0 | } |
9314 | 0 | } |
9315 | | |
9316 | | PUGI_IMPL_FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) |
9317 | 0 | { |
9318 | 0 | switch (rhs->type()) |
9319 | 0 | { |
9320 | 0 | case xpath_type_node_set: |
9321 | 0 | return lhs->set(static_cast<const xpath_variable_node_set*>(rhs)->value); |
9322 | | |
9323 | 0 | case xpath_type_number: |
9324 | 0 | return lhs->set(static_cast<const xpath_variable_number*>(rhs)->value); |
9325 | | |
9326 | 0 | case xpath_type_string: |
9327 | 0 | return lhs->set(static_cast<const xpath_variable_string*>(rhs)->value); |
9328 | | |
9329 | 0 | case xpath_type_boolean: |
9330 | 0 | return lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value); |
9331 | | |
9332 | 0 | default: |
9333 | 0 | assert(false && "Invalid variable type"); // unreachable |
9334 | 0 | return false; |
9335 | 0 | } |
9336 | 0 | } |
9337 | | |
9338 | | PUGI_IMPL_FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) |
9339 | 0 | { |
9340 | 0 | size_t length = static_cast<size_t>(end - begin); |
9341 | 0 | char_t* scratch = buffer; |
9342 | |
|
9343 | 0 | if (length >= sizeof(buffer) / sizeof(buffer[0])) |
9344 | 0 | { |
9345 | | // need to make dummy on-heap copy |
9346 | 0 | scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); |
9347 | 0 | if (!scratch) return false; |
9348 | 0 | } |
9349 | | |
9350 | | // copy string to zero-terminated buffer and perform lookup |
9351 | 0 | memcpy(scratch, begin, length * sizeof(char_t)); |
9352 | 0 | scratch[length] = 0; |
9353 | |
|
9354 | 0 | *out_result = set->get(scratch); |
9355 | | |
9356 | | // free dummy buffer |
9357 | 0 | if (scratch != buffer) xml_memory::deallocate(scratch); |
9358 | |
|
9359 | 0 | return true; |
9360 | 0 | } |
9361 | | PUGI_IMPL_NS_END |
9362 | | |
9363 | | // Internal node set class |
9364 | | PUGI_IMPL_NS_BEGIN |
9365 | | PUGI_IMPL_FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) |
9366 | 0 | { |
9367 | 0 | if (end - begin < 2) |
9368 | 0 | return xpath_node_set::type_sorted; |
9369 | | |
9370 | 0 | document_order_comparator cmp; |
9371 | |
|
9372 | 0 | bool first = cmp(begin[0], begin[1]); |
9373 | |
|
9374 | 0 | for (const xpath_node* it = begin + 1; it + 1 < end; ++it) |
9375 | 0 | if (cmp(it[0], it[1]) != first) |
9376 | 0 | return xpath_node_set::type_unsorted; |
9377 | | |
9378 | 0 | return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; |
9379 | 0 | } |
9380 | | |
9381 | | PUGI_IMPL_FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) |
9382 | 0 | { |
9383 | 0 | xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; |
9384 | |
|
9385 | 0 | if (type == xpath_node_set::type_unsorted) |
9386 | 0 | { |
9387 | 0 | xpath_node_set::type_t sorted = xpath_get_order(begin, end); |
9388 | |
|
9389 | 0 | if (sorted == xpath_node_set::type_unsorted) |
9390 | 0 | { |
9391 | 0 | sort(begin, end, document_order_comparator()); |
9392 | |
|
9393 | 0 | type = xpath_node_set::type_sorted; |
9394 | 0 | } |
9395 | 0 | else |
9396 | 0 | type = sorted; |
9397 | 0 | } |
9398 | |
|
9399 | 0 | if (type != order) reverse(begin, end); |
9400 | |
|
9401 | 0 | return order; |
9402 | 0 | } |
9403 | | |
9404 | | PUGI_IMPL_FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) |
9405 | 0 | { |
9406 | 0 | if (begin == end) return xpath_node(); |
9407 | | |
9408 | 0 | switch (type) |
9409 | 0 | { |
9410 | 0 | case xpath_node_set::type_sorted: |
9411 | 0 | return *begin; |
9412 | | |
9413 | 0 | case xpath_node_set::type_sorted_reverse: |
9414 | 0 | return *(end - 1); |
9415 | | |
9416 | 0 | case xpath_node_set::type_unsorted: |
9417 | 0 | return *min_element(begin, end, document_order_comparator()); |
9418 | | |
9419 | 0 | default: |
9420 | 0 | assert(false && "Invalid node set type"); // unreachable |
9421 | 0 | return xpath_node(); |
9422 | 0 | } |
9423 | 0 | } |
9424 | | |
9425 | | class xpath_node_set_raw |
9426 | | { |
9427 | | xpath_node_set::type_t _type; |
9428 | | |
9429 | | xpath_node* _begin; |
9430 | | xpath_node* _end; |
9431 | | xpath_node* _eos; |
9432 | | |
9433 | | public: |
9434 | 0 | xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(NULL), _end(NULL), _eos(NULL) |
9435 | 0 | { |
9436 | 0 | } |
9437 | | |
9438 | | xpath_node* begin() const |
9439 | 0 | { |
9440 | 0 | return _begin; |
9441 | 0 | } |
9442 | | |
9443 | | xpath_node* end() const |
9444 | 0 | { |
9445 | 0 | return _end; |
9446 | 0 | } |
9447 | | |
9448 | | bool empty() const |
9449 | 0 | { |
9450 | 0 | return _begin == _end; |
9451 | 0 | } |
9452 | | |
9453 | | size_t size() const |
9454 | 0 | { |
9455 | 0 | return static_cast<size_t>(_end - _begin); |
9456 | 0 | } |
9457 | | |
9458 | | xpath_node first() const |
9459 | 0 | { |
9460 | 0 | return xpath_first(_begin, _end, _type); |
9461 | 0 | } |
9462 | | |
9463 | | void push_back_grow(const xpath_node& node, xpath_allocator* alloc); |
9464 | | |
9465 | | void push_back(const xpath_node& node, xpath_allocator* alloc) |
9466 | 0 | { |
9467 | 0 | if (_end != _eos) |
9468 | 0 | *_end++ = node; |
9469 | 0 | else |
9470 | 0 | push_back_grow(node, alloc); |
9471 | 0 | } |
9472 | | |
9473 | | void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) |
9474 | 0 | { |
9475 | 0 | if (begin_ == end_) return; |
9476 | | |
9477 | 0 | size_t size_ = static_cast<size_t>(_end - _begin); |
9478 | 0 | size_t capacity = static_cast<size_t>(_eos - _begin); |
9479 | 0 | size_t count = static_cast<size_t>(end_ - begin_); |
9480 | |
|
9481 | 0 | if (size_ + count > capacity) |
9482 | 0 | { |
9483 | | // reallocate the old array or allocate a new one |
9484 | 0 | xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); |
9485 | 0 | if (!data) return; |
9486 | | |
9487 | | // finalize |
9488 | 0 | _begin = data; |
9489 | 0 | _end = data + size_; |
9490 | 0 | _eos = data + size_ + count; |
9491 | 0 | } |
9492 | | |
9493 | 0 | memcpy(_end, begin_, count * sizeof(xpath_node)); |
9494 | 0 | _end += count; |
9495 | 0 | } |
9496 | | |
9497 | | void sort_do() |
9498 | 0 | { |
9499 | 0 | _type = xpath_sort(_begin, _end, _type, false); |
9500 | 0 | } |
9501 | | |
9502 | | void truncate(xpath_node* pos) |
9503 | 0 | { |
9504 | 0 | assert(_begin <= pos && pos <= _end); |
9505 | | |
9506 | 0 | _end = pos; |
9507 | 0 | } |
9508 | | |
9509 | | void remove_duplicates(xpath_allocator* alloc) |
9510 | 0 | { |
9511 | 0 | if (_type == xpath_node_set::type_unsorted && _end - _begin > 2) |
9512 | 0 | { |
9513 | 0 | xpath_allocator_capture cr(alloc); |
9514 | |
|
9515 | 0 | size_t size_ = static_cast<size_t>(_end - _begin); |
9516 | |
|
9517 | 0 | size_t hash_size = 1; |
9518 | 0 | while (hash_size < size_ + size_ / 2) hash_size *= 2; |
9519 | |
|
9520 | 0 | const void** hash_data = static_cast<const void**>(alloc->allocate(hash_size * sizeof(void*))); |
9521 | 0 | if (!hash_data) return; |
9522 | | |
9523 | 0 | memset(hash_data, 0, hash_size * sizeof(void*)); |
9524 | |
|
9525 | 0 | xpath_node* write = _begin; |
9526 | |
|
9527 | 0 | for (xpath_node* it = _begin; it != _end; ++it) |
9528 | 0 | { |
9529 | 0 | const void* attr = it->attribute().internal_object(); |
9530 | 0 | const void* node = it->node().internal_object(); |
9531 | 0 | const void* key = attr ? attr : node; |
9532 | |
|
9533 | 0 | if (key && hash_insert(hash_data, hash_size, key)) |
9534 | 0 | { |
9535 | 0 | *write++ = *it; |
9536 | 0 | } |
9537 | 0 | } |
9538 | |
|
9539 | 0 | _end = write; |
9540 | 0 | } |
9541 | 0 | else |
9542 | 0 | { |
9543 | 0 | _end = unique(_begin, _end); |
9544 | 0 | } |
9545 | 0 | } |
9546 | | |
9547 | | xpath_node_set::type_t type() const |
9548 | 0 | { |
9549 | 0 | return _type; |
9550 | 0 | } |
9551 | | |
9552 | | void set_type(xpath_node_set::type_t value) |
9553 | 0 | { |
9554 | 0 | _type = value; |
9555 | 0 | } |
9556 | | }; |
9557 | | |
9558 | | PUGI_IMPL_FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) |
9559 | 0 | { |
9560 | 0 | size_t capacity = static_cast<size_t>(_eos - _begin); |
9561 | | |
9562 | | // get new capacity (1.5x rule) |
9563 | 0 | size_t new_capacity = capacity + capacity / 2 + 1; |
9564 | | |
9565 | | // reallocate the old array or allocate a new one |
9566 | 0 | xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); |
9567 | 0 | if (!data) return; |
9568 | | |
9569 | | // finalize |
9570 | 0 | _begin = data; |
9571 | 0 | _end = data + capacity; |
9572 | 0 | _eos = data + new_capacity; |
9573 | | |
9574 | | // push |
9575 | 0 | *_end++ = node; |
9576 | 0 | } |
9577 | | PUGI_IMPL_NS_END |
9578 | | |
9579 | | PUGI_IMPL_NS_BEGIN |
9580 | | struct xpath_context |
9581 | | { |
9582 | | xpath_node n; |
9583 | | size_t position, size; |
9584 | | |
9585 | 0 | xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) |
9586 | 0 | { |
9587 | 0 | } |
9588 | | }; |
9589 | | |
9590 | | enum lexeme_t |
9591 | | { |
9592 | | lex_none = 0, |
9593 | | lex_equal, |
9594 | | lex_not_equal, |
9595 | | lex_less, |
9596 | | lex_greater, |
9597 | | lex_less_or_equal, |
9598 | | lex_greater_or_equal, |
9599 | | lex_plus, |
9600 | | lex_minus, |
9601 | | lex_multiply, |
9602 | | lex_union, |
9603 | | lex_var_ref, |
9604 | | lex_open_brace, |
9605 | | lex_close_brace, |
9606 | | lex_quoted_string, |
9607 | | lex_number, |
9608 | | lex_slash, |
9609 | | lex_double_slash, |
9610 | | lex_open_square_brace, |
9611 | | lex_close_square_brace, |
9612 | | lex_string, |
9613 | | lex_comma, |
9614 | | lex_axis_attribute, |
9615 | | lex_dot, |
9616 | | lex_double_dot, |
9617 | | lex_double_colon, |
9618 | | lex_eof |
9619 | | }; |
9620 | | |
9621 | | struct xpath_lexer_string |
9622 | | { |
9623 | | const char_t* begin; |
9624 | | const char_t* end; |
9625 | | |
9626 | 0 | xpath_lexer_string(): begin(NULL), end(NULL) |
9627 | 0 | { |
9628 | 0 | } |
9629 | | |
9630 | | bool operator==(const char_t* other) const |
9631 | 0 | { |
9632 | 0 | size_t length = static_cast<size_t>(end - begin); |
9633 | |
|
9634 | 0 | return strequalrange(other, begin, length); |
9635 | 0 | } |
9636 | | }; |
9637 | | |
9638 | | class xpath_lexer |
9639 | | { |
9640 | | const char_t* _cur; |
9641 | | const char_t* _cur_lexeme_pos; |
9642 | | xpath_lexer_string _cur_lexeme_contents; |
9643 | | |
9644 | | lexeme_t _cur_lexeme; |
9645 | | |
9646 | | public: |
9647 | 0 | explicit xpath_lexer(const char_t* query): _cur(query) |
9648 | 0 | { |
9649 | 0 | next(); |
9650 | 0 | } |
9651 | | |
9652 | | const char_t* state() const |
9653 | 0 | { |
9654 | 0 | return _cur; |
9655 | 0 | } |
9656 | | |
9657 | | void next() |
9658 | 0 | { |
9659 | 0 | const char_t* cur = _cur; |
9660 | |
|
9661 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*cur, ct_space)) ++cur; |
9662 | | |
9663 | | // save lexeme position for error reporting |
9664 | 0 | _cur_lexeme_pos = cur; |
9665 | |
|
9666 | 0 | switch (*cur) |
9667 | 0 | { |
9668 | 0 | case 0: |
9669 | 0 | _cur_lexeme = lex_eof; |
9670 | 0 | break; |
9671 | | |
9672 | 0 | case '>': |
9673 | 0 | if (*(cur+1) == '=') |
9674 | 0 | { |
9675 | 0 | cur += 2; |
9676 | 0 | _cur_lexeme = lex_greater_or_equal; |
9677 | 0 | } |
9678 | 0 | else |
9679 | 0 | { |
9680 | 0 | cur += 1; |
9681 | 0 | _cur_lexeme = lex_greater; |
9682 | 0 | } |
9683 | 0 | break; |
9684 | | |
9685 | 0 | case '<': |
9686 | 0 | if (*(cur+1) == '=') |
9687 | 0 | { |
9688 | 0 | cur += 2; |
9689 | 0 | _cur_lexeme = lex_less_or_equal; |
9690 | 0 | } |
9691 | 0 | else |
9692 | 0 | { |
9693 | 0 | cur += 1; |
9694 | 0 | _cur_lexeme = lex_less; |
9695 | 0 | } |
9696 | 0 | break; |
9697 | | |
9698 | 0 | case '!': |
9699 | 0 | if (*(cur+1) == '=') |
9700 | 0 | { |
9701 | 0 | cur += 2; |
9702 | 0 | _cur_lexeme = lex_not_equal; |
9703 | 0 | } |
9704 | 0 | else |
9705 | 0 | { |
9706 | 0 | _cur_lexeme = lex_none; |
9707 | 0 | } |
9708 | 0 | break; |
9709 | | |
9710 | 0 | case '=': |
9711 | 0 | cur += 1; |
9712 | 0 | _cur_lexeme = lex_equal; |
9713 | |
|
9714 | 0 | break; |
9715 | | |
9716 | 0 | case '+': |
9717 | 0 | cur += 1; |
9718 | 0 | _cur_lexeme = lex_plus; |
9719 | |
|
9720 | 0 | break; |
9721 | | |
9722 | 0 | case '-': |
9723 | 0 | cur += 1; |
9724 | 0 | _cur_lexeme = lex_minus; |
9725 | |
|
9726 | 0 | break; |
9727 | | |
9728 | 0 | case '*': |
9729 | 0 | cur += 1; |
9730 | 0 | _cur_lexeme = lex_multiply; |
9731 | |
|
9732 | 0 | break; |
9733 | | |
9734 | 0 | case '|': |
9735 | 0 | cur += 1; |
9736 | 0 | _cur_lexeme = lex_union; |
9737 | |
|
9738 | 0 | break; |
9739 | | |
9740 | 0 | case '$': |
9741 | 0 | cur += 1; |
9742 | |
|
9743 | 0 | if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol)) |
9744 | 0 | { |
9745 | 0 | _cur_lexeme_contents.begin = cur; |
9746 | |
|
9747 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; |
9748 | |
|
9749 | 0 | if (cur[0] == ':' && PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // qname |
9750 | 0 | { |
9751 | 0 | cur++; // : |
9752 | |
|
9753 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; |
9754 | 0 | } |
9755 | |
|
9756 | 0 | _cur_lexeme_contents.end = cur; |
9757 | |
|
9758 | 0 | _cur_lexeme = lex_var_ref; |
9759 | 0 | } |
9760 | 0 | else |
9761 | 0 | { |
9762 | 0 | _cur_lexeme = lex_none; |
9763 | 0 | } |
9764 | |
|
9765 | 0 | break; |
9766 | | |
9767 | 0 | case '(': |
9768 | 0 | cur += 1; |
9769 | 0 | _cur_lexeme = lex_open_brace; |
9770 | |
|
9771 | 0 | break; |
9772 | | |
9773 | 0 | case ')': |
9774 | 0 | cur += 1; |
9775 | 0 | _cur_lexeme = lex_close_brace; |
9776 | |
|
9777 | 0 | break; |
9778 | | |
9779 | 0 | case '[': |
9780 | 0 | cur += 1; |
9781 | 0 | _cur_lexeme = lex_open_square_brace; |
9782 | |
|
9783 | 0 | break; |
9784 | | |
9785 | 0 | case ']': |
9786 | 0 | cur += 1; |
9787 | 0 | _cur_lexeme = lex_close_square_brace; |
9788 | |
|
9789 | 0 | break; |
9790 | | |
9791 | 0 | case ',': |
9792 | 0 | cur += 1; |
9793 | 0 | _cur_lexeme = lex_comma; |
9794 | |
|
9795 | 0 | break; |
9796 | | |
9797 | 0 | case '/': |
9798 | 0 | if (*(cur+1) == '/') |
9799 | 0 | { |
9800 | 0 | cur += 2; |
9801 | 0 | _cur_lexeme = lex_double_slash; |
9802 | 0 | } |
9803 | 0 | else |
9804 | 0 | { |
9805 | 0 | cur += 1; |
9806 | 0 | _cur_lexeme = lex_slash; |
9807 | 0 | } |
9808 | 0 | break; |
9809 | | |
9810 | 0 | case '.': |
9811 | 0 | if (*(cur+1) == '.') |
9812 | 0 | { |
9813 | 0 | cur += 2; |
9814 | 0 | _cur_lexeme = lex_double_dot; |
9815 | 0 | } |
9816 | 0 | else if (PUGI_IMPL_IS_CHARTYPEX(*(cur+1), ctx_digit)) |
9817 | 0 | { |
9818 | 0 | _cur_lexeme_contents.begin = cur; // . |
9819 | |
|
9820 | 0 | ++cur; |
9821 | |
|
9822 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; |
9823 | |
|
9824 | 0 | _cur_lexeme_contents.end = cur; |
9825 | |
|
9826 | 0 | _cur_lexeme = lex_number; |
9827 | 0 | } |
9828 | 0 | else |
9829 | 0 | { |
9830 | 0 | cur += 1; |
9831 | 0 | _cur_lexeme = lex_dot; |
9832 | 0 | } |
9833 | 0 | break; |
9834 | | |
9835 | 0 | case '@': |
9836 | 0 | cur += 1; |
9837 | 0 | _cur_lexeme = lex_axis_attribute; |
9838 | |
|
9839 | 0 | break; |
9840 | | |
9841 | 0 | case '"': |
9842 | 0 | case '\'': |
9843 | 0 | { |
9844 | 0 | char_t terminator = *cur; |
9845 | |
|
9846 | 0 | ++cur; |
9847 | |
|
9848 | 0 | _cur_lexeme_contents.begin = cur; |
9849 | 0 | while (*cur && *cur != terminator) cur++; |
9850 | 0 | _cur_lexeme_contents.end = cur; |
9851 | |
|
9852 | 0 | if (!*cur) |
9853 | 0 | _cur_lexeme = lex_none; |
9854 | 0 | else |
9855 | 0 | { |
9856 | 0 | cur += 1; |
9857 | 0 | _cur_lexeme = lex_quoted_string; |
9858 | 0 | } |
9859 | |
|
9860 | 0 | break; |
9861 | 0 | } |
9862 | | |
9863 | 0 | case ':': |
9864 | 0 | if (*(cur+1) == ':') |
9865 | 0 | { |
9866 | 0 | cur += 2; |
9867 | 0 | _cur_lexeme = lex_double_colon; |
9868 | 0 | } |
9869 | 0 | else |
9870 | 0 | { |
9871 | 0 | _cur_lexeme = lex_none; |
9872 | 0 | } |
9873 | 0 | break; |
9874 | | |
9875 | 0 | default: |
9876 | 0 | if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) |
9877 | 0 | { |
9878 | 0 | _cur_lexeme_contents.begin = cur; |
9879 | |
|
9880 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; |
9881 | |
|
9882 | 0 | if (*cur == '.') |
9883 | 0 | { |
9884 | 0 | cur++; |
9885 | |
|
9886 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_digit)) cur++; |
9887 | 0 | } |
9888 | |
|
9889 | 0 | _cur_lexeme_contents.end = cur; |
9890 | |
|
9891 | 0 | _cur_lexeme = lex_number; |
9892 | 0 | } |
9893 | 0 | else if (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_start_symbol)) |
9894 | 0 | { |
9895 | 0 | _cur_lexeme_contents.begin = cur; |
9896 | |
|
9897 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; |
9898 | |
|
9899 | 0 | if (cur[0] == ':') |
9900 | 0 | { |
9901 | 0 | if (cur[1] == '*') // namespace test ncname:* |
9902 | 0 | { |
9903 | 0 | cur += 2; // :* |
9904 | 0 | } |
9905 | 0 | else if (PUGI_IMPL_IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname |
9906 | 0 | { |
9907 | 0 | cur++; // : |
9908 | |
|
9909 | 0 | while (PUGI_IMPL_IS_CHARTYPEX(*cur, ctx_symbol)) cur++; |
9910 | 0 | } |
9911 | 0 | } |
9912 | |
|
9913 | 0 | _cur_lexeme_contents.end = cur; |
9914 | |
|
9915 | 0 | _cur_lexeme = lex_string; |
9916 | 0 | } |
9917 | 0 | else |
9918 | 0 | { |
9919 | 0 | _cur_lexeme = lex_none; |
9920 | 0 | } |
9921 | 0 | } |
9922 | | |
9923 | 0 | _cur = cur; |
9924 | 0 | } |
9925 | | |
9926 | | lexeme_t current() const |
9927 | 0 | { |
9928 | 0 | return _cur_lexeme; |
9929 | 0 | } |
9930 | | |
9931 | | const char_t* current_pos() const |
9932 | 0 | { |
9933 | 0 | return _cur_lexeme_pos; |
9934 | 0 | } |
9935 | | |
9936 | | const xpath_lexer_string& contents() const |
9937 | 0 | { |
9938 | 0 | assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); |
9939 | | |
9940 | 0 | return _cur_lexeme_contents; |
9941 | 0 | } |
9942 | | }; |
9943 | | |
9944 | | enum ast_type_t |
9945 | | { |
9946 | | ast_unknown, |
9947 | | ast_op_or, // left or right |
9948 | | ast_op_and, // left and right |
9949 | | ast_op_equal, // left = right |
9950 | | ast_op_not_equal, // left != right |
9951 | | ast_op_less, // left < right |
9952 | | ast_op_greater, // left > right |
9953 | | ast_op_less_or_equal, // left <= right |
9954 | | ast_op_greater_or_equal, // left >= right |
9955 | | ast_op_add, // left + right |
9956 | | ast_op_subtract, // left - right |
9957 | | ast_op_multiply, // left * right |
9958 | | ast_op_divide, // left / right |
9959 | | ast_op_mod, // left % right |
9960 | | ast_op_negate, // left - right |
9961 | | ast_op_union, // left | right |
9962 | | ast_predicate, // apply predicate to set; next points to next predicate |
9963 | | ast_filter, // select * from left where right |
9964 | | ast_string_constant, // string constant |
9965 | | ast_number_constant, // number constant |
9966 | | ast_variable, // variable |
9967 | | ast_func_last, // last() |
9968 | | ast_func_position, // position() |
9969 | | ast_func_count, // count(left) |
9970 | | ast_func_id, // id(left) |
9971 | | ast_func_local_name_0, // local-name() |
9972 | | ast_func_local_name_1, // local-name(left) |
9973 | | ast_func_namespace_uri_0, // namespace-uri() |
9974 | | ast_func_namespace_uri_1, // namespace-uri(left) |
9975 | | ast_func_name_0, // name() |
9976 | | ast_func_name_1, // name(left) |
9977 | | ast_func_string_0, // string() |
9978 | | ast_func_string_1, // string(left) |
9979 | | ast_func_concat, // concat(left, right, siblings) |
9980 | | ast_func_starts_with, // starts_with(left, right) |
9981 | | ast_func_contains, // contains(left, right) |
9982 | | ast_func_substring_before, // substring-before(left, right) |
9983 | | ast_func_substring_after, // substring-after(left, right) |
9984 | | ast_func_substring_2, // substring(left, right) |
9985 | | ast_func_substring_3, // substring(left, right, third) |
9986 | | ast_func_string_length_0, // string-length() |
9987 | | ast_func_string_length_1, // string-length(left) |
9988 | | ast_func_normalize_space_0, // normalize-space() |
9989 | | ast_func_normalize_space_1, // normalize-space(left) |
9990 | | ast_func_translate, // translate(left, right, third) |
9991 | | ast_func_boolean, // boolean(left) |
9992 | | ast_func_not, // not(left) |
9993 | | ast_func_true, // true() |
9994 | | ast_func_false, // false() |
9995 | | ast_func_lang, // lang(left) |
9996 | | ast_func_number_0, // number() |
9997 | | ast_func_number_1, // number(left) |
9998 | | ast_func_sum, // sum(left) |
9999 | | ast_func_floor, // floor(left) |
10000 | | ast_func_ceiling, // ceiling(left) |
10001 | | ast_func_round, // round(left) |
10002 | | ast_step, // process set left with step |
10003 | | ast_step_root, // select root node |
10004 | | |
10005 | | ast_opt_translate_table, // translate(left, right, third) where right/third are constants |
10006 | | ast_opt_compare_attribute // @name = 'string' |
10007 | | }; |
10008 | | |
10009 | | enum axis_t |
10010 | | { |
10011 | | axis_ancestor, |
10012 | | axis_ancestor_or_self, |
10013 | | axis_attribute, |
10014 | | axis_child, |
10015 | | axis_descendant, |
10016 | | axis_descendant_or_self, |
10017 | | axis_following, |
10018 | | axis_following_sibling, |
10019 | | axis_namespace, |
10020 | | axis_parent, |
10021 | | axis_preceding, |
10022 | | axis_preceding_sibling, |
10023 | | axis_self |
10024 | | }; |
10025 | | |
10026 | | enum nodetest_t |
10027 | | { |
10028 | | nodetest_none, |
10029 | | nodetest_name, |
10030 | | nodetest_type_node, |
10031 | | nodetest_type_comment, |
10032 | | nodetest_type_pi, |
10033 | | nodetest_type_text, |
10034 | | nodetest_pi, |
10035 | | nodetest_all, |
10036 | | nodetest_all_in_namespace |
10037 | | }; |
10038 | | |
10039 | | enum predicate_t |
10040 | | { |
10041 | | predicate_default, |
10042 | | predicate_posinv, |
10043 | | predicate_constant, |
10044 | | predicate_constant_one |
10045 | | }; |
10046 | | |
10047 | | enum nodeset_eval_t |
10048 | | { |
10049 | | nodeset_eval_all, |
10050 | | nodeset_eval_any, |
10051 | | nodeset_eval_first |
10052 | | }; |
10053 | | |
10054 | | template <axis_t N> struct axis_to_type |
10055 | | { |
10056 | | static const axis_t axis; |
10057 | | }; |
10058 | | |
10059 | | template <axis_t N> const axis_t axis_to_type<N>::axis = N; |
10060 | | |
10061 | | class xpath_ast_node |
10062 | | { |
10063 | | private: |
10064 | | // node type |
10065 | | char _type; |
10066 | | char _rettype; |
10067 | | |
10068 | | // for ast_step |
10069 | | char _axis; |
10070 | | |
10071 | | // for ast_step/ast_predicate/ast_filter |
10072 | | char _test; |
10073 | | |
10074 | | // tree node structure |
10075 | | xpath_ast_node* _left; |
10076 | | xpath_ast_node* _right; |
10077 | | xpath_ast_node* _next; |
10078 | | |
10079 | | union |
10080 | | { |
10081 | | // value for ast_string_constant |
10082 | | const char_t* string; |
10083 | | // value for ast_number_constant |
10084 | | double number; |
10085 | | // variable for ast_variable |
10086 | | xpath_variable* variable; |
10087 | | // node test for ast_step (node name/namespace/node type/pi target) |
10088 | | const char_t* nodetest; |
10089 | | // table for ast_opt_translate_table |
10090 | | const unsigned char* table; |
10091 | | } _data; |
10092 | | |
10093 | | xpath_ast_node(const xpath_ast_node&); |
10094 | | xpath_ast_node& operator=(const xpath_ast_node&); |
10095 | | |
10096 | | template <class Comp> static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) |
10097 | 0 | { |
10098 | 0 | xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); |
10099 | |
|
10100 | 0 | if (lt != xpath_type_node_set && rt != xpath_type_node_set) |
10101 | 0 | { |
10102 | 0 | if (lt == xpath_type_boolean || rt == xpath_type_boolean) |
10103 | 0 | return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); |
10104 | 0 | else if (lt == xpath_type_number || rt == xpath_type_number) |
10105 | 0 | return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); |
10106 | 0 | else if (lt == xpath_type_string || rt == xpath_type_string) |
10107 | 0 | { |
10108 | 0 | xpath_allocator_capture cr(stack.result); |
10109 | |
|
10110 | 0 | xpath_string ls = lhs->eval_string(c, stack); |
10111 | 0 | xpath_string rs = rhs->eval_string(c, stack); |
10112 | |
|
10113 | 0 | return comp(ls, rs); |
10114 | 0 | } |
10115 | 0 | } |
10116 | 0 | else if (lt == xpath_type_node_set && rt == xpath_type_node_set) |
10117 | 0 | { |
10118 | 0 | xpath_allocator_capture cr(stack.result); |
10119 | |
|
10120 | 0 | xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); |
10121 | 0 | xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); |
10122 | |
|
10123 | 0 | for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) |
10124 | 0 | for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) |
10125 | 0 | { |
10126 | 0 | xpath_allocator_capture cri(stack.result); |
10127 | |
|
10128 | 0 | if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) |
10129 | 0 | return true; |
10130 | 0 | } |
10131 | | |
10132 | 0 | return false; |
10133 | 0 | } |
10134 | 0 | else |
10135 | 0 | { |
10136 | 0 | if (lt == xpath_type_node_set) |
10137 | 0 | { |
10138 | 0 | swap(lhs, rhs); |
10139 | 0 | swap(lt, rt); |
10140 | 0 | } |
10141 | |
|
10142 | 0 | if (lt == xpath_type_boolean) |
10143 | 0 | return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); |
10144 | 0 | else if (lt == xpath_type_number) |
10145 | 0 | { |
10146 | 0 | xpath_allocator_capture cr(stack.result); |
10147 | |
|
10148 | 0 | double l = lhs->eval_number(c, stack); |
10149 | 0 | xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); |
10150 | |
|
10151 | 0 | for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) |
10152 | 0 | { |
10153 | 0 | xpath_allocator_capture cri(stack.result); |
10154 | |
|
10155 | 0 | if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) |
10156 | 0 | return true; |
10157 | 0 | } |
10158 | | |
10159 | 0 | return false; |
10160 | 0 | } |
10161 | 0 | else if (lt == xpath_type_string) |
10162 | 0 | { |
10163 | 0 | xpath_allocator_capture cr(stack.result); |
10164 | |
|
10165 | 0 | xpath_string l = lhs->eval_string(c, stack); |
10166 | 0 | xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); |
10167 | |
|
10168 | 0 | for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) |
10169 | 0 | { |
10170 | 0 | xpath_allocator_capture cri(stack.result); |
10171 | |
|
10172 | 0 | if (comp(l, string_value(*ri, stack.result))) |
10173 | 0 | return true; |
10174 | 0 | } |
10175 | | |
10176 | 0 | return false; |
10177 | 0 | } |
10178 | 0 | } |
10179 | | |
10180 | 0 | assert(false && "Wrong types"); // unreachable |
10181 | 0 | return false; |
10182 | 0 | } Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::xpath_ast_node::compare_eq<pugi::impl::(anonymous namespace)::equal_to>(pugi::impl::(anonymous namespace)::xpath_ast_node*, pugi::impl::(anonymous namespace)::xpath_ast_node*, pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::equal_to const&) Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::xpath_ast_node::compare_eq<pugi::impl::(anonymous namespace)::not_equal_to>(pugi::impl::(anonymous namespace)::xpath_ast_node*, pugi::impl::(anonymous namespace)::xpath_ast_node*, pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::not_equal_to const&) |
10183 | | |
10184 | | static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval) |
10185 | 0 | { |
10186 | 0 | return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any; |
10187 | 0 | } |
10188 | | |
10189 | | template <class Comp> static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) |
10190 | 0 | { |
10191 | 0 | xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); |
10192 | |
|
10193 | 0 | if (lt != xpath_type_node_set && rt != xpath_type_node_set) |
10194 | 0 | return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); |
10195 | 0 | else if (lt == xpath_type_node_set && rt == xpath_type_node_set) |
10196 | 0 | { |
10197 | 0 | xpath_allocator_capture cr(stack.result); |
10198 | |
|
10199 | 0 | xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); |
10200 | 0 | xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); |
10201 | |
|
10202 | 0 | for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) |
10203 | 0 | { |
10204 | 0 | xpath_allocator_capture cri(stack.result); |
10205 | |
|
10206 | 0 | double l = convert_string_to_number(string_value(*li, stack.result).c_str()); |
10207 | |
|
10208 | 0 | for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) |
10209 | 0 | { |
10210 | 0 | xpath_allocator_capture crii(stack.result); |
10211 | |
|
10212 | 0 | if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) |
10213 | 0 | return true; |
10214 | 0 | } |
10215 | 0 | } |
10216 | | |
10217 | 0 | return false; |
10218 | 0 | } |
10219 | 0 | else if (lt != xpath_type_node_set && rt == xpath_type_node_set) |
10220 | 0 | { |
10221 | 0 | xpath_allocator_capture cr(stack.result); |
10222 | |
|
10223 | 0 | double l = lhs->eval_number(c, stack); |
10224 | 0 | xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); |
10225 | |
|
10226 | 0 | for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) |
10227 | 0 | { |
10228 | 0 | xpath_allocator_capture cri(stack.result); |
10229 | |
|
10230 | 0 | if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) |
10231 | 0 | return true; |
10232 | 0 | } |
10233 | | |
10234 | 0 | return false; |
10235 | 0 | } |
10236 | 0 | else if (lt == xpath_type_node_set && rt != xpath_type_node_set) |
10237 | 0 | { |
10238 | 0 | xpath_allocator_capture cr(stack.result); |
10239 | |
|
10240 | 0 | xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); |
10241 | 0 | double r = rhs->eval_number(c, stack); |
10242 | |
|
10243 | 0 | for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) |
10244 | 0 | { |
10245 | 0 | xpath_allocator_capture cri(stack.result); |
10246 | |
|
10247 | 0 | if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) |
10248 | 0 | return true; |
10249 | 0 | } |
10250 | | |
10251 | 0 | return false; |
10252 | 0 | } |
10253 | 0 | else |
10254 | 0 | { |
10255 | 0 | assert(false && "Wrong types"); // unreachable |
10256 | 0 | return false; |
10257 | 0 | } |
10258 | 0 | } Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::xpath_ast_node::compare_rel<pugi::impl::(anonymous namespace)::less>(pugi::impl::(anonymous namespace)::xpath_ast_node*, pugi::impl::(anonymous namespace)::xpath_ast_node*, pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::less const&) Unexecuted instantiation: pugixml.cpp:bool pugi::impl::(anonymous namespace)::xpath_ast_node::compare_rel<pugi::impl::(anonymous namespace)::less_equal>(pugi::impl::(anonymous namespace)::xpath_ast_node*, pugi::impl::(anonymous namespace)::xpath_ast_node*, pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::less_equal const&) |
10259 | | |
10260 | | static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) |
10261 | 0 | { |
10262 | 0 | assert(ns.size() >= first); |
10263 | 0 | assert(expr->rettype() != xpath_type_number); |
10264 | | |
10265 | 0 | size_t i = 1; |
10266 | 0 | size_t size = ns.size() - first; |
10267 | |
|
10268 | 0 | xpath_node* last = ns.begin() + first; |
10269 | | |
10270 | | // remove_if... or well, sort of |
10271 | 0 | for (xpath_node* it = last; it != ns.end(); ++it, ++i) |
10272 | 0 | { |
10273 | 0 | xpath_context c(*it, i, size); |
10274 | |
|
10275 | 0 | if (expr->eval_boolean(c, stack)) |
10276 | 0 | { |
10277 | 0 | *last++ = *it; |
10278 | |
|
10279 | 0 | if (once) break; |
10280 | 0 | } |
10281 | 0 | } |
10282 | |
|
10283 | 0 | ns.truncate(last); |
10284 | 0 | } |
10285 | | |
10286 | | static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) |
10287 | 0 | { |
10288 | 0 | assert(ns.size() >= first); |
10289 | 0 | assert(expr->rettype() == xpath_type_number); |
10290 | | |
10291 | 0 | size_t i = 1; |
10292 | 0 | size_t size = ns.size() - first; |
10293 | |
|
10294 | 0 | xpath_node* last = ns.begin() + first; |
10295 | | |
10296 | | // remove_if... or well, sort of |
10297 | 0 | for (xpath_node* it = last; it != ns.end(); ++it, ++i) |
10298 | 0 | { |
10299 | 0 | xpath_context c(*it, i, size); |
10300 | |
|
10301 | 0 | if (expr->eval_number(c, stack) == static_cast<double>(i)) |
10302 | 0 | { |
10303 | 0 | *last++ = *it; |
10304 | |
|
10305 | 0 | if (once) break; |
10306 | 0 | } |
10307 | 0 | } |
10308 | |
|
10309 | 0 | ns.truncate(last); |
10310 | 0 | } |
10311 | | |
10312 | | static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) |
10313 | 0 | { |
10314 | 0 | assert(ns.size() >= first); |
10315 | 0 | assert(expr->rettype() == xpath_type_number); |
10316 | | |
10317 | 0 | size_t size = ns.size() - first; |
10318 | |
|
10319 | 0 | xpath_node* last = ns.begin() + first; |
10320 | |
|
10321 | 0 | xpath_node cn; |
10322 | 0 | xpath_context c(cn, 1, size); |
10323 | |
|
10324 | 0 | double er = expr->eval_number(c, stack); |
10325 | |
|
10326 | 0 | if (er >= 1.0 && er <= static_cast<double>(size)) |
10327 | 0 | { |
10328 | 0 | size_t eri = static_cast<size_t>(er); |
10329 | |
|
10330 | 0 | if (er == static_cast<double>(eri)) |
10331 | 0 | { |
10332 | 0 | xpath_node r = last[eri - 1]; |
10333 | |
|
10334 | 0 | *last++ = r; |
10335 | 0 | } |
10336 | 0 | } |
10337 | |
|
10338 | 0 | ns.truncate(last); |
10339 | 0 | } |
10340 | | |
10341 | | void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once) |
10342 | 0 | { |
10343 | 0 | if (ns.size() == first) return; |
10344 | | |
10345 | 0 | assert(_type == ast_filter || _type == ast_predicate); |
10346 | | |
10347 | 0 | if (_test == predicate_constant || _test == predicate_constant_one) |
10348 | 0 | apply_predicate_number_const(ns, first, _right, stack); |
10349 | 0 | else if (_right->rettype() == xpath_type_number) |
10350 | 0 | apply_predicate_number(ns, first, _right, stack, once); |
10351 | 0 | else |
10352 | 0 | apply_predicate_boolean(ns, first, _right, stack, once); |
10353 | 0 | } |
10354 | | |
10355 | | void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) |
10356 | 0 | { |
10357 | 0 | if (ns.size() == first) return; |
10358 | | |
10359 | 0 | bool last_once = eval_once(ns.type(), eval); |
10360 | |
|
10361 | 0 | for (xpath_ast_node* pred = _right; pred; pred = pred->_next) |
10362 | 0 | pred->apply_predicate(ns, first, stack, !pred->_next && last_once); |
10363 | 0 | } |
10364 | | |
10365 | | bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) |
10366 | 0 | { |
10367 | 0 | assert(a); |
10368 | | |
10369 | 0 | const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT(""); |
10370 | |
|
10371 | 0 | switch (_test) |
10372 | 0 | { |
10373 | 0 | case nodetest_name: |
10374 | 0 | if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) |
10375 | 0 | { |
10376 | 0 | ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); |
10377 | 0 | return true; |
10378 | 0 | } |
10379 | 0 | break; |
10380 | | |
10381 | 0 | case nodetest_type_node: |
10382 | 0 | case nodetest_all: |
10383 | 0 | if (is_xpath_attribute(name)) |
10384 | 0 | { |
10385 | 0 | ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); |
10386 | 0 | return true; |
10387 | 0 | } |
10388 | 0 | break; |
10389 | | |
10390 | 0 | case nodetest_all_in_namespace: |
10391 | 0 | if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) |
10392 | 0 | { |
10393 | 0 | ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); |
10394 | 0 | return true; |
10395 | 0 | } |
10396 | 0 | break; |
10397 | | |
10398 | 0 | default: |
10399 | 0 | ; |
10400 | 0 | } |
10401 | | |
10402 | 0 | return false; |
10403 | 0 | } |
10404 | | |
10405 | | bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) |
10406 | 0 | { |
10407 | 0 | assert(n); |
10408 | | |
10409 | 0 | xml_node_type type = PUGI_IMPL_NODETYPE(n); |
10410 | |
|
10411 | 0 | switch (_test) |
10412 | 0 | { |
10413 | 0 | case nodetest_name: |
10414 | 0 | if (type == node_element && n->name && strequal(n->name, _data.nodetest)) |
10415 | 0 | { |
10416 | 0 | ns.push_back(xml_node(n), alloc); |
10417 | 0 | return true; |
10418 | 0 | } |
10419 | 0 | break; |
10420 | | |
10421 | 0 | case nodetest_type_node: |
10422 | 0 | ns.push_back(xml_node(n), alloc); |
10423 | 0 | return true; |
10424 | | |
10425 | 0 | case nodetest_type_comment: |
10426 | 0 | if (type == node_comment) |
10427 | 0 | { |
10428 | 0 | ns.push_back(xml_node(n), alloc); |
10429 | 0 | return true; |
10430 | 0 | } |
10431 | 0 | break; |
10432 | | |
10433 | 0 | case nodetest_type_text: |
10434 | 0 | if (type == node_pcdata || type == node_cdata) |
10435 | 0 | { |
10436 | 0 | ns.push_back(xml_node(n), alloc); |
10437 | 0 | return true; |
10438 | 0 | } |
10439 | 0 | break; |
10440 | | |
10441 | 0 | case nodetest_type_pi: |
10442 | 0 | if (type == node_pi) |
10443 | 0 | { |
10444 | 0 | ns.push_back(xml_node(n), alloc); |
10445 | 0 | return true; |
10446 | 0 | } |
10447 | 0 | break; |
10448 | | |
10449 | 0 | case nodetest_pi: |
10450 | 0 | if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) |
10451 | 0 | { |
10452 | 0 | ns.push_back(xml_node(n), alloc); |
10453 | 0 | return true; |
10454 | 0 | } |
10455 | 0 | break; |
10456 | | |
10457 | 0 | case nodetest_all: |
10458 | 0 | if (type == node_element) |
10459 | 0 | { |
10460 | 0 | ns.push_back(xml_node(n), alloc); |
10461 | 0 | return true; |
10462 | 0 | } |
10463 | 0 | break; |
10464 | | |
10465 | 0 | case nodetest_all_in_namespace: |
10466 | 0 | if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) |
10467 | 0 | { |
10468 | 0 | ns.push_back(xml_node(n), alloc); |
10469 | 0 | return true; |
10470 | 0 | } |
10471 | 0 | break; |
10472 | | |
10473 | 0 | default: |
10474 | 0 | assert(false && "Unknown axis"); // unreachable |
10475 | 0 | } |
10476 | | |
10477 | 0 | return false; |
10478 | 0 | } |
10479 | | |
10480 | | template <class T> void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T) |
10481 | 0 | { |
10482 | 0 | const axis_t axis = T::axis; |
10483 | |
|
10484 | 0 | switch (axis) |
10485 | 0 | { |
10486 | 0 | case axis_attribute: |
10487 | 0 | { |
10488 | 0 | for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) |
10489 | 0 | if (step_push(ns, a, n, alloc) & once) |
10490 | 0 | return; |
10491 | | |
10492 | 0 | break; |
10493 | 0 | } |
10494 | | |
10495 | 0 | case axis_child: |
10496 | 0 | { |
10497 | 0 | for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) |
10498 | 0 | if (step_push(ns, c, alloc) & once) |
10499 | 0 | return; |
10500 | | |
10501 | 0 | break; |
10502 | 0 | } |
10503 | | |
10504 | 0 | case axis_descendant: |
10505 | 0 | case axis_descendant_or_self: |
10506 | 0 | { |
10507 | 0 | if (axis == axis_descendant_or_self) |
10508 | 0 | if (step_push(ns, n, alloc) & once) |
10509 | 0 | return; |
10510 | | |
10511 | 0 | xml_node_struct* cur = n->first_child; |
10512 | |
|
10513 | 0 | while (cur) |
10514 | 0 | { |
10515 | 0 | if (step_push(ns, cur, alloc) & once) |
10516 | 0 | return; |
10517 | | |
10518 | 0 | if (cur->first_child) |
10519 | 0 | cur = cur->first_child; |
10520 | 0 | else |
10521 | 0 | { |
10522 | 0 | while (!cur->next_sibling) |
10523 | 0 | { |
10524 | 0 | cur = cur->parent; |
10525 | |
|
10526 | 0 | if (cur == n) return; |
10527 | 0 | } |
10528 | | |
10529 | 0 | cur = cur->next_sibling; |
10530 | 0 | } |
10531 | 0 | } |
10532 | | |
10533 | 0 | break; |
10534 | 0 | } |
10535 | | |
10536 | 0 | case axis_following_sibling: |
10537 | 0 | { |
10538 | 0 | for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) |
10539 | 0 | if (step_push(ns, c, alloc) & once) |
10540 | 0 | return; |
10541 | | |
10542 | 0 | break; |
10543 | 0 | } |
10544 | | |
10545 | 0 | case axis_preceding_sibling: |
10546 | 0 | { |
10547 | 0 | for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) |
10548 | 0 | if (step_push(ns, c, alloc) & once) |
10549 | 0 | return; |
10550 | | |
10551 | 0 | break; |
10552 | 0 | } |
10553 | | |
10554 | 0 | case axis_following: |
10555 | 0 | { |
10556 | 0 | xml_node_struct* cur = n; |
10557 | | |
10558 | | // exit from this node so that we don't include descendants |
10559 | 0 | while (!cur->next_sibling) |
10560 | 0 | { |
10561 | 0 | cur = cur->parent; |
10562 | |
|
10563 | 0 | if (!cur) return; |
10564 | 0 | } |
10565 | | |
10566 | 0 | cur = cur->next_sibling; |
10567 | |
|
10568 | 0 | while (cur) |
10569 | 0 | { |
10570 | 0 | if (step_push(ns, cur, alloc) & once) |
10571 | 0 | return; |
10572 | | |
10573 | 0 | if (cur->first_child) |
10574 | 0 | cur = cur->first_child; |
10575 | 0 | else |
10576 | 0 | { |
10577 | 0 | while (!cur->next_sibling) |
10578 | 0 | { |
10579 | 0 | cur = cur->parent; |
10580 | |
|
10581 | 0 | if (!cur) return; |
10582 | 0 | } |
10583 | | |
10584 | 0 | cur = cur->next_sibling; |
10585 | 0 | } |
10586 | 0 | } |
10587 | | |
10588 | 0 | break; |
10589 | 0 | } |
10590 | | |
10591 | 0 | case axis_preceding: |
10592 | 0 | { |
10593 | 0 | xml_node_struct* cur = n; |
10594 | | |
10595 | | // exit from this node so that we don't include descendants |
10596 | 0 | while (!cur->prev_sibling_c->next_sibling) |
10597 | 0 | { |
10598 | 0 | cur = cur->parent; |
10599 | |
|
10600 | 0 | if (!cur) return; |
10601 | 0 | } |
10602 | | |
10603 | 0 | cur = cur->prev_sibling_c; |
10604 | |
|
10605 | 0 | while (cur) |
10606 | 0 | { |
10607 | 0 | if (cur->first_child) |
10608 | 0 | cur = cur->first_child->prev_sibling_c; |
10609 | 0 | else |
10610 | 0 | { |
10611 | | // leaf node, can't be ancestor |
10612 | 0 | if (step_push(ns, cur, alloc) & once) |
10613 | 0 | return; |
10614 | | |
10615 | 0 | while (!cur->prev_sibling_c->next_sibling) |
10616 | 0 | { |
10617 | 0 | cur = cur->parent; |
10618 | |
|
10619 | 0 | if (!cur) return; |
10620 | | |
10621 | 0 | if (!node_is_ancestor(cur, n)) |
10622 | 0 | if (step_push(ns, cur, alloc) & once) |
10623 | 0 | return; |
10624 | 0 | } |
10625 | | |
10626 | 0 | cur = cur->prev_sibling_c; |
10627 | 0 | } |
10628 | 0 | } |
10629 | | |
10630 | 0 | break; |
10631 | 0 | } |
10632 | | |
10633 | 0 | case axis_ancestor: |
10634 | 0 | case axis_ancestor_or_self: |
10635 | 0 | { |
10636 | 0 | if (axis == axis_ancestor_or_self) |
10637 | 0 | if (step_push(ns, n, alloc) & once) |
10638 | 0 | return; |
10639 | | |
10640 | 0 | xml_node_struct* cur = n->parent; |
10641 | |
|
10642 | 0 | while (cur) |
10643 | 0 | { |
10644 | 0 | if (step_push(ns, cur, alloc) & once) |
10645 | 0 | return; |
10646 | | |
10647 | 0 | cur = cur->parent; |
10648 | 0 | } |
10649 | | |
10650 | 0 | break; |
10651 | 0 | } |
10652 | | |
10653 | 0 | case axis_self: |
10654 | 0 | { |
10655 | 0 | step_push(ns, n, alloc); |
10656 | |
|
10657 | 0 | break; |
10658 | 0 | } |
10659 | | |
10660 | 0 | case axis_parent: |
10661 | 0 | { |
10662 | 0 | if (n->parent) |
10663 | 0 | step_push(ns, n->parent, alloc); |
10664 | |
|
10665 | 0 | break; |
10666 | 0 | } |
10667 | | |
10668 | 0 | default: |
10669 | 0 | assert(false && "Unimplemented axis"); // unreachable |
10670 | 0 | } |
10671 | 0 | } Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)0> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)0>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)1> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)1>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)2> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)2>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)3> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)3>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)4> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)4>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)5> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)5>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)6> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)6>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)7> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)7>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)9> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)9>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)10> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)10>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)11> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)11>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)12> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)12>) |
10672 | | |
10673 | | template <class T> void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) |
10674 | 0 | { |
10675 | 0 | const axis_t axis = T::axis; |
10676 | |
|
10677 | 0 | switch (axis) |
10678 | 0 | { |
10679 | 0 | case axis_ancestor: |
10680 | 0 | case axis_ancestor_or_self: |
10681 | 0 | { |
10682 | 0 | if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test |
10683 | 0 | if (step_push(ns, a, p, alloc) & once) |
10684 | 0 | return; |
10685 | | |
10686 | 0 | xml_node_struct* cur = p; |
10687 | |
|
10688 | 0 | while (cur) |
10689 | 0 | { |
10690 | 0 | if (step_push(ns, cur, alloc) & once) |
10691 | 0 | return; |
10692 | | |
10693 | 0 | cur = cur->parent; |
10694 | 0 | } |
10695 | | |
10696 | 0 | break; |
10697 | 0 | } |
10698 | | |
10699 | 0 | case axis_descendant_or_self: |
10700 | 0 | case axis_self: |
10701 | 0 | { |
10702 | 0 | if (_test == nodetest_type_node) // reject attributes based on principal node type test |
10703 | 0 | step_push(ns, a, p, alloc); |
10704 | |
|
10705 | 0 | break; |
10706 | 0 | } |
10707 | | |
10708 | 0 | case axis_following: |
10709 | 0 | { |
10710 | 0 | xml_node_struct* cur = p; |
10711 | |
|
10712 | 0 | while (cur) |
10713 | 0 | { |
10714 | 0 | if (cur->first_child) |
10715 | 0 | cur = cur->first_child; |
10716 | 0 | else |
10717 | 0 | { |
10718 | 0 | while (!cur->next_sibling) |
10719 | 0 | { |
10720 | 0 | cur = cur->parent; |
10721 | |
|
10722 | 0 | if (!cur) return; |
10723 | 0 | } |
10724 | | |
10725 | 0 | cur = cur->next_sibling; |
10726 | 0 | } |
10727 | | |
10728 | 0 | if (step_push(ns, cur, alloc) & once) |
10729 | 0 | return; |
10730 | 0 | } |
10731 | | |
10732 | 0 | break; |
10733 | 0 | } |
10734 | | |
10735 | 0 | case axis_parent: |
10736 | 0 | { |
10737 | 0 | step_push(ns, p, alloc); |
10738 | |
|
10739 | 0 | break; |
10740 | 0 | } |
10741 | | |
10742 | 0 | case axis_preceding: |
10743 | 0 | { |
10744 | | // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding |
10745 | 0 | step_fill(ns, p, alloc, once, v); |
10746 | 0 | break; |
10747 | 0 | } |
10748 | | |
10749 | 0 | default: |
10750 | 0 | assert(false && "Unimplemented axis"); // unreachable |
10751 | 0 | } |
10752 | 0 | } Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)0> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)0>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)1> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)1>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)5> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)5>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)6> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)6>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)9> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)9>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)10> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)10>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)12> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)12>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)2> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)2>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)3> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)3>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)4> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)4>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)7> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)7>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)11> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xml_attribute_struct*, pugi::xml_node_struct*, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)11>) |
10753 | | |
10754 | | template <class T> void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v) |
10755 | 0 | { |
10756 | 0 | const axis_t axis = T::axis; |
10757 | 0 | const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); |
10758 | |
|
10759 | 0 | if (xn.node()) |
10760 | 0 | step_fill(ns, xn.node().internal_object(), alloc, once, v); |
10761 | 0 | else if (axis_has_attributes && xn.attribute() && xn.parent()) |
10762 | 0 | step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v); |
10763 | 0 | } Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)0> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)0>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)1> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)1>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)2> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)2>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)3> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)3>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)4> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)4>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)5> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)5>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)6> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)6>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)7> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)7>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)9> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)9>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)10> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)10>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)11> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)11>) Unexecuted instantiation: pugixml.cpp:void pugi::impl::(anonymous namespace)::xpath_ast_node::step_fill<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)12> >(pugi::impl::(anonymous namespace)::xpath_node_set_raw&, pugi::xpath_node const&, pugi::impl::(anonymous namespace)::xpath_allocator*, bool, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)12>) |
10764 | | |
10765 | | template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) |
10766 | 0 | { |
10767 | 0 | const axis_t axis = T::axis; |
10768 | 0 | const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); |
10769 | 0 | const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; |
10770 | |
|
10771 | 0 | bool once = |
10772 | 0 | (axis == axis_attribute && _test == nodetest_name) || |
10773 | 0 | (!_right && eval_once(axis_type, eval)) || |
10774 | | // coverity[mixed_enums] |
10775 | 0 | (_right && !_right->_next && _right->_test == predicate_constant_one); |
10776 | |
|
10777 | 0 | xpath_node_set_raw ns; |
10778 | 0 | ns.set_type(axis_type); |
10779 | |
|
10780 | 0 | if (_left) |
10781 | 0 | { |
10782 | 0 | xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all); |
10783 | | |
10784 | | // self axis preserves the original order |
10785 | 0 | if (axis == axis_self) ns.set_type(s.type()); |
10786 | |
|
10787 | 0 | for (const xpath_node* it = s.begin(); it != s.end(); ++it) |
10788 | 0 | { |
10789 | 0 | size_t size = ns.size(); |
10790 | | |
10791 | | // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes |
10792 | 0 | if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); |
10793 | |
|
10794 | 0 | step_fill(ns, *it, stack.result, once, v); |
10795 | 0 | if (_right) apply_predicates(ns, size, stack, eval); |
10796 | 0 | } |
10797 | 0 | } |
10798 | 0 | else |
10799 | 0 | { |
10800 | 0 | step_fill(ns, c.n, stack.result, once, v); |
10801 | 0 | if (_right) apply_predicates(ns, 0, stack, eval); |
10802 | 0 | } |
10803 | | |
10804 | | // child, attribute and self axes always generate unique set of nodes |
10805 | | // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice |
10806 | 0 | if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) |
10807 | 0 | ns.remove_duplicates(stack.temp); |
10808 | |
|
10809 | 0 | return ns; |
10810 | 0 | } Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)0> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)0>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)1> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)1>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)3> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)3>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)4> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)4>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)5> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)5>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)6> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)6>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)7> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)7>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)9> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)9>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)10> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)10>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)11> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)11>) Unexecuted instantiation: pugixml.cpp:pugi::impl::(anonymous namespace)::xpath_node_set_raw pugi::impl::(anonymous namespace)::xpath_ast_node::step_do<pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)12> >(pugi::impl::(anonymous namespace)::xpath_context const&, pugi::impl::(anonymous namespace)::xpath_stack const&, pugi::impl::(anonymous namespace)::nodeset_eval_t, pugi::impl::(anonymous namespace)::axis_to_type<(pugi::impl::(anonymous namespace)::axis_t)12>) |
10811 | | |
10812 | | public: |
10813 | | xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): |
10814 | 0 | _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(NULL), _right(NULL), _next(NULL) |
10815 | 0 | { |
10816 | 0 | assert(type == ast_string_constant); |
10817 | 0 | _data.string = value; |
10818 | 0 | } |
10819 | | |
10820 | | xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): |
10821 | 0 | _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(NULL), _right(NULL), _next(NULL) |
10822 | 0 | { |
10823 | 0 | assert(type == ast_number_constant); |
10824 | 0 | _data.number = value; |
10825 | 0 | } |
10826 | | |
10827 | | xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): |
10828 | 0 | _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(NULL), _right(NULL), _next(NULL) |
10829 | 0 | { |
10830 | 0 | assert(type == ast_variable); |
10831 | 0 | _data.variable = value; |
10832 | 0 | } |
10833 | | |
10834 | | xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = NULL, xpath_ast_node* right = NULL): |
10835 | 0 | _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(NULL) |
10836 | 0 | { |
10837 | 0 | } |
10838 | | |
10839 | | xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): |
10840 | 0 | _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(NULL), _next(NULL) |
10841 | 0 | { |
10842 | 0 | assert(type == ast_step); |
10843 | 0 | _data.nodetest = contents; |
10844 | 0 | } |
10845 | | |
10846 | | xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): |
10847 | 0 | _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast<char>(test)), _left(left), _right(right), _next(NULL) |
10848 | 0 | { |
10849 | 0 | assert(type == ast_filter || type == ast_predicate); |
10850 | 0 | } |
10851 | | |
10852 | | void set_next(xpath_ast_node* value) |
10853 | 0 | { |
10854 | 0 | _next = value; |
10855 | 0 | } |
10856 | | |
10857 | | void set_right(xpath_ast_node* value) |
10858 | 0 | { |
10859 | 0 | _right = value; |
10860 | 0 | } |
10861 | | |
10862 | | bool eval_boolean(const xpath_context& c, const xpath_stack& stack) |
10863 | 0 | { |
10864 | 0 | switch (_type) |
10865 | 0 | { |
10866 | 0 | case ast_op_or: |
10867 | 0 | return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); |
10868 | | |
10869 | 0 | case ast_op_and: |
10870 | 0 | return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); |
10871 | | |
10872 | 0 | case ast_op_equal: |
10873 | 0 | return compare_eq(_left, _right, c, stack, equal_to()); |
10874 | | |
10875 | 0 | case ast_op_not_equal: |
10876 | 0 | return compare_eq(_left, _right, c, stack, not_equal_to()); |
10877 | | |
10878 | 0 | case ast_op_less: |
10879 | 0 | return compare_rel(_left, _right, c, stack, less()); |
10880 | | |
10881 | 0 | case ast_op_greater: |
10882 | 0 | return compare_rel(_right, _left, c, stack, less()); |
10883 | | |
10884 | 0 | case ast_op_less_or_equal: |
10885 | 0 | return compare_rel(_left, _right, c, stack, less_equal()); |
10886 | | |
10887 | 0 | case ast_op_greater_or_equal: |
10888 | 0 | return compare_rel(_right, _left, c, stack, less_equal()); |
10889 | | |
10890 | 0 | case ast_func_starts_with: |
10891 | 0 | { |
10892 | 0 | xpath_allocator_capture cr(stack.result); |
10893 | |
|
10894 | 0 | xpath_string lr = _left->eval_string(c, stack); |
10895 | 0 | xpath_string rr = _right->eval_string(c, stack); |
10896 | |
|
10897 | 0 | return starts_with(lr.c_str(), rr.c_str()); |
10898 | 0 | } |
10899 | | |
10900 | 0 | case ast_func_contains: |
10901 | 0 | { |
10902 | 0 | xpath_allocator_capture cr(stack.result); |
10903 | |
|
10904 | 0 | xpath_string lr = _left->eval_string(c, stack); |
10905 | 0 | xpath_string rr = _right->eval_string(c, stack); |
10906 | |
|
10907 | 0 | return find_substring(lr.c_str(), rr.c_str()) != NULL; |
10908 | 0 | } |
10909 | | |
10910 | 0 | case ast_func_boolean: |
10911 | 0 | return _left->eval_boolean(c, stack); |
10912 | | |
10913 | 0 | case ast_func_not: |
10914 | 0 | return !_left->eval_boolean(c, stack); |
10915 | | |
10916 | 0 | case ast_func_true: |
10917 | 0 | return true; |
10918 | | |
10919 | 0 | case ast_func_false: |
10920 | 0 | return false; |
10921 | | |
10922 | 0 | case ast_func_lang: |
10923 | 0 | { |
10924 | 0 | if (c.n.attribute()) return false; |
10925 | | |
10926 | 0 | xpath_allocator_capture cr(stack.result); |
10927 | |
|
10928 | 0 | xpath_string lang = _left->eval_string(c, stack); |
10929 | |
|
10930 | 0 | for (xml_node n = c.n.node(); n; n = n.parent()) |
10931 | 0 | { |
10932 | 0 | xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); |
10933 | |
|
10934 | 0 | if (a) |
10935 | 0 | { |
10936 | 0 | const char_t* value = a.value(); |
10937 | | |
10938 | | // strnicmp / strncasecmp is not portable |
10939 | 0 | for (const char_t* lit = lang.c_str(); *lit; ++lit) |
10940 | 0 | { |
10941 | 0 | if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; |
10942 | 0 | ++value; |
10943 | 0 | } |
10944 | | |
10945 | 0 | return *value == 0 || *value == '-'; |
10946 | 0 | } |
10947 | 0 | } |
10948 | | |
10949 | 0 | return false; |
10950 | 0 | } |
10951 | | |
10952 | 0 | case ast_opt_compare_attribute: |
10953 | 0 | { |
10954 | 0 | const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string(); |
10955 | |
|
10956 | 0 | xml_attribute attr = c.n.node().attribute(_left->_data.nodetest); |
10957 | |
|
10958 | 0 | return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name()); |
10959 | 0 | } |
10960 | | |
10961 | 0 | case ast_variable: |
10962 | 0 | { |
10963 | 0 | assert(_rettype == _data.variable->type()); |
10964 | | |
10965 | 0 | if (_rettype == xpath_type_boolean) |
10966 | 0 | return _data.variable->get_boolean(); |
10967 | | |
10968 | | // variable needs to be converted to the correct type, this is handled by the fallthrough block below |
10969 | 0 | break; |
10970 | 0 | } |
10971 | | |
10972 | 0 | default: |
10973 | 0 | ; |
10974 | 0 | } |
10975 | | |
10976 | | // none of the ast types that return the value directly matched, we need to perform type conversion |
10977 | 0 | switch (_rettype) |
10978 | 0 | { |
10979 | 0 | case xpath_type_number: |
10980 | 0 | return convert_number_to_boolean(eval_number(c, stack)); |
10981 | | |
10982 | 0 | case xpath_type_string: |
10983 | 0 | { |
10984 | 0 | xpath_allocator_capture cr(stack.result); |
10985 | |
|
10986 | 0 | return !eval_string(c, stack).empty(); |
10987 | 0 | } |
10988 | | |
10989 | 0 | case xpath_type_node_set: |
10990 | 0 | { |
10991 | 0 | xpath_allocator_capture cr(stack.result); |
10992 | |
|
10993 | 0 | return !eval_node_set(c, stack, nodeset_eval_any).empty(); |
10994 | 0 | } |
10995 | | |
10996 | 0 | default: |
10997 | 0 | assert(false && "Wrong expression for return type boolean"); // unreachable |
10998 | 0 | return false; |
10999 | 0 | } |
11000 | 0 | } |
11001 | | |
11002 | | double eval_number(const xpath_context& c, const xpath_stack& stack) |
11003 | 0 | { |
11004 | 0 | switch (_type) |
11005 | 0 | { |
11006 | 0 | case ast_op_add: |
11007 | 0 | return _left->eval_number(c, stack) + _right->eval_number(c, stack); |
11008 | | |
11009 | 0 | case ast_op_subtract: |
11010 | 0 | return _left->eval_number(c, stack) - _right->eval_number(c, stack); |
11011 | | |
11012 | 0 | case ast_op_multiply: |
11013 | 0 | return _left->eval_number(c, stack) * _right->eval_number(c, stack); |
11014 | | |
11015 | 0 | case ast_op_divide: |
11016 | 0 | return _left->eval_number(c, stack) / _right->eval_number(c, stack); |
11017 | | |
11018 | 0 | case ast_op_mod: |
11019 | 0 | return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); |
11020 | | |
11021 | 0 | case ast_op_negate: |
11022 | 0 | return -_left->eval_number(c, stack); |
11023 | | |
11024 | 0 | case ast_number_constant: |
11025 | 0 | return _data.number; |
11026 | | |
11027 | 0 | case ast_func_last: |
11028 | 0 | return static_cast<double>(c.size); |
11029 | | |
11030 | 0 | case ast_func_position: |
11031 | 0 | return static_cast<double>(c.position); |
11032 | | |
11033 | 0 | case ast_func_count: |
11034 | 0 | { |
11035 | 0 | xpath_allocator_capture cr(stack.result); |
11036 | |
|
11037 | 0 | return static_cast<double>(_left->eval_node_set(c, stack, nodeset_eval_all).size()); |
11038 | 0 | } |
11039 | | |
11040 | 0 | case ast_func_string_length_0: |
11041 | 0 | { |
11042 | 0 | xpath_allocator_capture cr(stack.result); |
11043 | |
|
11044 | 0 | return static_cast<double>(string_value(c.n, stack.result).length()); |
11045 | 0 | } |
11046 | | |
11047 | 0 | case ast_func_string_length_1: |
11048 | 0 | { |
11049 | 0 | xpath_allocator_capture cr(stack.result); |
11050 | |
|
11051 | 0 | return static_cast<double>(_left->eval_string(c, stack).length()); |
11052 | 0 | } |
11053 | | |
11054 | 0 | case ast_func_number_0: |
11055 | 0 | { |
11056 | 0 | xpath_allocator_capture cr(stack.result); |
11057 | |
|
11058 | 0 | return convert_string_to_number(string_value(c.n, stack.result).c_str()); |
11059 | 0 | } |
11060 | | |
11061 | 0 | case ast_func_number_1: |
11062 | 0 | return _left->eval_number(c, stack); |
11063 | | |
11064 | 0 | case ast_func_sum: |
11065 | 0 | { |
11066 | 0 | xpath_allocator_capture cr(stack.result); |
11067 | |
|
11068 | 0 | double r = 0; |
11069 | |
|
11070 | 0 | xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); |
11071 | |
|
11072 | 0 | for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) |
11073 | 0 | { |
11074 | 0 | xpath_allocator_capture cri(stack.result); |
11075 | |
|
11076 | 0 | r += convert_string_to_number(string_value(*it, stack.result).c_str()); |
11077 | 0 | } |
11078 | |
|
11079 | 0 | return r; |
11080 | 0 | } |
11081 | | |
11082 | 0 | case ast_func_floor: |
11083 | 0 | { |
11084 | 0 | double r = _left->eval_number(c, stack); |
11085 | |
|
11086 | 0 | return r == r ? floor(r) : r; |
11087 | 0 | } |
11088 | | |
11089 | 0 | case ast_func_ceiling: |
11090 | 0 | { |
11091 | 0 | double r = _left->eval_number(c, stack); |
11092 | |
|
11093 | 0 | return r == r ? ceil(r) : r; |
11094 | 0 | } |
11095 | | |
11096 | 0 | case ast_func_round: |
11097 | 0 | return round_nearest_nzero(_left->eval_number(c, stack)); |
11098 | | |
11099 | 0 | case ast_variable: |
11100 | 0 | { |
11101 | 0 | assert(_rettype == _data.variable->type()); |
11102 | | |
11103 | 0 | if (_rettype == xpath_type_number) |
11104 | 0 | return _data.variable->get_number(); |
11105 | | |
11106 | | // variable needs to be converted to the correct type, this is handled by the fallthrough block below |
11107 | 0 | break; |
11108 | 0 | } |
11109 | | |
11110 | 0 | default: |
11111 | 0 | ; |
11112 | 0 | } |
11113 | | |
11114 | | // none of the ast types that return the value directly matched, we need to perform type conversion |
11115 | 0 | switch (_rettype) |
11116 | 0 | { |
11117 | 0 | case xpath_type_boolean: |
11118 | 0 | return eval_boolean(c, stack) ? 1 : 0; |
11119 | | |
11120 | 0 | case xpath_type_string: |
11121 | 0 | case xpath_type_node_set: // implicit conversion to string |
11122 | 0 | { |
11123 | 0 | xpath_allocator_capture cr(stack.result); |
11124 | |
|
11125 | 0 | return convert_string_to_number(eval_string(c, stack).c_str()); |
11126 | 0 | } |
11127 | | |
11128 | 0 | default: |
11129 | 0 | assert(false && "Wrong expression for return type number"); // unreachable |
11130 | 0 | return 0; |
11131 | 0 | } |
11132 | 0 | } |
11133 | | |
11134 | | xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) |
11135 | 0 | { |
11136 | 0 | assert(_type == ast_func_concat); |
11137 | | |
11138 | 0 | xpath_allocator_capture ct(stack.temp); |
11139 | | |
11140 | | // count the string number |
11141 | 0 | size_t count = 1; |
11142 | 0 | for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; |
11143 | | |
11144 | | // allocate a buffer for temporary string objects |
11145 | 0 | xpath_string* buffer = static_cast<xpath_string*>(stack.temp->allocate(count * sizeof(xpath_string))); |
11146 | 0 | if (!buffer) return xpath_string(); |
11147 | | |
11148 | | // evaluate all strings to temporary stack |
11149 | 0 | xpath_stack swapped_stack = {stack.temp, stack.result}; |
11150 | |
|
11151 | 0 | buffer[0] = _left->eval_string(c, swapped_stack); |
11152 | |
|
11153 | 0 | size_t pos = 1; |
11154 | 0 | for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); |
11155 | 0 | assert(pos == count); |
11156 | | |
11157 | | // get total length |
11158 | 0 | size_t length = 0; |
11159 | 0 | for (size_t i = 0; i < count; ++i) length += buffer[i].length(); |
11160 | | |
11161 | | // create final string |
11162 | 0 | char_t* result = static_cast<char_t*>(stack.result->allocate((length + 1) * sizeof(char_t))); |
11163 | 0 | if (!result) return xpath_string(); |
11164 | | |
11165 | 0 | char_t* ri = result; |
11166 | |
|
11167 | 0 | for (size_t j = 0; j < count; ++j) |
11168 | 0 | for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) |
11169 | 0 | *ri++ = *bi; |
11170 | |
|
11171 | 0 | *ri = 0; |
11172 | |
|
11173 | 0 | return xpath_string::from_heap_preallocated(result, ri); |
11174 | 0 | } |
11175 | | |
11176 | | xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) |
11177 | 0 | { |
11178 | 0 | switch (_type) |
11179 | 0 | { |
11180 | 0 | case ast_string_constant: |
11181 | 0 | return xpath_string::from_const(_data.string); |
11182 | | |
11183 | 0 | case ast_func_local_name_0: |
11184 | 0 | { |
11185 | 0 | xpath_node na = c.n; |
11186 | |
|
11187 | 0 | return xpath_string::from_const(local_name(na)); |
11188 | 0 | } |
11189 | | |
11190 | 0 | case ast_func_local_name_1: |
11191 | 0 | { |
11192 | 0 | xpath_allocator_capture cr(stack.result); |
11193 | |
|
11194 | 0 | xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); |
11195 | 0 | xpath_node na = ns.first(); |
11196 | |
|
11197 | 0 | return xpath_string::from_const(local_name(na)); |
11198 | 0 | } |
11199 | | |
11200 | 0 | case ast_func_name_0: |
11201 | 0 | { |
11202 | 0 | xpath_node na = c.n; |
11203 | |
|
11204 | 0 | return xpath_string::from_const(qualified_name(na)); |
11205 | 0 | } |
11206 | | |
11207 | 0 | case ast_func_name_1: |
11208 | 0 | { |
11209 | 0 | xpath_allocator_capture cr(stack.result); |
11210 | |
|
11211 | 0 | xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); |
11212 | 0 | xpath_node na = ns.first(); |
11213 | |
|
11214 | 0 | return xpath_string::from_const(qualified_name(na)); |
11215 | 0 | } |
11216 | | |
11217 | 0 | case ast_func_namespace_uri_0: |
11218 | 0 | { |
11219 | 0 | xpath_node na = c.n; |
11220 | |
|
11221 | 0 | return xpath_string::from_const(namespace_uri(na)); |
11222 | 0 | } |
11223 | | |
11224 | 0 | case ast_func_namespace_uri_1: |
11225 | 0 | { |
11226 | 0 | xpath_allocator_capture cr(stack.result); |
11227 | |
|
11228 | 0 | xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); |
11229 | 0 | xpath_node na = ns.first(); |
11230 | |
|
11231 | 0 | return xpath_string::from_const(namespace_uri(na)); |
11232 | 0 | } |
11233 | | |
11234 | 0 | case ast_func_string_0: |
11235 | 0 | return string_value(c.n, stack.result); |
11236 | | |
11237 | 0 | case ast_func_string_1: |
11238 | 0 | return _left->eval_string(c, stack); |
11239 | | |
11240 | 0 | case ast_func_concat: |
11241 | 0 | return eval_string_concat(c, stack); |
11242 | | |
11243 | 0 | case ast_func_substring_before: |
11244 | 0 | { |
11245 | 0 | xpath_allocator_capture cr(stack.temp); |
11246 | |
|
11247 | 0 | xpath_stack swapped_stack = {stack.temp, stack.result}; |
11248 | |
|
11249 | 0 | xpath_string s = _left->eval_string(c, swapped_stack); |
11250 | 0 | xpath_string p = _right->eval_string(c, swapped_stack); |
11251 | |
|
11252 | 0 | const char_t* pos = find_substring(s.c_str(), p.c_str()); |
11253 | |
|
11254 | 0 | return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); |
11255 | 0 | } |
11256 | | |
11257 | 0 | case ast_func_substring_after: |
11258 | 0 | { |
11259 | 0 | xpath_allocator_capture cr(stack.temp); |
11260 | |
|
11261 | 0 | xpath_stack swapped_stack = {stack.temp, stack.result}; |
11262 | |
|
11263 | 0 | xpath_string s = _left->eval_string(c, swapped_stack); |
11264 | 0 | xpath_string p = _right->eval_string(c, swapped_stack); |
11265 | |
|
11266 | 0 | const char_t* pos = find_substring(s.c_str(), p.c_str()); |
11267 | 0 | if (!pos) return xpath_string(); |
11268 | | |
11269 | 0 | const char_t* rbegin = pos + p.length(); |
11270 | 0 | const char_t* rend = s.c_str() + s.length(); |
11271 | |
|
11272 | 0 | return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); |
11273 | 0 | } |
11274 | | |
11275 | 0 | case ast_func_substring_2: |
11276 | 0 | { |
11277 | 0 | xpath_allocator_capture cr(stack.temp); |
11278 | |
|
11279 | 0 | xpath_stack swapped_stack = {stack.temp, stack.result}; |
11280 | |
|
11281 | 0 | xpath_string s = _left->eval_string(c, swapped_stack); |
11282 | 0 | size_t s_length = s.length(); |
11283 | |
|
11284 | 0 | double first = round_nearest(_right->eval_number(c, stack)); |
11285 | |
|
11286 | 0 | if (is_nan(first)) return xpath_string(); // NaN |
11287 | 0 | else if (first >= static_cast<double>(s_length + 1)) return xpath_string(); |
11288 | | |
11289 | 0 | size_t pos = first < 1 ? 1 : static_cast<size_t>(first); |
11290 | 0 | assert(1 <= pos && pos <= s_length + 1); |
11291 | | |
11292 | 0 | const char_t* rbegin = s.c_str() + (pos - 1); |
11293 | 0 | const char_t* rend = s.c_str() + s.length(); |
11294 | |
|
11295 | 0 | return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); |
11296 | 0 | } |
11297 | | |
11298 | 0 | case ast_func_substring_3: |
11299 | 0 | { |
11300 | 0 | xpath_allocator_capture cr(stack.temp); |
11301 | |
|
11302 | 0 | xpath_stack swapped_stack = {stack.temp, stack.result}; |
11303 | |
|
11304 | 0 | xpath_string s = _left->eval_string(c, swapped_stack); |
11305 | 0 | size_t s_length = s.length(); |
11306 | |
|
11307 | 0 | double first = round_nearest(_right->eval_number(c, stack)); |
11308 | 0 | double last = first + round_nearest(_right->_next->eval_number(c, stack)); |
11309 | |
|
11310 | 0 | if (is_nan(first) || is_nan(last)) return xpath_string(); |
11311 | 0 | else if (first >= static_cast<double>(s_length + 1)) return xpath_string(); |
11312 | 0 | else if (first >= last) return xpath_string(); |
11313 | 0 | else if (last < 1) return xpath_string(); |
11314 | | |
11315 | 0 | size_t pos = first < 1 ? 1 : static_cast<size_t>(first); |
11316 | 0 | size_t end = last >= static_cast<double>(s_length + 1) ? s_length + 1 : static_cast<size_t>(last); |
11317 | |
|
11318 | 0 | assert(1 <= pos && pos <= end && end <= s_length + 1); |
11319 | 0 | const char_t* rbegin = s.c_str() + (pos - 1); |
11320 | 0 | const char_t* rend = s.c_str() + (end - 1); |
11321 | |
|
11322 | 0 | return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result); |
11323 | 0 | } |
11324 | | |
11325 | 0 | case ast_func_normalize_space_0: |
11326 | 0 | { |
11327 | 0 | xpath_string s = string_value(c.n, stack.result); |
11328 | |
|
11329 | 0 | char_t* begin = s.data(stack.result); |
11330 | 0 | if (!begin) return xpath_string(); |
11331 | | |
11332 | 0 | char_t* end = normalize_space(begin); |
11333 | |
|
11334 | 0 | return xpath_string::from_heap_preallocated(begin, end); |
11335 | 0 | } |
11336 | | |
11337 | 0 | case ast_func_normalize_space_1: |
11338 | 0 | { |
11339 | 0 | xpath_string s = _left->eval_string(c, stack); |
11340 | |
|
11341 | 0 | char_t* begin = s.data(stack.result); |
11342 | 0 | if (!begin) return xpath_string(); |
11343 | | |
11344 | 0 | char_t* end = normalize_space(begin); |
11345 | |
|
11346 | 0 | return xpath_string::from_heap_preallocated(begin, end); |
11347 | 0 | } |
11348 | | |
11349 | 0 | case ast_func_translate: |
11350 | 0 | { |
11351 | 0 | xpath_allocator_capture cr(stack.temp); |
11352 | |
|
11353 | 0 | xpath_stack swapped_stack = {stack.temp, stack.result}; |
11354 | |
|
11355 | 0 | xpath_string s = _left->eval_string(c, stack); |
11356 | 0 | xpath_string from = _right->eval_string(c, swapped_stack); |
11357 | 0 | xpath_string to = _right->_next->eval_string(c, swapped_stack); |
11358 | |
|
11359 | 0 | char_t* begin = s.data(stack.result); |
11360 | 0 | if (!begin) return xpath_string(); |
11361 | | |
11362 | 0 | char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); |
11363 | |
|
11364 | 0 | return xpath_string::from_heap_preallocated(begin, end); |
11365 | 0 | } |
11366 | | |
11367 | 0 | case ast_opt_translate_table: |
11368 | 0 | { |
11369 | 0 | xpath_string s = _left->eval_string(c, stack); |
11370 | |
|
11371 | 0 | char_t* begin = s.data(stack.result); |
11372 | 0 | if (!begin) return xpath_string(); |
11373 | | |
11374 | 0 | char_t* end = translate_table(begin, _data.table); |
11375 | |
|
11376 | 0 | return xpath_string::from_heap_preallocated(begin, end); |
11377 | 0 | } |
11378 | | |
11379 | 0 | case ast_variable: |
11380 | 0 | { |
11381 | 0 | assert(_rettype == _data.variable->type()); |
11382 | | |
11383 | 0 | if (_rettype == xpath_type_string) |
11384 | 0 | return xpath_string::from_const(_data.variable->get_string()); |
11385 | | |
11386 | | // variable needs to be converted to the correct type, this is handled by the fallthrough block below |
11387 | 0 | break; |
11388 | 0 | } |
11389 | | |
11390 | 0 | default: |
11391 | 0 | ; |
11392 | 0 | } |
11393 | | |
11394 | | // none of the ast types that return the value directly matched, we need to perform type conversion |
11395 | 0 | switch (_rettype) |
11396 | 0 | { |
11397 | 0 | case xpath_type_boolean: |
11398 | 0 | return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); |
11399 | | |
11400 | 0 | case xpath_type_number: |
11401 | 0 | return convert_number_to_string(eval_number(c, stack), stack.result); |
11402 | | |
11403 | 0 | case xpath_type_node_set: |
11404 | 0 | { |
11405 | 0 | xpath_allocator_capture cr(stack.temp); |
11406 | |
|
11407 | 0 | xpath_stack swapped_stack = {stack.temp, stack.result}; |
11408 | |
|
11409 | 0 | xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); |
11410 | 0 | return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); |
11411 | 0 | } |
11412 | | |
11413 | 0 | default: |
11414 | 0 | assert(false && "Wrong expression for return type string"); // unreachable |
11415 | 0 | return xpath_string(); |
11416 | 0 | } |
11417 | 0 | } |
11418 | | |
11419 | | xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval) |
11420 | 0 | { |
11421 | 0 | switch (_type) |
11422 | 0 | { |
11423 | 0 | case ast_op_union: |
11424 | 0 | { |
11425 | 0 | xpath_allocator_capture cr(stack.temp); |
11426 | |
|
11427 | 0 | xpath_stack swapped_stack = {stack.temp, stack.result}; |
11428 | |
|
11429 | 0 | xpath_node_set_raw ls = _left->eval_node_set(c, stack, eval); |
11430 | 0 | xpath_node_set_raw rs = _right->eval_node_set(c, swapped_stack, eval); |
11431 | | |
11432 | | // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother |
11433 | 0 | ls.set_type(xpath_node_set::type_unsorted); |
11434 | |
|
11435 | 0 | ls.append(rs.begin(), rs.end(), stack.result); |
11436 | 0 | ls.remove_duplicates(stack.temp); |
11437 | |
|
11438 | 0 | return ls; |
11439 | 0 | } |
11440 | | |
11441 | 0 | case ast_filter: |
11442 | 0 | { |
11443 | 0 | xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); |
11444 | | |
11445 | | // either expression is a number or it contains position() call; sort by document order |
11446 | 0 | if (_test != predicate_posinv) set.sort_do(); |
11447 | |
|
11448 | 0 | bool once = eval_once(set.type(), eval); |
11449 | |
|
11450 | 0 | apply_predicate(set, 0, stack, once); |
11451 | |
|
11452 | 0 | return set; |
11453 | 0 | } |
11454 | | |
11455 | 0 | case ast_func_id: |
11456 | 0 | return xpath_node_set_raw(); |
11457 | | |
11458 | 0 | case ast_step: |
11459 | 0 | { |
11460 | 0 | switch (_axis) |
11461 | 0 | { |
11462 | 0 | case axis_ancestor: |
11463 | 0 | return step_do(c, stack, eval, axis_to_type<axis_ancestor>()); |
11464 | | |
11465 | 0 | case axis_ancestor_or_self: |
11466 | 0 | return step_do(c, stack, eval, axis_to_type<axis_ancestor_or_self>()); |
11467 | | |
11468 | 0 | case axis_attribute: |
11469 | 0 | return step_do(c, stack, eval, axis_to_type<axis_attribute>()); |
11470 | | |
11471 | 0 | case axis_child: |
11472 | 0 | return step_do(c, stack, eval, axis_to_type<axis_child>()); |
11473 | | |
11474 | 0 | case axis_descendant: |
11475 | 0 | return step_do(c, stack, eval, axis_to_type<axis_descendant>()); |
11476 | | |
11477 | 0 | case axis_descendant_or_self: |
11478 | 0 | return step_do(c, stack, eval, axis_to_type<axis_descendant_or_self>()); |
11479 | | |
11480 | 0 | case axis_following: |
11481 | 0 | return step_do(c, stack, eval, axis_to_type<axis_following>()); |
11482 | | |
11483 | 0 | case axis_following_sibling: |
11484 | 0 | return step_do(c, stack, eval, axis_to_type<axis_following_sibling>()); |
11485 | | |
11486 | 0 | case axis_namespace: |
11487 | | // namespaced axis is not supported |
11488 | 0 | return xpath_node_set_raw(); |
11489 | | |
11490 | 0 | case axis_parent: |
11491 | 0 | return step_do(c, stack, eval, axis_to_type<axis_parent>()); |
11492 | | |
11493 | 0 | case axis_preceding: |
11494 | 0 | return step_do(c, stack, eval, axis_to_type<axis_preceding>()); |
11495 | | |
11496 | 0 | case axis_preceding_sibling: |
11497 | 0 | return step_do(c, stack, eval, axis_to_type<axis_preceding_sibling>()); |
11498 | | |
11499 | 0 | case axis_self: |
11500 | 0 | return step_do(c, stack, eval, axis_to_type<axis_self>()); |
11501 | | |
11502 | 0 | default: |
11503 | 0 | assert(false && "Unknown axis"); // unreachable |
11504 | 0 | return xpath_node_set_raw(); |
11505 | 0 | } |
11506 | 0 | } |
11507 | | |
11508 | 0 | case ast_step_root: |
11509 | 0 | { |
11510 | 0 | assert(!_right); // root step can't have any predicates |
11511 | | |
11512 | 0 | xpath_node_set_raw ns; |
11513 | |
|
11514 | 0 | ns.set_type(xpath_node_set::type_sorted); |
11515 | |
|
11516 | 0 | if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); |
11517 | 0 | else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); |
11518 | |
|
11519 | 0 | return ns; |
11520 | 0 | } |
11521 | | |
11522 | 0 | case ast_variable: |
11523 | 0 | { |
11524 | 0 | assert(_rettype == _data.variable->type()); |
11525 | | |
11526 | 0 | if (_rettype == xpath_type_node_set) |
11527 | 0 | { |
11528 | 0 | const xpath_node_set& s = _data.variable->get_node_set(); |
11529 | |
|
11530 | 0 | xpath_node_set_raw ns; |
11531 | |
|
11532 | 0 | ns.set_type(s.type()); |
11533 | 0 | ns.append(s.begin(), s.end(), stack.result); |
11534 | |
|
11535 | 0 | return ns; |
11536 | 0 | } |
11537 | | |
11538 | | // variable needs to be converted to the correct type, this is handled by the fallthrough block below |
11539 | 0 | break; |
11540 | 0 | } |
11541 | | |
11542 | 0 | default: |
11543 | 0 | ; |
11544 | 0 | } |
11545 | | |
11546 | | // none of the ast types that return the value directly matched, but conversions to node set are invalid |
11547 | 0 | assert(false && "Wrong expression for return type node set"); // unreachable |
11548 | 0 | return xpath_node_set_raw(); |
11549 | 0 | } |
11550 | | |
11551 | | void optimize(xpath_allocator* alloc) |
11552 | 0 | { |
11553 | 0 | if (_left) |
11554 | 0 | _left->optimize(alloc); |
11555 | |
|
11556 | 0 | if (_right) |
11557 | 0 | _right->optimize(alloc); |
11558 | |
|
11559 | 0 | if (_next) |
11560 | 0 | _next->optimize(alloc); |
11561 | | |
11562 | | // coverity[var_deref_model] |
11563 | 0 | optimize_self(alloc); |
11564 | 0 | } |
11565 | | |
11566 | | void optimize_self(xpath_allocator* alloc) |
11567 | 0 | { |
11568 | | // Rewrite [position()=expr] with [expr] |
11569 | | // Note that this step has to go before classification to recognize [position()=1] |
11570 | 0 | if ((_type == ast_filter || _type == ast_predicate) && |
11571 | 0 | _right && // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) |
11572 | 0 | _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) |
11573 | 0 | { |
11574 | 0 | _right = _right->_right; |
11575 | 0 | } |
11576 | | |
11577 | | // Classify filter/predicate ops to perform various optimizations during evaluation |
11578 | 0 | if ((_type == ast_filter || _type == ast_predicate) && _right) // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) |
11579 | 0 | { |
11580 | 0 | assert(_test == predicate_default); |
11581 | | |
11582 | 0 | if (_right->_type == ast_number_constant && _right->_data.number == 1.0) |
11583 | 0 | _test = predicate_constant_one; |
11584 | 0 | else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) |
11585 | 0 | _test = predicate_constant; |
11586 | 0 | else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) |
11587 | 0 | _test = predicate_posinv; |
11588 | 0 | } |
11589 | | |
11590 | | // Rewrite descendant-or-self::node()/child::foo with descendant::foo |
11591 | | // The former is a full form of //foo, the latter is much faster since it executes the node test immediately |
11592 | | // Do a similar kind of rewrite for self/descendant/descendant-or-self axes |
11593 | | // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) |
11594 | 0 | if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && |
11595 | 0 | _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && |
11596 | 0 | is_posinv_step()) |
11597 | 0 | { |
11598 | 0 | if (_axis == axis_child || _axis == axis_descendant) |
11599 | 0 | _axis = axis_descendant; |
11600 | 0 | else |
11601 | 0 | _axis = axis_descendant_or_self; |
11602 | |
|
11603 | 0 | _left = _left->_left; |
11604 | 0 | } |
11605 | | |
11606 | | // Use optimized lookup table implementation for translate() with constant arguments |
11607 | 0 | if (_type == ast_func_translate && |
11608 | 0 | _right && // workaround for clang static analyzer (_right is never null for ast_func_translate) |
11609 | 0 | _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) |
11610 | 0 | { |
11611 | 0 | unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); |
11612 | |
|
11613 | 0 | if (table) |
11614 | 0 | { |
11615 | 0 | _type = ast_opt_translate_table; |
11616 | 0 | _data.table = table; |
11617 | 0 | } |
11618 | 0 | } |
11619 | | |
11620 | | // Use optimized path for @attr = 'value' or @attr = $value |
11621 | 0 | if (_type == ast_op_equal && |
11622 | 0 | _left && _right && // workaround for clang static analyzer and Coverity (_left and _right are never null for ast_op_equal) |
11623 | | // coverity[mixed_enums] |
11624 | 0 | _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && |
11625 | 0 | (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) |
11626 | 0 | { |
11627 | 0 | _type = ast_opt_compare_attribute; |
11628 | 0 | } |
11629 | 0 | } |
11630 | | |
11631 | | bool is_posinv_expr() const |
11632 | 0 | { |
11633 | 0 | switch (_type) |
11634 | 0 | { |
11635 | 0 | case ast_func_position: |
11636 | 0 | case ast_func_last: |
11637 | 0 | return false; |
11638 | | |
11639 | 0 | case ast_string_constant: |
11640 | 0 | case ast_number_constant: |
11641 | 0 | case ast_variable: |
11642 | 0 | return true; |
11643 | | |
11644 | 0 | case ast_step: |
11645 | 0 | case ast_step_root: |
11646 | 0 | return true; |
11647 | | |
11648 | 0 | case ast_predicate: |
11649 | 0 | case ast_filter: |
11650 | 0 | return true; |
11651 | | |
11652 | 0 | default: |
11653 | 0 | if (_left && !_left->is_posinv_expr()) return false; |
11654 | | |
11655 | 0 | for (xpath_ast_node* n = _right; n; n = n->_next) |
11656 | 0 | if (!n->is_posinv_expr()) return false; |
11657 | | |
11658 | 0 | return true; |
11659 | 0 | } |
11660 | 0 | } |
11661 | | |
11662 | | bool is_posinv_step() const |
11663 | 0 | { |
11664 | 0 | assert(_type == ast_step); |
11665 | | |
11666 | 0 | for (xpath_ast_node* n = _right; n; n = n->_next) |
11667 | 0 | { |
11668 | 0 | assert(n->_type == ast_predicate); |
11669 | | |
11670 | 0 | if (n->_test != predicate_posinv) |
11671 | 0 | return false; |
11672 | 0 | } |
11673 | | |
11674 | 0 | return true; |
11675 | 0 | } |
11676 | | |
11677 | | xpath_value_type rettype() const |
11678 | 0 | { |
11679 | 0 | return static_cast<xpath_value_type>(_rettype); |
11680 | 0 | } |
11681 | | }; |
11682 | | |
11683 | | static const size_t xpath_ast_depth_limit = |
11684 | | #ifdef PUGIXML_XPATH_DEPTH_LIMIT |
11685 | | PUGIXML_XPATH_DEPTH_LIMIT |
11686 | | #else |
11687 | | 1024 |
11688 | | #endif |
11689 | | ; |
11690 | | |
11691 | | struct xpath_parser |
11692 | | { |
11693 | | xpath_allocator* _alloc; |
11694 | | xpath_lexer _lexer; |
11695 | | |
11696 | | const char_t* _query; |
11697 | | xpath_variable_set* _variables; |
11698 | | |
11699 | | xpath_parse_result* _result; |
11700 | | |
11701 | | char_t _scratch[32]; |
11702 | | |
11703 | | size_t _depth; |
11704 | | |
11705 | | xpath_ast_node* error(const char* message) |
11706 | 0 | { |
11707 | 0 | _result->error = message; |
11708 | 0 | _result->offset = _lexer.current_pos() - _query; |
11709 | |
|
11710 | 0 | return NULL; |
11711 | 0 | } |
11712 | | |
11713 | | xpath_ast_node* error_oom() |
11714 | 0 | { |
11715 | 0 | assert(_alloc->_error); |
11716 | 0 | *_alloc->_error = true; |
11717 | |
|
11718 | 0 | return NULL; |
11719 | 0 | } |
11720 | | |
11721 | | xpath_ast_node* error_rec() |
11722 | 0 | { |
11723 | 0 | return error("Exceeded maximum allowed query depth"); |
11724 | 0 | } |
11725 | | |
11726 | | void* alloc_node() |
11727 | 0 | { |
11728 | 0 | return _alloc->allocate(sizeof(xpath_ast_node)); |
11729 | 0 | } |
11730 | | |
11731 | | xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value) |
11732 | 0 | { |
11733 | 0 | void* memory = alloc_node(); |
11734 | 0 | return memory ? new (memory) xpath_ast_node(type, rettype, value) : NULL; |
11735 | 0 | } |
11736 | | |
11737 | | xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value) |
11738 | 0 | { |
11739 | 0 | void* memory = alloc_node(); |
11740 | 0 | return memory ? new (memory) xpath_ast_node(type, rettype, value) : NULL; |
11741 | 0 | } |
11742 | | |
11743 | | xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value) |
11744 | 0 | { |
11745 | 0 | void* memory = alloc_node(); |
11746 | 0 | return memory ? new (memory) xpath_ast_node(type, rettype, value) : NULL; |
11747 | 0 | } |
11748 | | |
11749 | | xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = NULL, xpath_ast_node* right = NULL) |
11750 | 0 | { |
11751 | 0 | void* memory = alloc_node(); |
11752 | 0 | return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : NULL; |
11753 | 0 | } |
11754 | | |
11755 | | xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents) |
11756 | 0 | { |
11757 | 0 | void* memory = alloc_node(); |
11758 | 0 | return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : NULL; |
11759 | 0 | } |
11760 | | |
11761 | | xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test) |
11762 | 0 | { |
11763 | 0 | void* memory = alloc_node(); |
11764 | 0 | return memory ? new (memory) xpath_ast_node(type, left, right, test) : NULL; |
11765 | 0 | } |
11766 | | |
11767 | | const char_t* alloc_string(const xpath_lexer_string& value) |
11768 | 0 | { |
11769 | 0 | if (!value.begin) |
11770 | 0 | return PUGIXML_TEXT(""); |
11771 | | |
11772 | 0 | size_t length = static_cast<size_t>(value.end - value.begin); |
11773 | |
|
11774 | 0 | char_t* c = static_cast<char_t*>(_alloc->allocate((length + 1) * sizeof(char_t))); |
11775 | 0 | if (!c) return NULL; |
11776 | | |
11777 | 0 | memcpy(c, value.begin, length * sizeof(char_t)); |
11778 | 0 | c[length] = 0; |
11779 | |
|
11780 | 0 | return c; |
11781 | 0 | } |
11782 | | |
11783 | | xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) |
11784 | 0 | { |
11785 | 0 | switch (name.begin[0]) |
11786 | 0 | { |
11787 | 0 | case 'b': |
11788 | 0 | if (name == PUGIXML_TEXT("boolean") && argc == 1) |
11789 | 0 | return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]); |
11790 | | |
11791 | 0 | break; |
11792 | | |
11793 | 0 | case 'c': |
11794 | 0 | if (name == PUGIXML_TEXT("count") && argc == 1) |
11795 | 0 | { |
11796 | 0 | if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); |
11797 | 0 | return alloc_node(ast_func_count, xpath_type_number, args[0]); |
11798 | 0 | } |
11799 | 0 | else if (name == PUGIXML_TEXT("contains") && argc == 2) |
11800 | 0 | return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); |
11801 | 0 | else if (name == PUGIXML_TEXT("concat") && argc >= 2) |
11802 | 0 | return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]); |
11803 | 0 | else if (name == PUGIXML_TEXT("ceiling") && argc == 1) |
11804 | 0 | return alloc_node(ast_func_ceiling, xpath_type_number, args[0]); |
11805 | | |
11806 | 0 | break; |
11807 | | |
11808 | 0 | case 'f': |
11809 | 0 | if (name == PUGIXML_TEXT("false") && argc == 0) |
11810 | 0 | return alloc_node(ast_func_false, xpath_type_boolean); |
11811 | 0 | else if (name == PUGIXML_TEXT("floor") && argc == 1) |
11812 | 0 | return alloc_node(ast_func_floor, xpath_type_number, args[0]); |
11813 | | |
11814 | 0 | break; |
11815 | | |
11816 | 0 | case 'i': |
11817 | 0 | if (name == PUGIXML_TEXT("id") && argc == 1) |
11818 | 0 | return alloc_node(ast_func_id, xpath_type_node_set, args[0]); |
11819 | | |
11820 | 0 | break; |
11821 | | |
11822 | 0 | case 'l': |
11823 | 0 | if (name == PUGIXML_TEXT("last") && argc == 0) |
11824 | 0 | return alloc_node(ast_func_last, xpath_type_number); |
11825 | 0 | else if (name == PUGIXML_TEXT("lang") && argc == 1) |
11826 | 0 | return alloc_node(ast_func_lang, xpath_type_boolean, args[0]); |
11827 | 0 | else if (name == PUGIXML_TEXT("local-name") && argc <= 1) |
11828 | 0 | { |
11829 | 0 | if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); |
11830 | 0 | return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]); |
11831 | 0 | } |
11832 | | |
11833 | 0 | break; |
11834 | | |
11835 | 0 | case 'n': |
11836 | 0 | if (name == PUGIXML_TEXT("name") && argc <= 1) |
11837 | 0 | { |
11838 | 0 | if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); |
11839 | 0 | return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]); |
11840 | 0 | } |
11841 | 0 | else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) |
11842 | 0 | { |
11843 | 0 | if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); |
11844 | 0 | return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]); |
11845 | 0 | } |
11846 | 0 | else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) |
11847 | 0 | return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); |
11848 | 0 | else if (name == PUGIXML_TEXT("not") && argc == 1) |
11849 | 0 | return alloc_node(ast_func_not, xpath_type_boolean, args[0]); |
11850 | 0 | else if (name == PUGIXML_TEXT("number") && argc <= 1) |
11851 | 0 | return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); |
11852 | | |
11853 | 0 | break; |
11854 | | |
11855 | 0 | case 'p': |
11856 | 0 | if (name == PUGIXML_TEXT("position") && argc == 0) |
11857 | 0 | return alloc_node(ast_func_position, xpath_type_number); |
11858 | | |
11859 | 0 | break; |
11860 | | |
11861 | 0 | case 'r': |
11862 | 0 | if (name == PUGIXML_TEXT("round") && argc == 1) |
11863 | 0 | return alloc_node(ast_func_round, xpath_type_number, args[0]); |
11864 | | |
11865 | 0 | break; |
11866 | | |
11867 | 0 | case 's': |
11868 | 0 | if (name == PUGIXML_TEXT("string") && argc <= 1) |
11869 | 0 | return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); |
11870 | 0 | else if (name == PUGIXML_TEXT("string-length") && argc <= 1) |
11871 | 0 | return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); |
11872 | 0 | else if (name == PUGIXML_TEXT("starts-with") && argc == 2) |
11873 | 0 | return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); |
11874 | 0 | else if (name == PUGIXML_TEXT("substring-before") && argc == 2) |
11875 | 0 | return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); |
11876 | 0 | else if (name == PUGIXML_TEXT("substring-after") && argc == 2) |
11877 | 0 | return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); |
11878 | 0 | else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) |
11879 | 0 | return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); |
11880 | 0 | else if (name == PUGIXML_TEXT("sum") && argc == 1) |
11881 | 0 | { |
11882 | 0 | if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); |
11883 | 0 | return alloc_node(ast_func_sum, xpath_type_number, args[0]); |
11884 | 0 | } |
11885 | | |
11886 | 0 | break; |
11887 | | |
11888 | 0 | case 't': |
11889 | 0 | if (name == PUGIXML_TEXT("translate") && argc == 3) |
11890 | 0 | return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]); |
11891 | 0 | else if (name == PUGIXML_TEXT("true") && argc == 0) |
11892 | 0 | return alloc_node(ast_func_true, xpath_type_boolean); |
11893 | | |
11894 | 0 | break; |
11895 | | |
11896 | 0 | default: |
11897 | 0 | break; |
11898 | 0 | } |
11899 | | |
11900 | 0 | return error("Unrecognized function or wrong parameter count"); |
11901 | 0 | } |
11902 | | |
11903 | | axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) |
11904 | 0 | { |
11905 | 0 | specified = true; |
11906 | |
|
11907 | 0 | switch (name.begin[0]) |
11908 | 0 | { |
11909 | 0 | case 'a': |
11910 | 0 | if (name == PUGIXML_TEXT("ancestor")) |
11911 | 0 | return axis_ancestor; |
11912 | 0 | else if (name == PUGIXML_TEXT("ancestor-or-self")) |
11913 | 0 | return axis_ancestor_or_self; |
11914 | 0 | else if (name == PUGIXML_TEXT("attribute")) |
11915 | 0 | return axis_attribute; |
11916 | | |
11917 | 0 | break; |
11918 | | |
11919 | 0 | case 'c': |
11920 | 0 | if (name == PUGIXML_TEXT("child")) |
11921 | 0 | return axis_child; |
11922 | | |
11923 | 0 | break; |
11924 | | |
11925 | 0 | case 'd': |
11926 | 0 | if (name == PUGIXML_TEXT("descendant")) |
11927 | 0 | return axis_descendant; |
11928 | 0 | else if (name == PUGIXML_TEXT("descendant-or-self")) |
11929 | 0 | return axis_descendant_or_self; |
11930 | | |
11931 | 0 | break; |
11932 | | |
11933 | 0 | case 'f': |
11934 | 0 | if (name == PUGIXML_TEXT("following")) |
11935 | 0 | return axis_following; |
11936 | 0 | else if (name == PUGIXML_TEXT("following-sibling")) |
11937 | 0 | return axis_following_sibling; |
11938 | | |
11939 | 0 | break; |
11940 | | |
11941 | 0 | case 'n': |
11942 | 0 | if (name == PUGIXML_TEXT("namespace")) |
11943 | 0 | return axis_namespace; |
11944 | | |
11945 | 0 | break; |
11946 | | |
11947 | 0 | case 'p': |
11948 | 0 | if (name == PUGIXML_TEXT("parent")) |
11949 | 0 | return axis_parent; |
11950 | 0 | else if (name == PUGIXML_TEXT("preceding")) |
11951 | 0 | return axis_preceding; |
11952 | 0 | else if (name == PUGIXML_TEXT("preceding-sibling")) |
11953 | 0 | return axis_preceding_sibling; |
11954 | | |
11955 | 0 | break; |
11956 | | |
11957 | 0 | case 's': |
11958 | 0 | if (name == PUGIXML_TEXT("self")) |
11959 | 0 | return axis_self; |
11960 | | |
11961 | 0 | break; |
11962 | | |
11963 | 0 | default: |
11964 | 0 | break; |
11965 | 0 | } |
11966 | | |
11967 | 0 | specified = false; |
11968 | 0 | return axis_child; |
11969 | 0 | } |
11970 | | |
11971 | | nodetest_t parse_node_test_type(const xpath_lexer_string& name) |
11972 | 0 | { |
11973 | 0 | switch (name.begin[0]) |
11974 | 0 | { |
11975 | 0 | case 'c': |
11976 | 0 | if (name == PUGIXML_TEXT("comment")) |
11977 | 0 | return nodetest_type_comment; |
11978 | | |
11979 | 0 | break; |
11980 | | |
11981 | 0 | case 'n': |
11982 | 0 | if (name == PUGIXML_TEXT("node")) |
11983 | 0 | return nodetest_type_node; |
11984 | | |
11985 | 0 | break; |
11986 | | |
11987 | 0 | case 'p': |
11988 | 0 | if (name == PUGIXML_TEXT("processing-instruction")) |
11989 | 0 | return nodetest_type_pi; |
11990 | | |
11991 | 0 | break; |
11992 | | |
11993 | 0 | case 't': |
11994 | 0 | if (name == PUGIXML_TEXT("text")) |
11995 | 0 | return nodetest_type_text; |
11996 | | |
11997 | 0 | break; |
11998 | | |
11999 | 0 | default: |
12000 | 0 | break; |
12001 | 0 | } |
12002 | | |
12003 | 0 | return nodetest_none; |
12004 | 0 | } |
12005 | | |
12006 | | // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall |
12007 | | xpath_ast_node* parse_primary_expression() |
12008 | 0 | { |
12009 | 0 | switch (_lexer.current()) |
12010 | 0 | { |
12011 | 0 | case lex_var_ref: |
12012 | 0 | { |
12013 | 0 | xpath_lexer_string name = _lexer.contents(); |
12014 | |
|
12015 | 0 | if (!_variables) |
12016 | 0 | return error("Unknown variable: variable set is not provided"); |
12017 | | |
12018 | 0 | xpath_variable* var = NULL; |
12019 | 0 | if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) |
12020 | 0 | return error_oom(); |
12021 | | |
12022 | 0 | if (!var) |
12023 | 0 | return error("Unknown variable: variable set does not contain the given name"); |
12024 | | |
12025 | 0 | _lexer.next(); |
12026 | |
|
12027 | 0 | return alloc_node(ast_variable, var->type(), var); |
12028 | 0 | } |
12029 | | |
12030 | 0 | case lex_open_brace: |
12031 | 0 | { |
12032 | 0 | _lexer.next(); |
12033 | |
|
12034 | 0 | xpath_ast_node* n = parse_expression(); |
12035 | 0 | if (!n) return NULL; |
12036 | | |
12037 | 0 | if (_lexer.current() != lex_close_brace) |
12038 | 0 | return error("Expected ')' to match an opening '('"); |
12039 | | |
12040 | 0 | _lexer.next(); |
12041 | |
|
12042 | 0 | return n; |
12043 | 0 | } |
12044 | | |
12045 | 0 | case lex_quoted_string: |
12046 | 0 | { |
12047 | 0 | const char_t* value = alloc_string(_lexer.contents()); |
12048 | 0 | if (!value) return NULL; |
12049 | | |
12050 | 0 | _lexer.next(); |
12051 | |
|
12052 | 0 | return alloc_node(ast_string_constant, xpath_type_string, value); |
12053 | 0 | } |
12054 | | |
12055 | 0 | case lex_number: |
12056 | 0 | { |
12057 | 0 | double value = 0; |
12058 | |
|
12059 | 0 | if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) |
12060 | 0 | return error_oom(); |
12061 | | |
12062 | 0 | _lexer.next(); |
12063 | |
|
12064 | 0 | return alloc_node(ast_number_constant, xpath_type_number, value); |
12065 | 0 | } |
12066 | | |
12067 | 0 | case lex_string: |
12068 | 0 | { |
12069 | 0 | xpath_ast_node* args[2] = {NULL}; |
12070 | 0 | size_t argc = 0; |
12071 | |
|
12072 | 0 | xpath_lexer_string function = _lexer.contents(); |
12073 | 0 | _lexer.next(); |
12074 | |
|
12075 | 0 | xpath_ast_node* last_arg = NULL; |
12076 | |
|
12077 | 0 | if (_lexer.current() != lex_open_brace) |
12078 | 0 | return error("Unrecognized function call"); |
12079 | 0 | _lexer.next(); |
12080 | |
|
12081 | 0 | size_t old_depth = _depth; |
12082 | |
|
12083 | 0 | while (_lexer.current() != lex_close_brace) |
12084 | 0 | { |
12085 | 0 | if (argc > 0) |
12086 | 0 | { |
12087 | 0 | if (_lexer.current() != lex_comma) |
12088 | 0 | return error("No comma between function arguments"); |
12089 | 0 | _lexer.next(); |
12090 | 0 | } |
12091 | | |
12092 | 0 | if (++_depth > xpath_ast_depth_limit) |
12093 | 0 | return error_rec(); |
12094 | | |
12095 | 0 | xpath_ast_node* n = parse_expression(); |
12096 | 0 | if (!n) return NULL; |
12097 | | |
12098 | 0 | if (argc < 2) args[argc] = n; |
12099 | 0 | else last_arg->set_next(n); |
12100 | |
|
12101 | 0 | argc++; |
12102 | 0 | last_arg = n; |
12103 | 0 | } |
12104 | | |
12105 | 0 | _lexer.next(); |
12106 | |
|
12107 | 0 | _depth = old_depth; |
12108 | |
|
12109 | 0 | return parse_function(function, argc, args); |
12110 | 0 | } |
12111 | | |
12112 | 0 | default: |
12113 | 0 | return error("Unrecognizable primary expression"); |
12114 | 0 | } |
12115 | 0 | } |
12116 | | |
12117 | | // FilterExpr ::= PrimaryExpr | FilterExpr Predicate |
12118 | | // Predicate ::= '[' PredicateExpr ']' |
12119 | | // PredicateExpr ::= Expr |
12120 | | xpath_ast_node* parse_filter_expression() |
12121 | 0 | { |
12122 | 0 | xpath_ast_node* n = parse_primary_expression(); |
12123 | 0 | if (!n) return NULL; |
12124 | | |
12125 | 0 | size_t old_depth = _depth; |
12126 | |
|
12127 | 0 | while (_lexer.current() == lex_open_square_brace) |
12128 | 0 | { |
12129 | 0 | _lexer.next(); |
12130 | |
|
12131 | 0 | if (++_depth > xpath_ast_depth_limit) |
12132 | 0 | return error_rec(); |
12133 | | |
12134 | 0 | if (n->rettype() != xpath_type_node_set) |
12135 | 0 | return error("Predicate has to be applied to node set"); |
12136 | | |
12137 | 0 | xpath_ast_node* expr = parse_expression(); |
12138 | 0 | if (!expr) return NULL; |
12139 | | |
12140 | 0 | n = alloc_node(ast_filter, n, expr, predicate_default); |
12141 | 0 | if (!n) return NULL; |
12142 | | |
12143 | 0 | if (_lexer.current() != lex_close_square_brace) |
12144 | 0 | return error("Expected ']' to match an opening '['"); |
12145 | | |
12146 | 0 | _lexer.next(); |
12147 | 0 | } |
12148 | | |
12149 | 0 | _depth = old_depth; |
12150 | |
|
12151 | 0 | return n; |
12152 | 0 | } |
12153 | | |
12154 | | // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep |
12155 | | // AxisSpecifier ::= AxisName '::' | '@'? |
12156 | | // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' |
12157 | | // NameTest ::= '*' | NCName ':' '*' | QName |
12158 | | // AbbreviatedStep ::= '.' | '..' |
12159 | | xpath_ast_node* parse_step(xpath_ast_node* set) |
12160 | 0 | { |
12161 | 0 | if (set && set->rettype() != xpath_type_node_set) |
12162 | 0 | return error("Step has to be applied to node set"); |
12163 | | |
12164 | 0 | bool axis_specified = false; |
12165 | 0 | axis_t axis = axis_child; // implied child axis |
12166 | |
|
12167 | 0 | if (_lexer.current() == lex_axis_attribute) |
12168 | 0 | { |
12169 | 0 | axis = axis_attribute; |
12170 | 0 | axis_specified = true; |
12171 | |
|
12172 | 0 | _lexer.next(); |
12173 | 0 | } |
12174 | 0 | else if (_lexer.current() == lex_dot) |
12175 | 0 | { |
12176 | 0 | _lexer.next(); |
12177 | |
|
12178 | 0 | if (_lexer.current() == lex_open_square_brace) |
12179 | 0 | return error("Predicates are not allowed after an abbreviated step"); |
12180 | | |
12181 | 0 | return alloc_node(ast_step, set, axis_self, nodetest_type_node, NULL); |
12182 | 0 | } |
12183 | 0 | else if (_lexer.current() == lex_double_dot) |
12184 | 0 | { |
12185 | 0 | _lexer.next(); |
12186 | |
|
12187 | 0 | if (_lexer.current() == lex_open_square_brace) |
12188 | 0 | return error("Predicates are not allowed after an abbreviated step"); |
12189 | | |
12190 | 0 | return alloc_node(ast_step, set, axis_parent, nodetest_type_node, NULL); |
12191 | 0 | } |
12192 | | |
12193 | 0 | nodetest_t nt_type = nodetest_none; |
12194 | 0 | xpath_lexer_string nt_name; |
12195 | |
|
12196 | 0 | if (_lexer.current() == lex_string) |
12197 | 0 | { |
12198 | | // node name test |
12199 | 0 | nt_name = _lexer.contents(); |
12200 | 0 | _lexer.next(); |
12201 | | |
12202 | | // was it an axis name? |
12203 | 0 | if (_lexer.current() == lex_double_colon) |
12204 | 0 | { |
12205 | | // parse axis name |
12206 | 0 | if (axis_specified) |
12207 | 0 | return error("Two axis specifiers in one step"); |
12208 | | |
12209 | 0 | axis = parse_axis_name(nt_name, axis_specified); |
12210 | |
|
12211 | 0 | if (!axis_specified) |
12212 | 0 | return error("Unknown axis"); |
12213 | | |
12214 | | // read actual node test |
12215 | 0 | _lexer.next(); |
12216 | |
|
12217 | 0 | if (_lexer.current() == lex_multiply) |
12218 | 0 | { |
12219 | 0 | nt_type = nodetest_all; |
12220 | 0 | nt_name = xpath_lexer_string(); |
12221 | 0 | _lexer.next(); |
12222 | 0 | } |
12223 | 0 | else if (_lexer.current() == lex_string) |
12224 | 0 | { |
12225 | 0 | nt_name = _lexer.contents(); |
12226 | 0 | _lexer.next(); |
12227 | 0 | } |
12228 | 0 | else |
12229 | 0 | { |
12230 | 0 | return error("Unrecognized node test"); |
12231 | 0 | } |
12232 | 0 | } |
12233 | | |
12234 | 0 | if (nt_type == nodetest_none) |
12235 | 0 | { |
12236 | | // node type test or processing-instruction |
12237 | 0 | if (_lexer.current() == lex_open_brace) |
12238 | 0 | { |
12239 | 0 | _lexer.next(); |
12240 | |
|
12241 | 0 | if (_lexer.current() == lex_close_brace) |
12242 | 0 | { |
12243 | 0 | _lexer.next(); |
12244 | |
|
12245 | 0 | nt_type = parse_node_test_type(nt_name); |
12246 | |
|
12247 | 0 | if (nt_type == nodetest_none) |
12248 | 0 | return error("Unrecognized node type"); |
12249 | | |
12250 | 0 | nt_name = xpath_lexer_string(); |
12251 | 0 | } |
12252 | 0 | else if (nt_name == PUGIXML_TEXT("processing-instruction")) |
12253 | 0 | { |
12254 | 0 | if (_lexer.current() != lex_quoted_string) |
12255 | 0 | return error("Only literals are allowed as arguments to processing-instruction()"); |
12256 | | |
12257 | 0 | nt_type = nodetest_pi; |
12258 | 0 | nt_name = _lexer.contents(); |
12259 | 0 | _lexer.next(); |
12260 | |
|
12261 | 0 | if (_lexer.current() != lex_close_brace) |
12262 | 0 | return error("Unmatched brace near processing-instruction()"); |
12263 | 0 | _lexer.next(); |
12264 | 0 | } |
12265 | 0 | else |
12266 | 0 | { |
12267 | 0 | return error("Unmatched brace near node type test"); |
12268 | 0 | } |
12269 | 0 | } |
12270 | | // QName or NCName:* |
12271 | 0 | else |
12272 | 0 | { |
12273 | 0 | if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* |
12274 | 0 | { |
12275 | 0 | nt_name.end--; // erase * |
12276 | |
|
12277 | 0 | nt_type = nodetest_all_in_namespace; |
12278 | 0 | } |
12279 | 0 | else |
12280 | 0 | { |
12281 | 0 | nt_type = nodetest_name; |
12282 | 0 | } |
12283 | 0 | } |
12284 | 0 | } |
12285 | 0 | } |
12286 | 0 | else if (_lexer.current() == lex_multiply) |
12287 | 0 | { |
12288 | 0 | nt_type = nodetest_all; |
12289 | 0 | _lexer.next(); |
12290 | 0 | } |
12291 | 0 | else |
12292 | 0 | { |
12293 | 0 | return error("Unrecognized node test"); |
12294 | 0 | } |
12295 | | |
12296 | 0 | const char_t* nt_name_copy = alloc_string(nt_name); |
12297 | 0 | if (!nt_name_copy) return NULL; |
12298 | | |
12299 | 0 | xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); |
12300 | 0 | if (!n) return NULL; |
12301 | | |
12302 | 0 | size_t old_depth = _depth; |
12303 | |
|
12304 | 0 | xpath_ast_node* last = NULL; |
12305 | |
|
12306 | 0 | while (_lexer.current() == lex_open_square_brace) |
12307 | 0 | { |
12308 | 0 | _lexer.next(); |
12309 | |
|
12310 | 0 | if (++_depth > xpath_ast_depth_limit) |
12311 | 0 | return error_rec(); |
12312 | | |
12313 | 0 | xpath_ast_node* expr = parse_expression(); |
12314 | 0 | if (!expr) return NULL; |
12315 | | |
12316 | 0 | xpath_ast_node* pred = alloc_node(ast_predicate, NULL, expr, predicate_default); |
12317 | 0 | if (!pred) return NULL; |
12318 | | |
12319 | 0 | if (_lexer.current() != lex_close_square_brace) |
12320 | 0 | return error("Expected ']' to match an opening '['"); |
12321 | 0 | _lexer.next(); |
12322 | |
|
12323 | 0 | if (last) last->set_next(pred); |
12324 | 0 | else n->set_right(pred); |
12325 | |
|
12326 | 0 | last = pred; |
12327 | 0 | } |
12328 | | |
12329 | 0 | _depth = old_depth; |
12330 | |
|
12331 | 0 | return n; |
12332 | 0 | } |
12333 | | |
12334 | | // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step |
12335 | | xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) |
12336 | 0 | { |
12337 | 0 | xpath_ast_node* n = parse_step(set); |
12338 | 0 | if (!n) return NULL; |
12339 | | |
12340 | 0 | size_t old_depth = _depth; |
12341 | |
|
12342 | 0 | while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) |
12343 | 0 | { |
12344 | 0 | lexeme_t l = _lexer.current(); |
12345 | 0 | _lexer.next(); |
12346 | |
|
12347 | 0 | if (l == lex_double_slash) |
12348 | 0 | { |
12349 | 0 | n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, NULL); |
12350 | 0 | if (!n) return NULL; |
12351 | | |
12352 | 0 | ++_depth; |
12353 | 0 | } |
12354 | | |
12355 | 0 | if (++_depth > xpath_ast_depth_limit) |
12356 | 0 | return error_rec(); |
12357 | | |
12358 | 0 | n = parse_step(n); |
12359 | 0 | if (!n) return NULL; |
12360 | 0 | } |
12361 | | |
12362 | 0 | _depth = old_depth; |
12363 | |
|
12364 | 0 | return n; |
12365 | 0 | } |
12366 | | |
12367 | | // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath |
12368 | | // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath |
12369 | | xpath_ast_node* parse_location_path() |
12370 | 0 | { |
12371 | 0 | if (_lexer.current() == lex_slash) |
12372 | 0 | { |
12373 | 0 | _lexer.next(); |
12374 | |
|
12375 | 0 | xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); |
12376 | 0 | if (!n) return NULL; |
12377 | | |
12378 | | // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path |
12379 | 0 | lexeme_t l = _lexer.current(); |
12380 | |
|
12381 | 0 | if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) |
12382 | 0 | return parse_relative_location_path(n); |
12383 | 0 | else |
12384 | 0 | return n; |
12385 | 0 | } |
12386 | 0 | else if (_lexer.current() == lex_double_slash) |
12387 | 0 | { |
12388 | 0 | _lexer.next(); |
12389 | |
|
12390 | 0 | xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); |
12391 | 0 | if (!n) return NULL; |
12392 | | |
12393 | 0 | n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, NULL); |
12394 | 0 | if (!n) return NULL; |
12395 | | |
12396 | 0 | return parse_relative_location_path(n); |
12397 | 0 | } |
12398 | | |
12399 | | // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 |
12400 | 0 | return parse_relative_location_path(NULL); |
12401 | 0 | } |
12402 | | |
12403 | | // PathExpr ::= LocationPath |
12404 | | // | FilterExpr |
12405 | | // | FilterExpr '/' RelativeLocationPath |
12406 | | // | FilterExpr '//' RelativeLocationPath |
12407 | | // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr |
12408 | | // UnaryExpr ::= UnionExpr | '-' UnaryExpr |
12409 | | xpath_ast_node* parse_path_or_unary_expression() |
12410 | 0 | { |
12411 | | // Clarification. |
12412 | | // PathExpr begins with either LocationPath or FilterExpr. |
12413 | | // FilterExpr begins with PrimaryExpr |
12414 | | // PrimaryExpr begins with '$' in case of it being a variable reference, |
12415 | | // '(' in case of it being an expression, string literal, number constant or |
12416 | | // function call. |
12417 | 0 | if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || |
12418 | 0 | _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || |
12419 | 0 | _lexer.current() == lex_string) |
12420 | 0 | { |
12421 | 0 | if (_lexer.current() == lex_string) |
12422 | 0 | { |
12423 | | // This is either a function call, or not - if not, we shall proceed with location path |
12424 | 0 | const char_t* state = _lexer.state(); |
12425 | |
|
12426 | 0 | while (PUGI_IMPL_IS_CHARTYPE(*state, ct_space)) ++state; |
12427 | |
|
12428 | 0 | if (*state != '(') |
12429 | 0 | return parse_location_path(); |
12430 | | |
12431 | | // This looks like a function call; however this still can be a node-test. Check it. |
12432 | 0 | if (parse_node_test_type(_lexer.contents()) != nodetest_none) |
12433 | 0 | return parse_location_path(); |
12434 | 0 | } |
12435 | | |
12436 | 0 | xpath_ast_node* n = parse_filter_expression(); |
12437 | 0 | if (!n) return NULL; |
12438 | | |
12439 | 0 | if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) |
12440 | 0 | { |
12441 | 0 | lexeme_t l = _lexer.current(); |
12442 | 0 | _lexer.next(); |
12443 | |
|
12444 | 0 | if (l == lex_double_slash) |
12445 | 0 | { |
12446 | 0 | if (n->rettype() != xpath_type_node_set) |
12447 | 0 | return error("Step has to be applied to node set"); |
12448 | | |
12449 | 0 | n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, NULL); |
12450 | 0 | if (!n) return NULL; |
12451 | 0 | } |
12452 | | |
12453 | | // select from location path |
12454 | 0 | return parse_relative_location_path(n); |
12455 | 0 | } |
12456 | | |
12457 | 0 | return n; |
12458 | 0 | } |
12459 | 0 | else if (_lexer.current() == lex_minus) |
12460 | 0 | { |
12461 | 0 | _lexer.next(); |
12462 | | |
12463 | | // precedence 7+ - only parses union expressions |
12464 | 0 | xpath_ast_node* n = parse_expression(7); |
12465 | 0 | if (!n) return NULL; |
12466 | | |
12467 | 0 | return alloc_node(ast_op_negate, xpath_type_number, n); |
12468 | 0 | } |
12469 | 0 | else |
12470 | 0 | { |
12471 | 0 | return parse_location_path(); |
12472 | 0 | } |
12473 | 0 | } |
12474 | | |
12475 | | struct binary_op_t |
12476 | | { |
12477 | | ast_type_t asttype; |
12478 | | xpath_value_type rettype; |
12479 | | int precedence; |
12480 | | |
12481 | 0 | binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) |
12482 | 0 | { |
12483 | 0 | } |
12484 | | |
12485 | 0 | binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) |
12486 | 0 | { |
12487 | 0 | } |
12488 | | |
12489 | | static binary_op_t parse(xpath_lexer& lexer) |
12490 | 0 | { |
12491 | 0 | switch (lexer.current()) |
12492 | 0 | { |
12493 | 0 | case lex_string: |
12494 | 0 | if (lexer.contents() == PUGIXML_TEXT("or")) |
12495 | 0 | return binary_op_t(ast_op_or, xpath_type_boolean, 1); |
12496 | 0 | else if (lexer.contents() == PUGIXML_TEXT("and")) |
12497 | 0 | return binary_op_t(ast_op_and, xpath_type_boolean, 2); |
12498 | 0 | else if (lexer.contents() == PUGIXML_TEXT("div")) |
12499 | 0 | return binary_op_t(ast_op_divide, xpath_type_number, 6); |
12500 | 0 | else if (lexer.contents() == PUGIXML_TEXT("mod")) |
12501 | 0 | return binary_op_t(ast_op_mod, xpath_type_number, 6); |
12502 | 0 | else |
12503 | 0 | return binary_op_t(); |
12504 | | |
12505 | 0 | case lex_equal: |
12506 | 0 | return binary_op_t(ast_op_equal, xpath_type_boolean, 3); |
12507 | | |
12508 | 0 | case lex_not_equal: |
12509 | 0 | return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); |
12510 | | |
12511 | 0 | case lex_less: |
12512 | 0 | return binary_op_t(ast_op_less, xpath_type_boolean, 4); |
12513 | | |
12514 | 0 | case lex_greater: |
12515 | 0 | return binary_op_t(ast_op_greater, xpath_type_boolean, 4); |
12516 | | |
12517 | 0 | case lex_less_or_equal: |
12518 | 0 | return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); |
12519 | | |
12520 | 0 | case lex_greater_or_equal: |
12521 | 0 | return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); |
12522 | | |
12523 | 0 | case lex_plus: |
12524 | 0 | return binary_op_t(ast_op_add, xpath_type_number, 5); |
12525 | | |
12526 | 0 | case lex_minus: |
12527 | 0 | return binary_op_t(ast_op_subtract, xpath_type_number, 5); |
12528 | | |
12529 | 0 | case lex_multiply: |
12530 | 0 | return binary_op_t(ast_op_multiply, xpath_type_number, 6); |
12531 | | |
12532 | 0 | case lex_union: |
12533 | 0 | return binary_op_t(ast_op_union, xpath_type_node_set, 7); |
12534 | | |
12535 | 0 | default: |
12536 | 0 | return binary_op_t(); |
12537 | 0 | } |
12538 | 0 | } |
12539 | | }; |
12540 | | |
12541 | | xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) |
12542 | 0 | { |
12543 | 0 | binary_op_t op = binary_op_t::parse(_lexer); |
12544 | |
|
12545 | 0 | while (op.asttype != ast_unknown && op.precedence >= limit) |
12546 | 0 | { |
12547 | 0 | _lexer.next(); |
12548 | |
|
12549 | 0 | if (++_depth > xpath_ast_depth_limit) |
12550 | 0 | return error_rec(); |
12551 | | |
12552 | 0 | xpath_ast_node* rhs = parse_path_or_unary_expression(); |
12553 | 0 | if (!rhs) return NULL; |
12554 | | |
12555 | 0 | binary_op_t nextop = binary_op_t::parse(_lexer); |
12556 | |
|
12557 | 0 | while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) |
12558 | 0 | { |
12559 | 0 | rhs = parse_expression_rec(rhs, nextop.precedence); |
12560 | 0 | if (!rhs) return NULL; |
12561 | | |
12562 | 0 | nextop = binary_op_t::parse(_lexer); |
12563 | 0 | } |
12564 | | |
12565 | 0 | if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) |
12566 | 0 | return error("Union operator has to be applied to node sets"); |
12567 | | |
12568 | 0 | lhs = alloc_node(op.asttype, op.rettype, lhs, rhs); |
12569 | 0 | if (!lhs) return NULL; |
12570 | | |
12571 | 0 | op = binary_op_t::parse(_lexer); |
12572 | 0 | } |
12573 | | |
12574 | 0 | return lhs; |
12575 | 0 | } |
12576 | | |
12577 | | // Expr ::= OrExpr |
12578 | | // OrExpr ::= AndExpr | OrExpr 'or' AndExpr |
12579 | | // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr |
12580 | | // EqualityExpr ::= RelationalExpr |
12581 | | // | EqualityExpr '=' RelationalExpr |
12582 | | // | EqualityExpr '!=' RelationalExpr |
12583 | | // RelationalExpr ::= AdditiveExpr |
12584 | | // | RelationalExpr '<' AdditiveExpr |
12585 | | // | RelationalExpr '>' AdditiveExpr |
12586 | | // | RelationalExpr '<=' AdditiveExpr |
12587 | | // | RelationalExpr '>=' AdditiveExpr |
12588 | | // AdditiveExpr ::= MultiplicativeExpr |
12589 | | // | AdditiveExpr '+' MultiplicativeExpr |
12590 | | // | AdditiveExpr '-' MultiplicativeExpr |
12591 | | // MultiplicativeExpr ::= UnaryExpr |
12592 | | // | MultiplicativeExpr '*' UnaryExpr |
12593 | | // | MultiplicativeExpr 'div' UnaryExpr |
12594 | | // | MultiplicativeExpr 'mod' UnaryExpr |
12595 | | xpath_ast_node* parse_expression(int limit = 0) |
12596 | 0 | { |
12597 | 0 | size_t old_depth = _depth; |
12598 | |
|
12599 | 0 | if (++_depth > xpath_ast_depth_limit) |
12600 | 0 | return error_rec(); |
12601 | | |
12602 | 0 | xpath_ast_node* n = parse_path_or_unary_expression(); |
12603 | 0 | if (!n) return NULL; |
12604 | | |
12605 | 0 | n = parse_expression_rec(n, limit); |
12606 | |
|
12607 | 0 | _depth = old_depth; |
12608 | |
|
12609 | 0 | return n; |
12610 | 0 | } |
12611 | | |
12612 | 0 | xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result), _depth(0) |
12613 | 0 | { |
12614 | 0 | } |
12615 | | |
12616 | | xpath_ast_node* parse() |
12617 | 0 | { |
12618 | 0 | xpath_ast_node* n = parse_expression(); |
12619 | 0 | if (!n) return NULL; |
12620 | | |
12621 | 0 | assert(_depth == 0); |
12622 | | |
12623 | | // check if there are unparsed tokens left |
12624 | 0 | if (_lexer.current() != lex_eof) |
12625 | 0 | return error("Incorrect query"); |
12626 | | |
12627 | 0 | return n; |
12628 | 0 | } |
12629 | | |
12630 | | static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) |
12631 | 0 | { |
12632 | 0 | xpath_parser parser(query, variables, alloc, result); |
12633 | |
|
12634 | 0 | return parser.parse(); |
12635 | 0 | } |
12636 | | }; |
12637 | | |
12638 | | struct xpath_query_impl |
12639 | | { |
12640 | | static xpath_query_impl* create() |
12641 | 0 | { |
12642 | 0 | void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); |
12643 | 0 | if (!memory) return NULL; |
12644 | | |
12645 | 0 | return new (memory) xpath_query_impl(); |
12646 | 0 | } |
12647 | | |
12648 | | static void destroy(xpath_query_impl* impl) |
12649 | 0 | { |
12650 | | // free all allocated pages |
12651 | 0 | impl->alloc.release(); |
12652 | | |
12653 | | // free allocator memory (with the first page) |
12654 | 0 | xml_memory::deallocate(impl); |
12655 | 0 | } |
12656 | | |
12657 | 0 | xpath_query_impl(): root(NULL), alloc(&block, &oom), oom(false) |
12658 | 0 | { |
12659 | 0 | block.next = NULL; |
12660 | 0 | block.capacity = sizeof(block.data); |
12661 | 0 | } |
12662 | | |
12663 | | xpath_ast_node* root; |
12664 | | xpath_allocator alloc; |
12665 | | xpath_memory_block block; |
12666 | | bool oom; |
12667 | | }; |
12668 | | |
12669 | | PUGI_IMPL_FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) |
12670 | 0 | { |
12671 | 0 | if (!impl) return NULL; |
12672 | | |
12673 | 0 | if (impl->root->rettype() != xpath_type_node_set) |
12674 | 0 | { |
12675 | | #ifdef PUGIXML_NO_EXCEPTIONS |
12676 | | return 0; |
12677 | | #else |
12678 | 0 | xpath_parse_result res; |
12679 | 0 | res.error = "Expression does not evaluate to node set"; |
12680 | |
|
12681 | 0 | throw xpath_exception(res); |
12682 | 0 | #endif |
12683 | 0 | } |
12684 | | |
12685 | 0 | return impl->root; |
12686 | 0 | } |
12687 | | PUGI_IMPL_NS_END |
12688 | | |
12689 | | namespace pugi |
12690 | | { |
12691 | | #ifndef PUGIXML_NO_EXCEPTIONS |
12692 | 0 | PUGI_IMPL_FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) |
12693 | 0 | { |
12694 | 0 | assert(_result.error); |
12695 | 0 | } |
12696 | | |
12697 | | PUGI_IMPL_FN const char* xpath_exception::what() const PUGIXML_NOEXCEPT |
12698 | 0 | { |
12699 | 0 | return _result.error; |
12700 | 0 | } |
12701 | | |
12702 | | PUGI_IMPL_FN const xpath_parse_result& xpath_exception::result() const |
12703 | 0 | { |
12704 | 0 | return _result; |
12705 | 0 | } |
12706 | | #endif |
12707 | | |
12708 | | PUGI_IMPL_FN xpath_node::xpath_node() |
12709 | 0 | { |
12710 | 0 | } |
12711 | | |
12712 | 0 | PUGI_IMPL_FN xpath_node::xpath_node(const xml_node& node_): _node(node_) |
12713 | 0 | { |
12714 | 0 | } |
12715 | | |
12716 | 0 | PUGI_IMPL_FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) |
12717 | 0 | { |
12718 | 0 | } |
12719 | | |
12720 | | PUGI_IMPL_FN xml_node xpath_node::node() const |
12721 | 0 | { |
12722 | 0 | return _attribute ? xml_node() : _node; |
12723 | 0 | } |
12724 | | |
12725 | | PUGI_IMPL_FN xml_attribute xpath_node::attribute() const |
12726 | 0 | { |
12727 | 0 | return _attribute; |
12728 | 0 | } |
12729 | | |
12730 | | PUGI_IMPL_FN xml_node xpath_node::parent() const |
12731 | 0 | { |
12732 | 0 | return _attribute ? _node : _node.parent(); |
12733 | 0 | } |
12734 | | |
12735 | | PUGI_IMPL_FN static void unspecified_bool_xpath_node(xpath_node***) |
12736 | 0 | { |
12737 | 0 | } |
12738 | | |
12739 | | PUGI_IMPL_FN xpath_node::operator xpath_node::unspecified_bool_type() const |
12740 | 0 | { |
12741 | 0 | return (_node || _attribute) ? unspecified_bool_xpath_node : NULL; |
12742 | 0 | } |
12743 | | |
12744 | | PUGI_IMPL_FN bool xpath_node::operator!() const |
12745 | 0 | { |
12746 | 0 | return !(_node || _attribute); |
12747 | 0 | } |
12748 | | |
12749 | | PUGI_IMPL_FN bool xpath_node::operator==(const xpath_node& n) const |
12750 | 0 | { |
12751 | 0 | return _node == n._node && _attribute == n._attribute; |
12752 | 0 | } |
12753 | | |
12754 | | PUGI_IMPL_FN bool xpath_node::operator!=(const xpath_node& n) const |
12755 | 0 | { |
12756 | 0 | return _node != n._node || _attribute != n._attribute; |
12757 | 0 | } |
12758 | | |
12759 | | #ifdef __BORLANDC__ |
12760 | | PUGI_IMPL_FN bool operator&&(const xpath_node& lhs, bool rhs) |
12761 | | { |
12762 | | return (bool)lhs && rhs; |
12763 | | } |
12764 | | |
12765 | | PUGI_IMPL_FN bool operator||(const xpath_node& lhs, bool rhs) |
12766 | | { |
12767 | | return (bool)lhs || rhs; |
12768 | | } |
12769 | | #endif |
12770 | | |
12771 | | PUGI_IMPL_FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) |
12772 | 0 | { |
12773 | 0 | assert(begin_ <= end_); |
12774 | | |
12775 | 0 | size_t size_ = static_cast<size_t>(end_ - begin_); |
12776 | | |
12777 | | // use internal buffer for 0 or 1 elements, heap buffer otherwise |
12778 | 0 | xpath_node* storage = (size_ <= 1) ? _storage : static_cast<xpath_node*>(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); |
12779 | |
|
12780 | 0 | if (!storage) |
12781 | 0 | { |
12782 | | #ifdef PUGIXML_NO_EXCEPTIONS |
12783 | | return; |
12784 | | #else |
12785 | 0 | throw std::bad_alloc(); |
12786 | 0 | #endif |
12787 | 0 | } |
12788 | | |
12789 | | // deallocate old buffer |
12790 | 0 | if (_begin != _storage) |
12791 | 0 | impl::xml_memory::deallocate(_begin); |
12792 | | |
12793 | | // size check is necessary because for begin_ = end_ = nullptr, memcpy is UB |
12794 | 0 | if (size_) |
12795 | 0 | memcpy(storage, begin_, size_ * sizeof(xpath_node)); |
12796 | |
|
12797 | 0 | _begin = storage; |
12798 | 0 | _end = storage + size_; |
12799 | 0 | _type = type_; |
12800 | 0 | } |
12801 | | |
12802 | | #ifdef PUGIXML_HAS_MOVE |
12803 | | PUGI_IMPL_FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT |
12804 | 0 | { |
12805 | 0 | _type = rhs._type; |
12806 | 0 | _storage[0] = rhs._storage[0]; |
12807 | 0 | _begin = (rhs._begin == rhs._storage) ? _storage : rhs._begin; |
12808 | 0 | _end = _begin + (rhs._end - rhs._begin); |
12809 | |
|
12810 | 0 | rhs._type = type_unsorted; |
12811 | 0 | rhs._begin = rhs._storage; |
12812 | 0 | rhs._end = rhs._storage; |
12813 | 0 | } |
12814 | | #endif |
12815 | | |
12816 | 0 | PUGI_IMPL_FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage) |
12817 | 0 | { |
12818 | 0 | } |
12819 | | |
12820 | 0 | PUGI_IMPL_FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(_storage), _end(_storage) |
12821 | 0 | { |
12822 | 0 | _assign(begin_, end_, type_); |
12823 | 0 | } |
12824 | | |
12825 | | PUGI_IMPL_FN xpath_node_set::~xpath_node_set() |
12826 | 0 | { |
12827 | 0 | if (_begin != _storage) |
12828 | 0 | impl::xml_memory::deallocate(_begin); |
12829 | 0 | } |
12830 | | |
12831 | 0 | PUGI_IMPL_FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(_storage), _end(_storage) |
12832 | 0 | { |
12833 | 0 | _assign(ns._begin, ns._end, ns._type); |
12834 | 0 | } |
12835 | | |
12836 | | PUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) |
12837 | 0 | { |
12838 | 0 | if (this == &ns) return *this; |
12839 | | |
12840 | 0 | _assign(ns._begin, ns._end, ns._type); |
12841 | |
|
12842 | 0 | return *this; |
12843 | 0 | } |
12844 | | |
12845 | | #ifdef PUGIXML_HAS_MOVE |
12846 | 0 | PUGI_IMPL_FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage) |
12847 | 0 | { |
12848 | 0 | _move(rhs); |
12849 | 0 | } |
12850 | | |
12851 | | PUGI_IMPL_FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT |
12852 | 0 | { |
12853 | 0 | if (this == &rhs) return *this; |
12854 | | |
12855 | 0 | if (_begin != _storage) |
12856 | 0 | impl::xml_memory::deallocate(_begin); |
12857 | |
|
12858 | 0 | _move(rhs); |
12859 | |
|
12860 | 0 | return *this; |
12861 | 0 | } |
12862 | | #endif |
12863 | | |
12864 | | PUGI_IMPL_FN xpath_node_set::type_t xpath_node_set::type() const |
12865 | 0 | { |
12866 | 0 | return _type; |
12867 | 0 | } |
12868 | | |
12869 | | PUGI_IMPL_FN size_t xpath_node_set::size() const |
12870 | 0 | { |
12871 | 0 | return _end - _begin; |
12872 | 0 | } |
12873 | | |
12874 | | PUGI_IMPL_FN bool xpath_node_set::empty() const |
12875 | 0 | { |
12876 | 0 | return _begin == _end; |
12877 | 0 | } |
12878 | | |
12879 | | PUGI_IMPL_FN const xpath_node& xpath_node_set::operator[](size_t index) const |
12880 | 0 | { |
12881 | 0 | assert(index < size()); |
12882 | 0 | return _begin[index]; |
12883 | 0 | } |
12884 | | |
12885 | | PUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::begin() const |
12886 | 0 | { |
12887 | 0 | return _begin; |
12888 | 0 | } |
12889 | | |
12890 | | PUGI_IMPL_FN xpath_node_set::const_iterator xpath_node_set::end() const |
12891 | 0 | { |
12892 | 0 | return _end; |
12893 | 0 | } |
12894 | | |
12895 | | PUGI_IMPL_FN void xpath_node_set::sort(bool reverse) |
12896 | 0 | { |
12897 | 0 | _type = impl::xpath_sort(_begin, _end, _type, reverse); |
12898 | 0 | } |
12899 | | |
12900 | | PUGI_IMPL_FN xpath_node xpath_node_set::first() const |
12901 | 0 | { |
12902 | 0 | return impl::xpath_first(_begin, _end, _type); |
12903 | 0 | } |
12904 | | |
12905 | 0 | PUGI_IMPL_FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) |
12906 | 0 | { |
12907 | 0 | } |
12908 | | |
12909 | | PUGI_IMPL_FN xpath_parse_result::operator bool() const |
12910 | 0 | { |
12911 | 0 | return error == NULL; |
12912 | 0 | } |
12913 | | |
12914 | | PUGI_IMPL_FN const char* xpath_parse_result::description() const |
12915 | 0 | { |
12916 | 0 | return error ? error : "No error"; |
12917 | 0 | } |
12918 | | |
12919 | 0 | PUGI_IMPL_FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(NULL) |
12920 | 0 | { |
12921 | 0 | } |
12922 | | |
12923 | | PUGI_IMPL_FN const char_t* xpath_variable::name() const |
12924 | 0 | { |
12925 | 0 | switch (_type) |
12926 | 0 | { |
12927 | 0 | case xpath_type_node_set: |
12928 | 0 | return static_cast<const impl::xpath_variable_node_set*>(this)->name; |
12929 | | |
12930 | 0 | case xpath_type_number: |
12931 | 0 | return static_cast<const impl::xpath_variable_number*>(this)->name; |
12932 | | |
12933 | 0 | case xpath_type_string: |
12934 | 0 | return static_cast<const impl::xpath_variable_string*>(this)->name; |
12935 | | |
12936 | 0 | case xpath_type_boolean: |
12937 | 0 | return static_cast<const impl::xpath_variable_boolean*>(this)->name; |
12938 | | |
12939 | 0 | default: |
12940 | 0 | assert(false && "Invalid variable type"); // unreachable |
12941 | 0 | return NULL; |
12942 | 0 | } |
12943 | 0 | } |
12944 | | |
12945 | | PUGI_IMPL_FN xpath_value_type xpath_variable::type() const |
12946 | 0 | { |
12947 | 0 | return _type; |
12948 | 0 | } |
12949 | | |
12950 | | PUGI_IMPL_FN bool xpath_variable::get_boolean() const |
12951 | 0 | { |
12952 | 0 | return (_type == xpath_type_boolean) ? static_cast<const impl::xpath_variable_boolean*>(this)->value : false; |
12953 | 0 | } |
12954 | | |
12955 | | PUGI_IMPL_FN double xpath_variable::get_number() const |
12956 | 0 | { |
12957 | 0 | return (_type == xpath_type_number) ? static_cast<const impl::xpath_variable_number*>(this)->value : impl::gen_nan(); |
12958 | 0 | } |
12959 | | |
12960 | | PUGI_IMPL_FN const char_t* xpath_variable::get_string() const |
12961 | 0 | { |
12962 | 0 | const char_t* value = (_type == xpath_type_string) ? static_cast<const impl::xpath_variable_string*>(this)->value : NULL; |
12963 | 0 | return value ? value : PUGIXML_TEXT(""); |
12964 | 0 | } |
12965 | | |
12966 | | PUGI_IMPL_FN const xpath_node_set& xpath_variable::get_node_set() const |
12967 | 0 | { |
12968 | 0 | if (_type == xpath_type_node_set) |
12969 | 0 | return static_cast<const impl::xpath_variable_node_set*>(this)->value; |
12970 | 0 | static const xpath_node_set dummy_node_set; |
12971 | 0 | return dummy_node_set; |
12972 | 0 | } |
12973 | | |
12974 | | PUGI_IMPL_FN bool xpath_variable::set(bool value) |
12975 | 0 | { |
12976 | 0 | if (_type != xpath_type_boolean) return false; |
12977 | | |
12978 | 0 | static_cast<impl::xpath_variable_boolean*>(this)->value = value; |
12979 | 0 | return true; |
12980 | 0 | } |
12981 | | |
12982 | | PUGI_IMPL_FN bool xpath_variable::set(double value) |
12983 | 0 | { |
12984 | 0 | if (_type != xpath_type_number) return false; |
12985 | | |
12986 | 0 | static_cast<impl::xpath_variable_number*>(this)->value = value; |
12987 | 0 | return true; |
12988 | 0 | } |
12989 | | |
12990 | | PUGI_IMPL_FN bool xpath_variable::set(const char_t* value) |
12991 | 0 | { |
12992 | 0 | if (_type != xpath_type_string) return false; |
12993 | | |
12994 | 0 | impl::xpath_variable_string* var = static_cast<impl::xpath_variable_string*>(this); |
12995 | | |
12996 | | // duplicate string |
12997 | 0 | size_t size = (impl::strlength(value) + 1) * sizeof(char_t); |
12998 | |
|
12999 | 0 | char_t* copy = static_cast<char_t*>(impl::xml_memory::allocate(size)); |
13000 | 0 | if (!copy) return false; |
13001 | | |
13002 | 0 | memcpy(copy, value, size); |
13003 | | |
13004 | | // replace old string |
13005 | 0 | if (var->value) impl::xml_memory::deallocate(var->value); |
13006 | 0 | var->value = copy; |
13007 | |
|
13008 | 0 | return true; |
13009 | 0 | } |
13010 | | |
13011 | | PUGI_IMPL_FN bool xpath_variable::set(const xpath_node_set& value) |
13012 | 0 | { |
13013 | 0 | if (_type != xpath_type_node_set) return false; |
13014 | | |
13015 | 0 | static_cast<impl::xpath_variable_node_set*>(this)->value = value; |
13016 | 0 | return true; |
13017 | 0 | } |
13018 | | |
13019 | | PUGI_IMPL_FN xpath_variable_set::xpath_variable_set() |
13020 | 0 | { |
13021 | 0 | for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) |
13022 | 0 | _data[i] = NULL; |
13023 | 0 | } |
13024 | | |
13025 | | PUGI_IMPL_FN xpath_variable_set::~xpath_variable_set() |
13026 | 0 | { |
13027 | 0 | for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) |
13028 | 0 | _destroy(_data[i]); |
13029 | 0 | } |
13030 | | |
13031 | | PUGI_IMPL_FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) |
13032 | 0 | { |
13033 | 0 | for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) |
13034 | 0 | _data[i] = NULL; |
13035 | |
|
13036 | 0 | _assign(rhs); |
13037 | 0 | } |
13038 | | |
13039 | | PUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) |
13040 | 0 | { |
13041 | 0 | if (this == &rhs) return *this; |
13042 | | |
13043 | 0 | _assign(rhs); |
13044 | |
|
13045 | 0 | return *this; |
13046 | 0 | } |
13047 | | |
13048 | | #ifdef PUGIXML_HAS_MOVE |
13049 | | PUGI_IMPL_FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT |
13050 | 0 | { |
13051 | 0 | for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) |
13052 | 0 | { |
13053 | 0 | _data[i] = rhs._data[i]; |
13054 | 0 | rhs._data[i] = NULL; |
13055 | 0 | } |
13056 | 0 | } |
13057 | | |
13058 | | PUGI_IMPL_FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT |
13059 | 0 | { |
13060 | 0 | for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) |
13061 | 0 | { |
13062 | 0 | _destroy(_data[i]); |
13063 | |
|
13064 | 0 | _data[i] = rhs._data[i]; |
13065 | 0 | rhs._data[i] = NULL; |
13066 | 0 | } |
13067 | |
|
13068 | 0 | return *this; |
13069 | 0 | } |
13070 | | #endif |
13071 | | |
13072 | | PUGI_IMPL_FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) |
13073 | 0 | { |
13074 | 0 | xpath_variable_set temp; |
13075 | |
|
13076 | 0 | for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) |
13077 | 0 | if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i])) |
13078 | 0 | return; |
13079 | | |
13080 | 0 | _swap(temp); |
13081 | 0 | } |
13082 | | |
13083 | | PUGI_IMPL_FN void xpath_variable_set::_swap(xpath_variable_set& rhs) |
13084 | 0 | { |
13085 | 0 | for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) |
13086 | 0 | { |
13087 | 0 | xpath_variable* chain = _data[i]; |
13088 | |
|
13089 | 0 | _data[i] = rhs._data[i]; |
13090 | 0 | rhs._data[i] = chain; |
13091 | 0 | } |
13092 | 0 | } |
13093 | | |
13094 | | PUGI_IMPL_FN xpath_variable* xpath_variable_set::_find(const char_t* name) const |
13095 | 0 | { |
13096 | 0 | const size_t hash_size = sizeof(_data) / sizeof(_data[0]); |
13097 | 0 | size_t hash = impl::hash_string(name) % hash_size; |
13098 | | |
13099 | | // look for existing variable |
13100 | 0 | for (xpath_variable* var = _data[hash]; var; var = var->_next) |
13101 | 0 | { |
13102 | 0 | const char_t* vn = var->name(); |
13103 | 0 | if (vn && impl::strequal(vn, name)) |
13104 | 0 | return var; |
13105 | 0 | } |
13106 | | |
13107 | 0 | return NULL; |
13108 | 0 | } |
13109 | | |
13110 | | PUGI_IMPL_FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) |
13111 | 0 | { |
13112 | 0 | xpath_variable* last = NULL; |
13113 | |
|
13114 | 0 | while (var) |
13115 | 0 | { |
13116 | | // allocate storage for new variable |
13117 | 0 | xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name()); |
13118 | 0 | if (!nvar) return false; |
13119 | | |
13120 | | // link the variable to the result immediately to handle failures gracefully |
13121 | 0 | if (last) |
13122 | 0 | last->_next = nvar; |
13123 | 0 | else |
13124 | 0 | *out_result = nvar; |
13125 | |
|
13126 | 0 | last = nvar; |
13127 | | |
13128 | | // copy the value; this can fail due to out-of-memory conditions |
13129 | 0 | if (!impl::copy_xpath_variable(nvar, var)) return false; |
13130 | | |
13131 | 0 | var = var->_next; |
13132 | 0 | } |
13133 | | |
13134 | 0 | return true; |
13135 | 0 | } |
13136 | | |
13137 | | PUGI_IMPL_FN void xpath_variable_set::_destroy(xpath_variable* var) |
13138 | 0 | { |
13139 | 0 | while (var) |
13140 | 0 | { |
13141 | 0 | xpath_variable* next = var->_next; |
13142 | |
|
13143 | 0 | impl::delete_xpath_variable(var->_type, var); |
13144 | |
|
13145 | 0 | var = next; |
13146 | 0 | } |
13147 | 0 | } |
13148 | | |
13149 | | PUGI_IMPL_FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) |
13150 | 0 | { |
13151 | 0 | const size_t hash_size = sizeof(_data) / sizeof(_data[0]); |
13152 | 0 | size_t hash = impl::hash_string(name) % hash_size; |
13153 | | |
13154 | | // look for existing variable |
13155 | 0 | for (xpath_variable* var = _data[hash]; var; var = var->_next) |
13156 | 0 | { |
13157 | 0 | const char_t* vn = var->name(); |
13158 | 0 | if (vn && impl::strequal(vn, name)) |
13159 | 0 | return var->type() == type ? var : NULL; |
13160 | 0 | } |
13161 | | |
13162 | | // add new variable |
13163 | 0 | xpath_variable* result = impl::new_xpath_variable(type, name); |
13164 | |
|
13165 | 0 | if (result) |
13166 | 0 | { |
13167 | 0 | result->_next = _data[hash]; |
13168 | |
|
13169 | 0 | _data[hash] = result; |
13170 | 0 | } |
13171 | |
|
13172 | 0 | return result; |
13173 | 0 | } |
13174 | | |
13175 | | PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, bool value) |
13176 | 0 | { |
13177 | 0 | xpath_variable* var = add(name, xpath_type_boolean); |
13178 | 0 | return var ? var->set(value) : false; |
13179 | 0 | } |
13180 | | |
13181 | | PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, double value) |
13182 | 0 | { |
13183 | 0 | xpath_variable* var = add(name, xpath_type_number); |
13184 | 0 | return var ? var->set(value) : false; |
13185 | 0 | } |
13186 | | |
13187 | | PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const char_t* value) |
13188 | 0 | { |
13189 | 0 | xpath_variable* var = add(name, xpath_type_string); |
13190 | 0 | return var ? var->set(value) : false; |
13191 | 0 | } |
13192 | | |
13193 | | PUGI_IMPL_FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) |
13194 | 0 | { |
13195 | 0 | xpath_variable* var = add(name, xpath_type_node_set); |
13196 | 0 | return var ? var->set(value) : false; |
13197 | 0 | } |
13198 | | |
13199 | | PUGI_IMPL_FN xpath_variable* xpath_variable_set::get(const char_t* name) |
13200 | 0 | { |
13201 | 0 | return _find(name); |
13202 | 0 | } |
13203 | | |
13204 | | PUGI_IMPL_FN const xpath_variable* xpath_variable_set::get(const char_t* name) const |
13205 | 0 | { |
13206 | 0 | return _find(name); |
13207 | 0 | } |
13208 | | |
13209 | 0 | PUGI_IMPL_FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(NULL) |
13210 | 0 | { |
13211 | 0 | impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); |
13212 | |
|
13213 | 0 | if (!qimpl) |
13214 | 0 | { |
13215 | | #ifdef PUGIXML_NO_EXCEPTIONS |
13216 | | _result.error = "Out of memory"; |
13217 | | #else |
13218 | 0 | throw std::bad_alloc(); |
13219 | 0 | #endif |
13220 | 0 | } |
13221 | 0 | else |
13222 | 0 | { |
13223 | 0 | using impl::auto_deleter; // MSVC7 workaround |
13224 | 0 | auto_deleter<impl::xpath_query_impl> impl(qimpl, impl::xpath_query_impl::destroy); |
13225 | |
|
13226 | 0 | qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); |
13227 | |
|
13228 | 0 | if (qimpl->root) |
13229 | 0 | { |
13230 | 0 | qimpl->root->optimize(&qimpl->alloc); |
13231 | |
|
13232 | 0 | _impl = impl.release(); |
13233 | 0 | _result.error = NULL; |
13234 | 0 | } |
13235 | 0 | else |
13236 | 0 | { |
13237 | | #ifdef PUGIXML_NO_EXCEPTIONS |
13238 | | if (qimpl->oom) _result.error = "Out of memory"; |
13239 | | #else |
13240 | 0 | if (qimpl->oom) throw std::bad_alloc(); |
13241 | 0 | throw xpath_exception(_result); |
13242 | 0 | #endif |
13243 | 0 | } |
13244 | 0 | } |
13245 | 0 | } |
13246 | | |
13247 | 0 | PUGI_IMPL_FN xpath_query::xpath_query(): _impl(NULL) |
13248 | 0 | { |
13249 | 0 | } |
13250 | | |
13251 | | PUGI_IMPL_FN xpath_query::~xpath_query() |
13252 | 0 | { |
13253 | 0 | if (_impl) |
13254 | 0 | impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl)); |
13255 | 0 | } |
13256 | | |
13257 | | #ifdef PUGIXML_HAS_MOVE |
13258 | | PUGI_IMPL_FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT |
13259 | 0 | { |
13260 | 0 | _impl = rhs._impl; |
13261 | 0 | _result = rhs._result; |
13262 | 0 | rhs._impl = NULL; |
13263 | 0 | rhs._result = xpath_parse_result(); |
13264 | 0 | } |
13265 | | |
13266 | | PUGI_IMPL_FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT |
13267 | 0 | { |
13268 | 0 | if (this == &rhs) return *this; |
13269 | | |
13270 | 0 | if (_impl) |
13271 | 0 | impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl)); |
13272 | |
|
13273 | 0 | _impl = rhs._impl; |
13274 | 0 | _result = rhs._result; |
13275 | 0 | rhs._impl = NULL; |
13276 | 0 | rhs._result = xpath_parse_result(); |
13277 | |
|
13278 | 0 | return *this; |
13279 | 0 | } |
13280 | | #endif |
13281 | | |
13282 | | PUGI_IMPL_FN xpath_value_type xpath_query::return_type() const |
13283 | 0 | { |
13284 | 0 | if (!_impl) return xpath_type_none; |
13285 | | |
13286 | 0 | return static_cast<impl::xpath_query_impl*>(_impl)->root->rettype(); |
13287 | 0 | } |
13288 | | |
13289 | | PUGI_IMPL_FN bool xpath_query::evaluate_boolean(const xpath_node& n) const |
13290 | 0 | { |
13291 | 0 | if (!_impl) return false; |
13292 | | |
13293 | 0 | impl::xpath_context c(n, 1, 1); |
13294 | 0 | impl::xpath_stack_data sd; |
13295 | |
|
13296 | 0 | bool r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack); |
13297 | |
|
13298 | 0 | if (sd.oom) |
13299 | 0 | { |
13300 | | #ifdef PUGIXML_NO_EXCEPTIONS |
13301 | | return false; |
13302 | | #else |
13303 | 0 | throw std::bad_alloc(); |
13304 | 0 | #endif |
13305 | 0 | } |
13306 | | |
13307 | 0 | return r; |
13308 | 0 | } |
13309 | | |
13310 | | PUGI_IMPL_FN double xpath_query::evaluate_number(const xpath_node& n) const |
13311 | 0 | { |
13312 | 0 | if (!_impl) return impl::gen_nan(); |
13313 | | |
13314 | 0 | impl::xpath_context c(n, 1, 1); |
13315 | 0 | impl::xpath_stack_data sd; |
13316 | |
|
13317 | 0 | double r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack); |
13318 | |
|
13319 | 0 | if (sd.oom) |
13320 | 0 | { |
13321 | | #ifdef PUGIXML_NO_EXCEPTIONS |
13322 | | return impl::gen_nan(); |
13323 | | #else |
13324 | 0 | throw std::bad_alloc(); |
13325 | 0 | #endif |
13326 | 0 | } |
13327 | | |
13328 | 0 | return r; |
13329 | 0 | } |
13330 | | |
13331 | | #ifndef PUGIXML_NO_STL |
13332 | | PUGI_IMPL_FN string_t xpath_query::evaluate_string(const xpath_node& n) const |
13333 | 0 | { |
13334 | 0 | if (!_impl) return string_t(); |
13335 | | |
13336 | 0 | impl::xpath_context c(n, 1, 1); |
13337 | 0 | impl::xpath_stack_data sd; |
13338 | |
|
13339 | 0 | impl::xpath_string r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack); |
13340 | |
|
13341 | 0 | if (sd.oom) |
13342 | 0 | { |
13343 | | #ifdef PUGIXML_NO_EXCEPTIONS |
13344 | | return string_t(); |
13345 | | #else |
13346 | 0 | throw std::bad_alloc(); |
13347 | 0 | #endif |
13348 | 0 | } |
13349 | | |
13350 | 0 | return string_t(r.c_str(), r.length()); |
13351 | 0 | } |
13352 | | #endif |
13353 | | |
13354 | | PUGI_IMPL_FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const |
13355 | 0 | { |
13356 | 0 | impl::xpath_context c(n, 1, 1); |
13357 | 0 | impl::xpath_stack_data sd; |
13358 | |
|
13359 | 0 | impl::xpath_string r = _impl ? static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string(); |
13360 | |
|
13361 | 0 | if (sd.oom) |
13362 | 0 | { |
13363 | | #ifdef PUGIXML_NO_EXCEPTIONS |
13364 | | r = impl::xpath_string(); |
13365 | | #else |
13366 | 0 | throw std::bad_alloc(); |
13367 | 0 | #endif |
13368 | 0 | } |
13369 | | |
13370 | 0 | size_t full_size = r.length() + 1; |
13371 | |
|
13372 | 0 | if (capacity > 0) |
13373 | 0 | { |
13374 | 0 | size_t size = (full_size < capacity) ? full_size : capacity; |
13375 | 0 | assert(size > 0); |
13376 | | |
13377 | 0 | memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); |
13378 | 0 | buffer[size - 1] = 0; |
13379 | 0 | } |
13380 | | |
13381 | 0 | return full_size; |
13382 | 0 | } |
13383 | | |
13384 | | PUGI_IMPL_FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const |
13385 | 0 | { |
13386 | 0 | impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl)); |
13387 | 0 | if (!root) return xpath_node_set(); |
13388 | | |
13389 | 0 | impl::xpath_context c(n, 1, 1); |
13390 | 0 | impl::xpath_stack_data sd; |
13391 | |
|
13392 | 0 | impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); |
13393 | |
|
13394 | 0 | if (sd.oom) |
13395 | 0 | { |
13396 | | #ifdef PUGIXML_NO_EXCEPTIONS |
13397 | | return xpath_node_set(); |
13398 | | #else |
13399 | 0 | throw std::bad_alloc(); |
13400 | 0 | #endif |
13401 | 0 | } |
13402 | | |
13403 | 0 | return xpath_node_set(r.begin(), r.end(), r.type()); |
13404 | 0 | } |
13405 | | |
13406 | | PUGI_IMPL_FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const |
13407 | 0 | { |
13408 | 0 | impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl)); |
13409 | 0 | if (!root) return xpath_node(); |
13410 | | |
13411 | 0 | impl::xpath_context c(n, 1, 1); |
13412 | 0 | impl::xpath_stack_data sd; |
13413 | |
|
13414 | 0 | impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); |
13415 | |
|
13416 | 0 | if (sd.oom) |
13417 | 0 | { |
13418 | | #ifdef PUGIXML_NO_EXCEPTIONS |
13419 | | return xpath_node(); |
13420 | | #else |
13421 | 0 | throw std::bad_alloc(); |
13422 | 0 | #endif |
13423 | 0 | } |
13424 | | |
13425 | 0 | return r.first(); |
13426 | 0 | } |
13427 | | |
13428 | | PUGI_IMPL_FN const xpath_parse_result& xpath_query::result() const |
13429 | 0 | { |
13430 | 0 | return _result; |
13431 | 0 | } |
13432 | | |
13433 | | PUGI_IMPL_FN static void unspecified_bool_xpath_query(xpath_query***) |
13434 | 0 | { |
13435 | 0 | } |
13436 | | |
13437 | | PUGI_IMPL_FN xpath_query::operator xpath_query::unspecified_bool_type() const |
13438 | 0 | { |
13439 | 0 | return _impl ? unspecified_bool_xpath_query : NULL; |
13440 | 0 | } |
13441 | | |
13442 | | PUGI_IMPL_FN bool xpath_query::operator!() const |
13443 | 0 | { |
13444 | 0 | return !_impl; |
13445 | 0 | } |
13446 | | |
13447 | | PUGI_IMPL_FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const |
13448 | 0 | { |
13449 | 0 | xpath_query q(query, variables); |
13450 | 0 | return q.evaluate_node(*this); |
13451 | 0 | } |
13452 | | |
13453 | | PUGI_IMPL_FN xpath_node xml_node::select_node(const xpath_query& query) const |
13454 | 0 | { |
13455 | 0 | return query.evaluate_node(*this); |
13456 | 0 | } |
13457 | | |
13458 | | PUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const |
13459 | 0 | { |
13460 | 0 | xpath_query q(query, variables); |
13461 | 0 | return q.evaluate_node_set(*this); |
13462 | 0 | } |
13463 | | |
13464 | | PUGI_IMPL_FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const |
13465 | 0 | { |
13466 | 0 | return query.evaluate_node_set(*this); |
13467 | 0 | } |
13468 | | |
13469 | | PUGI_IMPL_FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const |
13470 | 0 | { |
13471 | 0 | xpath_query q(query, variables); |
13472 | 0 | return q.evaluate_node(*this); |
13473 | 0 | } |
13474 | | |
13475 | | PUGI_IMPL_FN xpath_node xml_node::select_single_node(const xpath_query& query) const |
13476 | 0 | { |
13477 | 0 | return query.evaluate_node(*this); |
13478 | 0 | } |
13479 | | } |
13480 | | |
13481 | | #endif |
13482 | | |
13483 | | #ifdef __BORLANDC__ |
13484 | | # pragma option pop |
13485 | | #endif |
13486 | | |
13487 | | #if defined(_MSC_VER) && defined(__c2__) |
13488 | | # pragma clang diagnostic pop |
13489 | | #endif |
13490 | | |
13491 | | #if defined(__clang__) |
13492 | | # pragma clang diagnostic pop |
13493 | | #endif |
13494 | | |
13495 | | // Intel C++ does not properly keep warning state for function templates, |
13496 | | // so popping warning state at the end of translation unit leads to warnings in the middle. |
13497 | | #if defined(_MSC_VER) && !defined(__INTEL_COMPILER) |
13498 | | # pragma warning(pop) |
13499 | | #endif |
13500 | | |
13501 | | // Undefine all local macros (makes sure we're not leaking macros in header-only mode) |
13502 | | #undef PUGI_IMPL_NO_INLINE |
13503 | | #undef PUGI_IMPL_UNLIKELY |
13504 | | #undef PUGI_IMPL_STATIC_ASSERT |
13505 | | #undef PUGI_IMPL_DMC_VOLATILE |
13506 | | #undef PUGI_IMPL_UNSIGNED_OVERFLOW |
13507 | | #undef PUGI_IMPL_MSVC_CRT_VERSION |
13508 | | #undef PUGI_IMPL_SNPRINTF |
13509 | | #undef PUGI_IMPL_NS_BEGIN |
13510 | | #undef PUGI_IMPL_NS_END |
13511 | | #undef PUGI_IMPL_FN |
13512 | | #undef PUGI_IMPL_FN_NO_INLINE |
13513 | | #undef PUGI_IMPL_GETHEADER_IMPL |
13514 | | #undef PUGI_IMPL_GETPAGE_IMPL |
13515 | | #undef PUGI_IMPL_GETPAGE |
13516 | | #undef PUGI_IMPL_NODETYPE |
13517 | | #undef PUGI_IMPL_IS_CHARTYPE_IMPL |
13518 | | #undef PUGI_IMPL_IS_CHARTYPE |
13519 | | #undef PUGI_IMPL_IS_CHARTYPEX |
13520 | | #undef PUGI_IMPL_ENDSWITH |
13521 | | #undef PUGI_IMPL_SKIPWS |
13522 | | #undef PUGI_IMPL_OPTSET |
13523 | | #undef PUGI_IMPL_PUSHNODE |
13524 | | #undef PUGI_IMPL_POPNODE |
13525 | | #undef PUGI_IMPL_SCANFOR |
13526 | | #undef PUGI_IMPL_SCANWHILE |
13527 | | #undef PUGI_IMPL_SCANWHILE_UNROLL |
13528 | | #undef PUGI_IMPL_ENDSEG |
13529 | | #undef PUGI_IMPL_THROW_ERROR |
13530 | | #undef PUGI_IMPL_CHECK_ERROR |
13531 | | |
13532 | | #endif |
13533 | | |
13534 | | /** |
13535 | | * Copyright (c) 2006-2025 Arseny Kapoulkine |
13536 | | * |
13537 | | * Permission is hereby granted, free of charge, to any person |
13538 | | * obtaining a copy of this software and associated documentation |
13539 | | * files (the "Software"), to deal in the Software without |
13540 | | * restriction, including without limitation the rights to use, |
13541 | | * copy, modify, merge, publish, distribute, sublicense, and/or sell |
13542 | | * copies of the Software, and to permit persons to whom the |
13543 | | * Software is furnished to do so, subject to the following |
13544 | | * conditions: |
13545 | | * |
13546 | | * The above copyright notice and this permission notice shall be |
13547 | | * included in all copies or substantial portions of the Software. |
13548 | | * |
13549 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
13550 | | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
13551 | | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
13552 | | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
13553 | | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
13554 | | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
13555 | | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
13556 | | * OTHER DEALINGS IN THE SOFTWARE. |
13557 | | */ |