CARLA
 
载入中...
搜索中...
未找到
pugixml.cpp
浏览该文件的文档.
1/**
2 * pugixml parser - version 1.9
3 * --------------------------------------------------------
4 * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
5 * Report bugs and download new versions at http://pugixml.org/
6 *
7 * This library is distributed under the MIT License. See notice at the end
8 * of this file.
9 *
10 * This work is based on the pugxml parser, which is:
11 * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
12 */
13
14#ifndef SOURCE_PUGIXML_CPP
15#define SOURCE_PUGIXML_CPP
16
17#include "pugixml.hpp"
18
19#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22#include <assert.h>
23#include <limits.h>
24
25#ifdef PUGIXML_WCHAR_MODE
26# include <wchar.h>
27#endif
28
29#ifndef PUGIXML_NO_XPATH
30# include <math.h>
31# include <float.h>
32#endif
33
34#ifndef PUGIXML_NO_STL
35# include <istream>
36# include <ostream>
37# include <string>
38#endif
39
40// For placement new
41#include <new>
42
43#if defined(__clang__)
44# pragma clang diagnostic push
45# pragma clang diagnostic ignored "-Wconversion"
46# pragma clang diagnostic ignored "-Wdouble-promotion"
47#endif
48
49// 如果代码是在Microsoft Visual C++编译器下编译的
50#ifdef _MSC_VER
51 // 保存当前的警告状态,以便稍后恢复
52 #pragma warning(push)
53
54 // 禁用警告4127:条件表达式是常量。这通常发生在if语句的条件始终为真或始终为假时。
55 #pragma warning(disable: 4127)
56
57 // 禁用警告4324:结构体由于使用了__declspec(align())进行了填充。这通常是为了满足特定的对齐要求。
58 #pragma warning(disable: 4324)
59
60 // 禁用警告4702:不可达代码。这通常发生在代码路径在逻辑上不可能被执行到。
61 #pragma warning(disable: 4702)
62
63 // 禁用警告4996:这个函数或变量可能不安全。这通常用于标记那些被认为不安全的函数,如strcpy()等。
64 #pragma warning(disable: 4996)
65#endif
66
67// 如果代码是在Microsoft Visual C++编译器下编译,并且同时使用了Clang编译器前端(如Clang-Cl)
68#if defined(_MSC_VER) && defined(__clang__)
69 // 保存当前的Clang诊断状态,以便稍后恢复
70 #pragma clang diagnostic push
71
72 // 忽略警告-Wdeprecated:这个函数或变量可能不安全。这与4996警告类似,但由Clang发出。
73 #pragma clang diagnostic ignored "-Wdeprecated"
74#endif
75
76// 如果代码是在Intel C++编译器下编译的
77#ifdef __INTEL_COMPILER
78 // 禁用警告177:函数已被声明,但从未被引用。
79 #pragma warning(disable: 177)
80
81 // 禁用警告279:控制表达式是常量。这与4127警告类似。
82 #pragma warning(disable: 279)
83
84 // 禁用警告1478和1786:函数被声明为‘弃用’。这表示函数或变量在未来的版本中可能会被移除。
85 #pragma warning(disable: 1478 1786)
86
87 // 禁用警告1684:从指针转换为同等大小的整型类型。这通常发生在指针和整型大小相同但类型不同的转换中。
88 #pragma warning(disable: 1684)
89#endif
90
91// 如果代码是在Borland C++编译器下编译,并且定义了PUGIXML_HEADER_ONLY宏
92#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)
93 // 禁用警告8080:符号已被声明但从未被使用。这通常发生在定义了但未使用的变量或函数上。
94 #pragma warn -8080
95#endif
96
97#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)
98# pragma warn -8080 //符号已被声明但从未被使用;即便在入栈 / 出栈括号内禁用(相关设置),该警告也不会消失
99#endif
100
101#ifdef __BORLANDC__
102# pragma option push
103# pragma warn -8008 //条件始终为假
104# pragma warn -8066 //不可达代码
105#endif
106
107#ifdef __SNC__
108// 由于编译器的一个漏洞,使用 diag_push 和 diag_pop 无法禁用模板内部的警告。
109# pragma diag_suppress=178 // 函数已声明但从未被引用
110# pragma diag_suppress=237 // 控制表达式是常量
111#endif
112
113#ifdef __TI_COMPILER_VERSION__
114# pragma diag_suppress 179 // 函数已被声明,但从未被引用
115#endif
116
117//内联控制
118#if defined(_MSC_VER) && _MSC_VER >= 1300
119# define PUGI__NO_INLINE __declspec(noinline)
120#elif defined(__GNUC__)
121# define PUGI__NO_INLINE __attribute__((noinline))
122#else
123# define PUGI__NO_INLINE
124#endif
125
126//分支权重控制
127#if defined(__GNUC__) && !defined(__c2__)
128# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0)
129#else
130# define PUGI__UNLIKELY(cond) (cond)
131#endif
132
133//简单静态断言
134#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
135
136// Digital Mars C++ bug workaround for passing char loaded from memory via stack
137#ifdef __DMC__
138# define PUGI__DMC_VOLATILE volatile
139#else
140# define PUGI__DMC_VOLATILE
141#endif
142
143// 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
144#if defined(__clang__) && defined(__has_attribute)
145# if __has_attribute(no_sanitize)
146# define PUGI__UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
147# else
148# define PUGI__UNSIGNED_OVERFLOW
149# endif
150#else
151# define PUGI__UNSIGNED_OVERFLOW
152#endif
153
154// 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)
155#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)
156using std::memcpy;
157using std::memmove;
158using std::memset;
159#endif
160
161// Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations
162#if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX)
163# define LLONG_MIN (-LLONG_MAX - 1LL)
164# define LLONG_MAX __LONG_LONG_MAX__
165# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
166#endif
167
168// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features
169#if defined(_MSC_VER) && !defined(__S3E__)
170# define PUGI__MSVC_CRT_VERSION _MSC_VER
171#endif
172
173// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size.
174#if __cplusplus >= 201103
175# define PUGI__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
176#elif defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
177# define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__)
178#else
179# define PUGI__SNPRINTF sprintf
180#endif
181
182// 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.
183#ifdef PUGIXML_HEADER_ONLY
184# define PUGI__NS_BEGIN namespace pugi { namespace impl {
185# define PUGI__NS_END } }
186# define PUGI__FN inline
187# define PUGI__FN_NO_INLINE inline
188#else
189# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces
190# define PUGI__NS_BEGIN namespace pugi { namespace impl {
191# define PUGI__NS_END } }
192# else
193# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace {
194# define PUGI__NS_END } } }
195# endif
196# define PUGI__FN
197# define PUGI__FN_NO_INLINE PUGI__NO_INLINE
198#endif
199
200// uintptr_t
201#if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561)
202namespace pugi
203{
204# ifndef _UINTPTR_T_DEFINED
205 typedef size_t uintptr_t;
206# endif
207
208 typedef unsigned __int8 uint8_t;
209 typedef unsigned __int16 uint16_t;
210 typedef unsigned __int32 uint32_t;
211}
212#else
213# include <stdint.h>
214#endif
215
216// Memory allocation
217// PUGI__NS_BEGIN 和 PUGI__NS_END 宏通常用于定义命名空间的开始和结束
219// 定义一个函数,用于分配指定大小的内存。这个函数简单地调用了标准库的 malloc 函数
220PUGI__FN void* default_allocate(size_t size)
221{
222 return malloc(size);
223}
224// 定义一个函数,用于释放之前分配的内存。这个函数简单地调用了标准库的 free 函数
226{
227 free(ptr);
228}
229// 定义一个模板结构体,用于存储内存管理函数(分配和释放)。
230template <typename T>
232{
233 static allocation_function allocate;
234 static deallocation_function deallocate;
235};
236// 为模板结构体的静态成员变量分配默认值。
237// 这里使用了模板特化的语法来指定每个类型T的 allocate 和 deallocate 成员变量的值
238// Global allocation functions are stored in class statics so that in header mode linker deduplicates them
239// Without a template<> we'll get multiple definitions of the same static
240template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
241template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
242// 使用typedef为 xml_memory_management_function_storage<int> 创建一个别名 xml_memory
243// 专门用于int类型的内存管理函数存储
246
247// String utilities
249// Get string length
250// 获取字符串长度
251// char_t 是一个类型别名,它根据是否定义了 PUGIXML_WCHAR_MODE 宏来决定是 char 还是 wchar_t 类型
252PUGI__FN size_t strlength(const char_t* s)
253{
254 // 断言 s 不为 nullptr,确保传入的字符串指针是有效的
255 assert(s);
256 // 根据是否定义了 PUGIXML_WCHAR_MODE 宏来选择使用 wcslen 还是 strlen 函数来获取字符串长度
257#ifdef PUGIXML_WCHAR_MODE
258 return wcslen(s);// 宽字符字符串长度
259#else
260 return strlen(s);// 单字节字符字符串长度
261#endif
262}
263
264// Compare two strings
265PUGI__FN bool strequal(const char_t* src, const char_t* dst)
266{
267 // 断言 src 和 dst 都不为 nullptr,确保传入的字符串指针都是有效的
268 assert(src && dst);
269
270#ifdef PUGIXML_WCHAR_MODE
271 return wcscmp(src, dst) == 0;// 宽字符字符串比较
272#else
273 return strcmp(src, dst) == 0;// 单字节字符字符串比较
274#endif
275 }
276
277// Compare lhs with [rhs_begin, rhs_end)
278PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)
279{
280 // 逐个字符比较 lhs 和 rhs,如果发现有不相等的字符,则返回 false
281 for (size_t i = 0; i < count; ++i)
282 if (lhs[i] != rhs[i])
283 return false;
284 // 如果循环结束后没有发现不相等的字符,则检查 lhs 的第 count 个字符是否为空终止符,
285 // 如果是,则返回 true,表示字符串相等(在指定范围内);否则返回 false
286 return lhs[count] == 0;
287}
288
289// Get length of wide string, even if CRT lacks wide character support
290PUGI__FN size_t strlength_wide(const wchar_t* s)
291{
292 // 断言 s 不为 nullptr,确保传入的宽字符串指针是有效的
293 assert(s);
294 // 如果定义了 PUGIXML_WCHAR_MODE 宏,则直接使用 wcslen 函数获取长度
295#ifdef PUGIXML_WCHAR_MODE
296 return wcslen(s);
297#else
298 // 如果没有定义 PUGIXML_WCHAR_MODE 宏,则手动计算长度。
299 // 通过遍历字符串直到遇到空终止符来计算长度
300 const wchar_t* end = s;
301 while (*end) end++;
302 return static_cast<size_t>(end - s);// 返回字符串的长度
303#endif
304}
306
307// auto_ptr-like object for exception recovery
309// 定义一个模板结构体 auto_deleter,它可以用于任何类型 T
310template <typename T> struct auto_deleter
311{
312 // 定义一个类型别名 D,它是一个指向函数的指针,该函数接受一个指向 T 类型的指针作为参数,并返回 void
313 typedef void (*D)(T*);
314 // 成员变量:
315 // data:指向需要被自动删除的对象的指针。
316 // deleter:一个函数指针,指向用于删除 data 指向的对象的函数
319 // 构造函数,接受一个指向对象的指针和一个删除该对象的函数指针
320 auto_deleter(T* data_, D deleter_) : data(data_), deleter(deleter_)
321 {
322 }
323 // 析构函数,当 auto_deleter 对象被销毁时调用。
324 // 如果 data 非空,则调用 deleter 函数来删除 data 指向的对象
326 {
327 if (data) deleter(data);
328 }
329 // release 函数,用于手动释放对对象的所有权。
330 // 它返回 data 指向的对象的指针,并将 data 设置为 0(nullptr)。
331 // 这意味着之后 auto_deleter 对象不会再尝试删除该对象
333 {
334 T* result = data;
335 data = 0;
336 return result;
337 }
338};
340
341#ifdef PUGIXML_COMPACT
343 // 定义了一个名为 compact_hash_table 的类,用于实现紧凑的哈希表
344 class compact_hash_table
345 {
346 public:
347 // 类的构造函数,初始化哈希表的项指针为0,容量和计数也为0
348 compact_hash_table(): _items(0), _capacity(0), _count(0)
349 {
350 }
351 // 清除哈希表的方法。释放已分配的内存,并将容量和计数重置为0
352 void clear()
353 {
354 // 如果已分配了内存给哈希表的项
355 if (_items)
356 {
357 // 使用 xml_memory::deallocate 方法释放内存
359 // 将项指针、容量和计数重置为0
360 _items = 0;
361 _capacity = 0;
362 _count = 0;
363 }
364 }
365 // 根据给定的键查找值的方法
366 void* find(const void* key)
367 {
368 // 如果哈希表的容量为0,表示哈希表为空,直接返回0(表示未找到
369 if (_capacity == 0) return 0;
370 // 调用 get_item 方法根据键获取对应的项(这个方法在给出的代码中没有定义,可能是类的私有方法)
371 item_t* item = get_item(key);
372 // 使用 assert 断言确保获取的项不为空,且项的键要么与给定的键相等,要么是一个空项(键和值都为0)
373 assert(item);
374 assert(item->key == key || (item->key == 0 && item->value == 0));
375 // 返回找到的项的值
376 return item->value;
377 }
378 // 插入一个键值对到哈希表中
379 void insert(const void* key, void* value)
380 {
381 // 断言哈希表的容量不为0,并且当前存储的项的数量小于容量的3/4(为了保持哈希表的负载因子在一个合理的范围内)
382 assert(_capacity != 0 && _count < _capacity - _capacity / 4);
383 // 调用get_item方法根据键获取哈希表中的项(这个方法在给出的代码中没有定义,可能是类的私有方法)
384 item_t* item = get_item(key);
385 // 断言确保获取的项不为空
386 assert(item);
387 // 如果找到的项的键为空,表示这是一个空槽,可以插入新的键值对
388 if (item->key == 0)
389 {
390 // 增加哈希表中当前存储的项的数量
391 _count++;
392 // 将键设置为给定的键
393 item->key = key;
394 }
395 // 无论是否是新插入的项,都将值设置为给定的值(如果键已经存在,则更新值)
396 item->value = value;
397 }
398 // 为哈希表预留额外的空间(默认是16个单位)
399 bool reserve(size_t extra = 16)
400 {
401 // 如果加上额外空间后的项的数量大于等于当前容量的3/4,则需要重新哈希以扩展容量
402 if (_count + extra >= _capacity - _capacity / 4)
403 // 调用rehash方法重新分配内存并重新插入所有项(这个方法在给出的代码中没有定义,可能是类的私有方法)
404 // 如果rehash方法返回true,表示重新哈希成功;否则,表示失败(尽管在这个实现中它总是返回true或false,但具体取决于rehash的实现)
405 return rehash(_count + extra);
406 return rehash(_count + extra);
407 // 如果不需要重新哈希,则直接返回true
408 return true;
409 }
410 // 私有成员定义开始
411 private:
412 // 定义一个结构体item_t,用于存储哈希表中的项
413 struct item_t
414 {
415 const void* key;// 指向键的指针,使用void*表示可以是任意类型的键
416 void* value; // 指向值的指针,使用void*表示可以是任意类型的值
417 };
418 // 指向item_t数组的指针,用于存储哈希表中的所有项
419 item_t* _items;
420 // 哈希表的容量,即_items数组可以存储的项的最大数量
421 size_t _capacity;
422 // 当前哈希表中存储的项的数量
423 size_t _count;
424 // 一个成员函数,用于在需要时重新调整哈希表的大小
425 bool rehash(size_t count);
426 // 一个成员函数,用于根据给定的键查找对应的项
427 item_t* get_item(const void* key)
428 {
429 // 确保传入的键不为空
430 assert(key);
431 // 确保哈希表的容量大于0
432 assert(_capacity > 0);
433 // 计算哈希表容量的掩码,用于将哈希值限制在哈希表的范围内
434 size_t hashmod = _capacity - 1;
435 // 计算给定键的初始桶(bucket)位置
436 size_t bucket = hash(key) & hashmod;
437 // 使用二次探测法解决哈希冲突
438 for (size_t probe = 0; probe <= hashmod; ++probe)
439 {
440 // 引用当前桶中的项
441 item_t& probe_item = _items[bucket];
442 // 如果找到了键匹配的项,或者该位置为空(表示哈希表中没有更多的项),则返回该项的指针
443 if (probe_item.key == key || probe_item.key == 0)
444 return &probe_item;
445
446 // hash collision, quadratic probing
447 // 计算下一个探测的位置,使用二次探测法
448 bucket = (bucket + probe + 1) & hashmod;
449 }
450 // 断言失败,表示哈希表已满(理论上应该不可达,因为哈希表在满之前会进行扩容)
451 assert(false && "Hash table is full"); // unreachable
452 // 如果上面的断言失败,则返回0(实际上由于断言的存在,这行代码不会被执行)
453 return 0;
454 }
455 // 定义一个静态函数,用于计算给定键的哈希值,并处理可能的无符号整数溢出
456 static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key)
457 {
458 // 将指针转换为无符号整数类型(uintptr_t),然后转换为unsigned int。
459 // 这里的转换是为了获取一个可以用于哈希计算的数值。
460 unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key));
461
462 // MurmurHash3 32-bit finalizer
463 h ^= h >> 16; // 将h右移16位后与自身异或
464 h *= 0x85ebca6bu; // 乘以一个魔术数字(常数)
465 h ^= h >> 13; // 再次右移13位后与自身异或
466 h *= 0xc2b2ae35u; // 再次乘以另一个魔术数字
467 h ^= h >> 16; // 最后,再次右移16位后与自身异或
468
469 return h;
470 }
471 };
472 // 定义一个函数,用于根据当前存储的元素数量重新调整哈希表的容量
473 PUGI__FN_NO_INLINE bool compact_hash_table::rehash(size_t count)
474 {
475 // 初始化容量为32
476 size_t capacity = 32;
477 // 当当前元素数量大于等于当前容量的3/4时,将容量翻倍,直到满足条件
478 while (count >= capacity - capacity / 4)
479 capacity *= 2;
480 // 创建一个新的哈希表实例
481 compact_hash_table rt;
482 // 设置新哈希表的容量为计算得到的新容量
483 rt._capacity = capacity;
484 // 为新哈希表分配内存空间
485 rt._items = static_cast<item_t*>(xml_memory::allocate(sizeof(item_t) * capacity));
486 // 如果内存分配失败,则返回false
487 if (!rt._items)
488 return false;
489 // 将新分配的内存空间初始化为0
490 memset(rt._items, 0, sizeof(item_t) * capacity);
491 // 遍历旧哈希表的每一项,如果键不为空,则将其插入到新哈希表中
492 for (size_t i = 0; i < _capacity; ++i)
493 if (_items[i].key)
494 rt.insert(_items[i].key, _items[i].value);
495 // 如果旧哈希表有已分配的内存,则释放它
496 if (_items)
498 // 更新旧哈希表的容量为新的容量,并设置其项指针为新分配的内存
499 _capacity = capacity;
500 _items = rt._items;
501 // 断言确保重新哈希后元素数量未变。
502 assert(_count == rt._count);
503 // 返回true,表示重新哈希成功
504 return true;
505 }
506
507// PUGI__NS_END 和 PUGI__NS_BEGIN 是命名空间结束和开始的宏,用于创建一个命名空间。
508// 这样可以避免全局命名冲突,并组织代码结构。
510#endif
511
513#ifdef PUGIXML_COMPACT
514 // 在紧凑模式下,内存块对齐的字节数为4。
515 static const uintptr_t xml_memory_block_alignment = 4;
516#else
517 // 在非紧凑模式下,内存块对齐的字节数为指针大小,通常是4字节或8字节,取决于平台。
518 static const uintptr_t xml_memory_block_alignment = sizeof(void*);
519#endif
520
521 // 以下是一些额外的元数据位掩码,用于标记XML页面的特定属性。
522 static const uintptr_t xml_memory_page_contents_shared_mask = 64; // 内容是否共享
523 static const uintptr_t xml_memory_page_name_allocated_mask = 32; // 名称是否已分配
524 static const uintptr_t xml_memory_page_value_allocated_mask = 16; // 值是否已分配
525 static const uintptr_t xml_memory_page_type_mask = 15; // 节点类型掩码
526
527 // 组合掩码,用于字符串的唯一性检查。
530
531#ifdef PUGIXML_COMPACT
532 #define PUGI__GETHEADER_IMPL(object, page, flags)// 在紧凑模式下,以下宏定义为空,因为它们不被使用。
533 #define PUGI__GETPAGE_IMPL(header) (header).get_page()
534#else
535 // 在非紧凑模式下,定义了如何从对象获取其头部信息和页面信息的宏。
536 // PUGI__GETHEADER_IMPL 宏用于计算对象头部信息,包括对象相对于页面的位置和标志。
537 #define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast<char*>(object) - reinterpret_cast<char*>(page)) << 8) | (flags))
538 // PUGI__GETPAGE_IMPL 宏用于从头部信息中获取页面指针。
539 // 这个宏通过void*类型转换指针,以避免增加目标类型的对齐要求的警告。
540 #define PUGI__GETPAGE_IMPL(header) static_cast<impl::xml_memory_page*>(const_cast<void*>(static_cast<const void*>(reinterpret_cast<const char*>(&header) - (header >> 8))))
541#endif
542
543 // PUGI__GETPAGE 宏用于从节点中获取页面。
544 #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header)
545 // PUGI__NODETYPE 宏用于从节点中获取节点类型。
546 #define PUGI__NODETYPE(n) static_cast<xml_node_type>((n)->header & impl::xml_memory_page_type_mask)
547 struct xml_allocator;
548 // 定义xml_memory_page结构体,用于管理内存页
550 {
551 // 静态成员函数,用于在给定内存块上构造xml_memory_page对象
552 // 这个函数不分配新的内存,而是将传入的void*内存块转换为xml_memory_page*并初始化它
553 static xml_memory_page* construct(void* memory)
554 {
555 // 将传入的void*内存块转换为xml_memory_page*类型
556 xml_memory_page* result = static_cast<xml_memory_page*>(memory);
557 // 初始化成员变量
558 result->allocator = 0; // 指向xml_allocator的指针,初始化为0(空指针)
559 result->prev = 0; // 指向前一个内存页的指针,初始化为0(空指针)
560 result->next = 0; // 指向下一个内存页的指针,初始化为0(空指针)
561 result->busy_size = 0; // 当前页中已分配(忙碌)的内存大小,初始化为0
562 result->freed_size = 0; // 当前页中已释放(空闲)的内存大小,初始化为0
563 // 如果定义了PUGIXML_COMPACT宏,则初始化与紧凑模式相关的成员变量
564 #ifdef PUGIXML_COMPACT
565 result->compact_string_base = 0;
566 result->compact_shared_parent = 0;
567 result->compact_page_marker = 0;
568 #endif
569 // 返回构造并初始化后的xml_memory_page对象的指针
570 return result;
571 }
572 // 成员变量
574 // 内存页之间的双向链表链接
577 // 内存使用情况统计
578 size_t busy_size;
580 // 注意:如果定义了PUGIXML_COMPACT宏,则还会存在以下成员变量
581 // 这些变量在紧凑模式下用于优化内存使用和访问速度
582 #ifdef PUGIXML_COMPACT
583 char_t* compact_string_base;
584 void* compact_shared_parent;
585 uint32_t* compact_page_marker;
586 #endif
587 };
588
589 static const size_t xml_memory_page_size =
590 #ifdef PUGIXML_MEMORY_PAGE_SIZE
591 (PUGIXML_MEMORY_PAGE_SIZE)
592 #else
593 32768
594 #endif
595 - sizeof(xml_memory_page);
596 // 定义xml_memory_string_header结构体,用于描述字符串在内存页中的位置和大小
598 {
599 // 从内存页数据起始位置到字符串起始位置的偏移量
600 uint16_t page_offset; // offset from page->data
601 // 如果字符串占据整个内存页,则为0;否则为字符串的实际大小
602 uint16_t full_size; // 0 if string occupies whole page
603 };
604 // 定义xml_allocator结构体,用于管理内存页的分配
606 {
607 // 构造函数,接受一个指向根内存页的指针,并初始化成员变量
608 xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)
609 {
610 #ifdef PUGIXML_COMPACT
611 _hash = 0;// 如果定义了PUGIXML_COMPACT宏,则初始化_hash为0
612 #endif
613 }
614 // 成员函数:分配一个新的内存页
615 // 接受一个数据大小参数,用于确定除了结构体本身外还需要多少额外空间
617 {
618 // 计算总大小:内存页结构体的大小加上额外数据的大小
619 size_t size = sizeof(xml_memory_page) + data_size;
620
621 // allocate block with some alignment, leaving memory for worst-case padding
622 void* memory = xml_memory::allocate(size);
623 if (!memory) return 0;// 如果分配失败,则返回0(空指针)
624
625 // prepare page structure
626 // 在分配的内存块上构造xml_memory_page对象
628 assert(page);// 确保构造成功
629 // 设置新页面的分配器为根页面的分配器(可能是为了保持一致性或实现某种内存管理策略)
630 page->allocator = _root->allocator;
631 // 返回新分配的页面
632 return page;
633 }
634 // 定义deallocate_page函数,用于释放一个内存页
635 // 接受一个指向xml_memory_page对象的指针作为参数
637 {
638 // 调用xml_memory类的静态成员函数deallocate来释放内存页
639 // 假设xml_memory是一个管理内存分配的类,类似于标准库中的allocator
641 }
642 // 它用于在内存不足时分配内存,可能是通过分配一个新的内存页或其他机制
643 void* allocate_memory_oob(size_t size, xml_memory_page*& out_page);
644 // 定义allocate_memory函数,尝试在当前根内存页中分配指定大小的内存
645 // 如果内存不足,则调用allocate_memory_oob函数
646 void* allocate_memory(size_t size, xml_memory_page*& out_page)
647 {
648 // 使用PUGI__UNLIKELY宏来提示编译器这个条件可能不常发生
649 // 这有助于优化生成的代码(尽管这取决于编译器的实现)
651 // 如果当前根内存页中的已分配大小加上请求的大小超过了内存页的总大小
652 // 则调用allocate_memory_oob函数来分配内存,并传递请求的大小和out_page引用
653 return allocate_memory_oob(size, out_page);
654 // 计算分配内存的起始地址
655 // _root是指向当前根内存页的指针,sizeof(xml_memory_page)是内存页结构体的大小
656 // _busy_size是当前已分配(忙碌)的内存大小
657 void* buf = reinterpret_cast<char*>(_root) + sizeof(xml_memory_page) + _busy_size;
658 // 更新已分配(忙碌)的内存大小
659 _busy_size += size;
660 // 将out_page引用设置为指向当前根内存页的指针
661 out_page = _root;
662 // 返回分配的内存的起始地址
663 return buf;
664 }
665
666 #ifdef PUGIXML_COMPACT
667 // allocate_object 函数用于在紧凑模式下分配指定大小的对象,并返回指向分配的内存的指针。
668 // 同时,它还更新 out_page 参数以指向包含该内存的对象页。
669 void* allocate_object(size_t size, xml_memory_page*& out_page)
670 {
671 // 首先,调用 allocate_memory 函数分配比请求大小多出 uint32_t 大小的内存块。
672 // 额外的空间用于存储一个标记,用于跟踪内存页的元数据。
673 void* result = allocate_memory(size + sizeof(uint32_t), out_page);
674 if (!result) return 0; // 如果内存分配失败,则返回 nullptr。
675
676 // 计算 result 指针与页标记之间的偏移量。
677 ptrdiff_t offset = static_cast<char*>(result) - reinterpret_cast<char*>(out_page->compact_page_marker);
678
679 // 如果偏移量超过了一个阈值(意味着 result 距离页标记足够远),则需要插入一个新的标记。
680 if (PUGI__UNLIKELY(static_cast<uintptr_t>(offset) >= 256 * xml_memory_block_alignment))
681 {
682 // 将 result 指针转换为 uint32_t 指针,并设置标记。
683 uint32_t* marker = static_cast<uint32_t*>(result);
684 *marker = static_cast<uint32_t>(reinterpret_cast<char*>(marker) - reinterpret_cast<char*>(out_page));
685 out_page->compact_page_marker = marker; // 更新页的标记指针。
686
687 // 因为我们不会重用页空间直到重新分配它,所以我们可以将标记块视为已释放。
688 // 这将确保 deallocate_memory 正确地跟踪大小。
689 out_page->freed_size += sizeof(uint32_t); // 更新已释放的大小。
690
691 return marker + 1; // 返回指向标记后的对象内存的指针。
692 }
693 else
694 {
695 // 如果不需要插入新标记,则回滚 uint32_t 的部分,并更新 busy_size。
696 _busy_size -= sizeof(uint32_t);
697
698 return result; // 返回原始的内存分配指针。
699 }
700 }
701 #else
702 // 定义一个函数,用于分配指定大小的内存对象,并返回指向该对象的指针。
703// 同时,通过out_page参数返回该对象所在的内存页。
704 void* allocate_object(size_t size, xml_memory_page*& out_page)
705 {
706 // 调用另一个函数allocate_memory来实际进行内存分配,并返回分配的指针。
707 return allocate_memory(size, out_page);
708 }
709#endif
710 // 定义一个函数,用于释放之前分配的内存
711 void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
712 {
713 // 如果当前释放的内存页是根页(_root),则更新根页的忙碌大小(_busy_size)。
714 if (page == _root) page->busy_size = _busy_size;
715 // 断言检查,确保指针ptr指向的内存确实位于page所管理的内存范围内
716 assert(ptr >= reinterpret_cast<char*>(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast<char*>(page) + sizeof(xml_memory_page) + page->busy_size);
717 (void)!ptr;
718 // 更新页面已释放内存的大小
719 page->freed_size += size;
720 // 断言检查,确保已释放的内存大小不会超过该页面的忙碌大小
721 assert(page->freed_size <= page->busy_size);
722 // 如果整个页面的内存都已释放,则进行清理操作
723 if (page->freed_size == page->busy_size)
724 {
725 // 如果这是最后一个页面(即没有下一个页面),
726 if (page->next == 0)
727 {
728 // 断言检查,确保这是根页面。
729 assert(_root == page);
730
731 // 如果是顶部页面被释放,则重置其大小。
732 // top page freed, just reset sizes
733 page->busy_size = 0;
734 page->freed_size = 0;
735
736#ifdef PUGIXML_COMPACT
737 // reset compact state to maximize efficiency
738 page->compact_string_base = 0;
739 page->compact_shared_parent = 0;
740 page->compact_page_marker = 0;
741#endif
742 // 重置全局忙碌大小。
743 _busy_size = 0;
744 }
745 else
746 {
747 // 断言检查,确保这不是根页面,并且它有前一个页面。
748 assert(_root != page);
749 assert(page->prev);
750 // 从页面中移除该页面(假设页面存储在一个双向链表中)
751 // remove from the list
752 page->prev->next = page->next;
753 page->next->prev = page->prev;
754
755 // 释放该页面。
756 // deallocate
757 deallocate_page(page);
758 }
759 }
760 }
761 // allocate_string函数用于分配一段内存空间
762 char_t* allocate_string(size_t length)
763 { // 定义一个静态常量,表示最大编码偏移量。
764 static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment;
765 // 这是一种在编译期间进行条件检查的方式,
766 PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset);
767 // 计算为存储字符串以及相关头部信息(可能用于管理字符串内存块的一些元数据)所需分配的内存大小。
768 // allocate memory for string and header block
769 size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);
770
771 // round size up to block alignment boundary
772 size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1);
773
774 xml_memory_page* page;
775 xml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page));
776
777 if (!header) return 0;
778
779 // setup header
780 ptrdiff_t page_offset = reinterpret_cast<char*>(header) - reinterpret_cast<char*>(page) - sizeof(xml_memory_page);
781
782 assert(page_offset % xml_memory_block_alignment == 0);
783 assert(page_offset >= 0 && static_cast<size_t>(page_offset) < max_encoded_offset);
784 header->page_offset = static_cast<uint16_t>(static_cast<size_t>(page_offset) / xml_memory_block_alignment);
785
786 // full_size == 0 for large strings that occupy the whole page
787 assert(full_size % xml_memory_block_alignment == 0);
788 assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0));
789 header->full_size = static_cast<uint16_t>(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0);
790
791 // round-trip through void* to avoid 'cast increases required alignment of target type' warning
792 // header is guaranteed a pointer-sized alignment, which should be enough for char_t
793 return static_cast<char_t*>(static_cast<void*>(header + 1));
794 }
795
796 void deallocate_string(char_t* string)
797 {
798 // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
799 // 此函数通过将指针转换为 void* 类型来避免出现“cast increases required alignment of target type”这样的警告
800 // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string
801 // 前提是如果输入的字符串是通过 allocate_string 函数分配的,那么我们能保证输入字符串具有合适的(指针大小的)对齐方式
802 // get header
803 // 将传入的字符串指针先转换为 void* 类型,再将其转换回 xml_memory_string_header* 类型,并向前偏移一个单位(减1操作),目的是获取指向该字符串头部信息的指针。
804 // 这里假设内存布局中字符串头部信息就在字符串实际数据的前面,通过这样的指针运算来获取相关管理信息。
805 xml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1;
806 assert(header);
807
808 // deallocate
809 // 计算此字符串所在内存页内的偏移量。先加上 xml_memory_page 结构体的大小,再根据字符串头部记录的页内偏移量(乘以每个内存块的对齐大小)来确定其在内存页中的准确偏移位置。
810 // 这样后续就能通过这个偏移量找到对应的内存页。
811 size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment;
812 // 通过先将 header 指针转换为 char* 类型(方便进行字节级别的偏移计算),减去前面计算得到的页偏移量,再转换回 xml_memory_page* 类型,从而得到该字符串所在的内存页指针。
813 xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));
814 // 如果 full_size 等于 0,说明这个字符串占据了整个内存页,否则按照头部记录的实际大小(乘以内存块对齐大小,可能涉及到内存对齐相关的换算)来确定字符串实际占用的内存大小。
815 // if full_size == 0 then this string occupies the whole page
816 size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment;
817
818 deallocate_memory(header, full_size, page);
819 }
820
821 bool reserve()
822 {
823 #ifdef PUGIXML_COMPACT
824 return _hash->reserve();
825 #else
826 return true;
827 #endif
828 }
829
832
833 #ifdef PUGIXML_COMPACT
834 compact_hash_table* _hash;
835 #endif
836 };
837 // 该函数用于在内存不足(out of bounds,简称OOB)的情况下分配内存。
838 // 参数size指定了要分配的内存大小,out_page通过引用返回分配的内存所在的页面。
840 {
841 // 定义一个阈值,用于区分“大”分配和“小”分配。
842 // 这里,大分配是指超过页面大小四分之一的分配。
843 const size_t large_allocation_threshold = xml_memory_page_size / 4;
844 // 根据分配大小分配一个页面。如果分配大小超过阈值,则分配一个足够大的页面
845 xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size);
846 // 通过引用参数返回分配的页面
847 out_page = page;
848 // 如果页面分配失败(即page为nullptr),则返回nullptr表示分配失败。
849 if (!page) return 0;
850 // 如果分配大小小于或等于阈值,则执行以下操作
851 if (size <= large_allocation_threshold)
852 {
853 // 更新根页面的忙碌大小为全局忙碌大小
855
856 // insert page at the end of linked list
857 page->prev = _root;
858 _root->next = page;
859 // 更新_root指针,使其指向新插入的页面。
860 _root = page;
861 // 更新全局忙碌大小为当前分配的大小。
862 _busy_size = size;
863 }
864 else
865 {
866 // insert page before the end of linked list, so that it is deleted as soon as possible
867 // the last page is not deleted even if it's empty (see deallocate_memory)
868 // 对于大分配,将页面插入到链表末尾之前的位置。
869 assert(_root->prev);
870 // 插入新页面到链表中。
871 page->prev = _root->prev;
872 page->next = _root;
873
874 _root->prev->next = page;
875 _root->prev = page;
876 // 设置新页面的忙碌大小为当前分配的大小
877 page->busy_size = size;
878 }
879 // 返回指向页面内部数据的指针(跳过页面头部)。
880 return reinterpret_cast<char*>(page) + sizeof(xml_memory_page);
881 }
883
884#ifdef PUGIXML_COMPACT
885// 开始命名空间(假设PUGI__NS_BEGIN是一个宏,用于定义或进入特定的命名空间)
887 // 定义紧凑对齐的log2值,这里为2,意味着对齐是2的2次方,即4字节对齐
888 static const uintptr_t compact_alignment_log2 = 2;
889 // 根据log2值计算紧凑对齐的实际值,这里是1 << 2 = 4
890 static const uintptr_t compact_alignment = 1 << compact_alignment_log2;
891 // 定义一个紧凑头部类,用于管理紧凑内存页的头部信息
892 class compact_header
893 {
894 public:
895 // 构造函数,接收一个指向xml_memory_page的指针和一个标志位
896 compact_header(xml_memory_page* page, unsigned int flags)
897 {
898 // 静态断言,确保xml_memory_block_alignment与compact_alignment相等
900 // 计算当前对象相对于page->compact_page_marker的偏移量
901 ptrdiff_t offset = (reinterpret_cast<char*>(this) - reinterpret_cast<char*>(page->compact_page_marker));
902 // 断言偏移量是对齐的,并且小于256个对齐单位
903 assert(offset % compact_alignment == 0 && static_cast<uintptr_t>(offset) < 256 * compact_alignment);
904 // 计算并存储页面索引
905 _page = static_cast<unsigned char>(offset >> compact_alignment_log2);
906 // 存储标志位
907 _flags = static_cast<unsigned char>(flags);
908 }
909 // 位与赋值操作符,用于修改标志位
910 void operator&=(uintptr_t mod)
911 {
912 _flags &= static_cast<unsigned char>(mod);
913 }
914 // 位或赋值操作符,用于修改标志位
915 void operator|=(uintptr_t mod)
916 {
917 _flags |= static_cast<unsigned char>(mod);
918 }
919 // 位与操作符,用于获取标志位与给定值的交集
920 uintptr_t operator&(uintptr_t mod) const
921 {
922 return _flags & mod;
923 }
924 // 获取关联的xml_memory_page对象
925 xml_memory_page* get_page() const
926 {
927 // round-trip through void* to silence 'cast increases required alignment of target type' warnings
928 const char* page_marker = reinterpret_cast<const char*>(this) - (_page << compact_alignment_log2);
929 const char* page = page_marker - *reinterpret_cast<const uint32_t*>(static_cast<const void*>(page_marker));
930 // 返回指向xml_memory_page的指针
931 return const_cast<xml_memory_page*>(reinterpret_cast<const xml_memory_page*>(static_cast<const void*>(page)));
932 }
933 // 紧凑头部类的私有成员变量定义
934 private:
935 // 存储页面索引的变量,用于标识当前对象所在的内存页
936 unsigned char _page;
937 // 存储标志位的变量,用于记录一些额外的信息或状态
938 unsigned char _flags;
939 };
940 // 一个函数,用于根据给定的对象和头部偏移量获取对应的xml_memory_page
941 // 指向xml_memory_page的指针,该页面包含了object
942 PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset)
943 {
944 // 将object指针向前移动header_offset,得到compact_header的指针
945 const compact_header* header = reinterpret_cast<const compact_header*>(static_cast<const char*>(object) - header_offset);
946 // 调用compact_header的get_page方法获取xml_memory_page指针
947 return header->get_page();
948 }
949 // 一个模板函数,用于根据给定的对象和头部偏移量获取对应的值
950 // 指向T类型的指针,该值通过哈希表查找得到
951 template <int header_offset, typename T> PUGI__FN_NO_INLINE T* compact_get_value(const void* object)
952 {// 首先获取包含object的xml_memory_page,然后通过页面的allocator和_hash成员查找object对应的值
953 return static_cast<T*>(compact_get_page(object, header_offset)->allocator->_hash->find(object));
954 }
955 // 一个模板函数,用于根据给定的对象和头部偏移量设置对应的值
956 template <int header_offset, typename T> PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value)
957 {
958 // 首先获取包含object的xml_memory_page,然后通过页面的allocator和_hash成员插入object和value的对应关系
959 compact_get_page(object, header_offset)->allocator->_hash->insert(object, value);
960 }
961 // 一个模板类,用于管理紧凑指针,这些指针通过哈希表进行存储和查找
962 template <typename T, int header_offset, int start = -126> class compact_pointer
963 {
964 public:
965 // compact_pointer的默认构造函数,初始化_data成员为0
966 compact_pointer(): _data(0)
967 {
968 }
969 // 重载赋值运算符,用于将一个compact_pointer对象赋值给另一个
970 void operator=(const compact_pointer& rhs)
971 {
972 *this = rhs + 0;
973 }
974 // 重载赋值运算符,用于将一个原生指针赋值给compact_pointer对象
975 void operator=(T* value)
976 {
977 if (value)
978 {
979 // value is guaranteed to be compact-aligned; 'this' is not
980 // value变量是被确保按照紧凑对齐(compact-aligned)方式进行对齐的;而“this”指针所指向的对象却并非如此。
981 // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
982 // 这里所说的紧凑对齐应该是一种特定的内存对齐要求,意味着value的内存地址满足相应的对齐规则,便于后续某些操作(比如数据的读取、处理等)更高效地进行,而“this”指向的对象不符合该对齐规则。
983 // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
984 // 我们的解码操作是基于将“this”指针按照紧凑对齐方式向下对齐来进行的(可以参考 operator T* 相关部分,推测那里可能定义了具体的对齐操作或者转换逻辑)。
985 // compensate for arithmetic shift rounding for negative values
986 // 所以,当遇到负的偏移量(例如 -3)时,我们需要通过将偏移量的差值(diff)调整为 compact_alignment - 1。
987 ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
988 ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start;
989 // 如果计算出的偏移量(经过调整并转换为无符号后)小于等于253,
990 // 则将其加1后存储到_data中(因为0被保留用于空指针)
991 if (static_cast<uintptr_t>(offset) <= 253)
992 _data = static_cast<unsigned char>(offset + 1);
993 else
994 {
995 // compact_set_value是一个模板函数,header_offset是一个占位符,表示可能的头部偏移量
996 compact_set_value<header_offset>(this, value);
997
998 _data = 255;// 设置_data为255,表示指针值存储在外部
999 }
1000 }
1001 else
1002 // 如果赋值为nullptr,则将_data设置为0
1003 _data = 0;
1004 }
1005 // 定义一个类型转换运算符,将compact_pointer转换为T*
1006 operator T*() const
1007 {
1008 if (_data)// 如果_data不为0
1009 {
1010 if (_data < 255)// 如果_data的值小于255,表示指针值直接存储在_data中(经过编码)
1011 {
1012 // 计算基地址:将this指针向下对齐到compact_alignment的倍数
1013 uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
1014 // 根据_data计算偏移量,并加上start(可能是一个调整值),然后乘以compact_alignment得到实际地址
1015 // 最后,将基地址与计算出的偏移量相加,得到目标指针的地址,并转换为T*类型
1016 return reinterpret_cast<T*>(base + (_data - 1 + start) * compact_alignment);
1017 }
1018 else // 如果_data的值等于255,表示指针值存储在外部
1019 // 调用compact_get_value函数来获取存储在外部的指针值
1020 return compact_get_value<header_offset, T>(this);
1021 }
1022 else// 如果_data为0,表示空指针
1023 return 0;// 返回nullptr
1024 }
1025 // 定义一个成员访问运算符,允许通过compact_pointer对象直接访问目标对象的成员
1026 T* operator->() const
1027 {
1028 return *this;
1029 }
1030 // compact_pointer类的私有成员定义结束
1031 private:
1032 // 用于存储编码后的指针值或标记指针值是否存储在外部
1033 unsigned char _data;
1034 };
1035 // 定义一个模板类compact_pointer_parent,它可能是compact_pointer的基类或用于提供某些共享功能
1036 template <typename T, int header_offset> class compact_pointer_parent
1037 {
1038 // 类的构造函数,初始化成员变量_data为0
1039 public:
1040 compact_pointer_parent(): _data(0)
1041 {
1042 }
1043 // 拷贝赋值操作符
1044 void operator=(const compact_pointer_parent& rhs)
1045 {
1046 *this = rhs + 0;
1047 }
1048 // 赋值操作符重载,接受一个T*类型的参数
1049 void operator=(T* value)
1050 {
1051 if (value)
1052 {
1053 // value is guaranteed to be compact-aligned; 'this' is not
1054 // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
1055 // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
1056 // compensate for arithmetic shift behavior for negative values
1057 // 将value和this指针转换为char*,然后计算它们之间的差值diff
1058 ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
1059 ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533;
1060 // 如果计算出的offset在可存储范围内(即小于或等于65533),则直接存储为_data的值(加1是为了留出0表示null的特殊情况)
1061 if (static_cast<uintptr_t>(offset) <= 65533)
1062 {
1063 _data = static_cast<unsigned short>(offset + 1);
1064 }
1065 else
1066 {
1067 // 如果offset超出范围,则需要使用更复杂的内存管理机制
1068 // 首先,获取与this相关的内存页
1069 xml_memory_page* page = compact_get_page(this, header_offset);
1070 // 如果该页还没有共享父指针,则设置它
1071 if (PUGI__UNLIKELY(page->compact_shared_parent == 0))
1072 page->compact_shared_parent = value;
1073 // 如果共享父指针已经是value,则使用特殊值65534表示
1074 if (page->compact_shared_parent == value)
1075 {
1076 _data = 65534;
1077 }
1078 else
1079 {\
1080 // 否则,使用某种机制将value存储到内存页中,并将_data设置为特殊值65535
1081 compact_set_value<header_offset>(this, value);
1082
1083 _data = 65535;
1084 }
1085 }
1086 }
1087 else
1088 {
1089 // 如果value为nullptr,则将_data设置为0,表示null指针
1090 _data = 0;
1091 }
1092 }
1093
1094 operator T*() const
1095 {
1096 if (_data)// 如果_data非零
1097 {
1098 if (_data < 65534)// 如果_data的值小于65534
1099 {
1100 // 调整当前对象的地址,使其符合compact_alignment对齐要求
1101 uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
1102 // 根据_data的值计算实际存储T类型数据的地址,并返回该地址
1103 // 这里假设了一个特殊的存储策略,其中65533作为偏移量的基准点
1104 return reinterpret_cast<T*>(base + (_data - 1 - 65533) * compact_alignment);
1105 }
1106 else if (_data == 65534)// 如果_data的值等于65534
1107 // 调用compact_get_page函数获取一个特定的页面,并返回该页面中compact_shared_parent指向的T类型数据的地址
1108 return static_cast<T*>(compact_get_page(this, header_offset)->compact_shared_parent);
1109 else// 如果_data的值大于65534
1110 // 调用compact_get_value函数,根据_data的值和header_offset获取并返回T类型数据的地址
1111 return compact_get_value<header_offset, T>(this);
1112 }
1113 else// 如果_data为零
1114 // 返回空指针,表示没有有效的T类型数据
1115 return 0;
1116 }
1117
1118 T* operator->() const
1119 {
1120 // 返回当前对象转换为T*的结果,允许通过->操作符访问T类型对象的成员
1121 return *this;
1122 }
1123
1124 private:
1125 uint16_t _data;
1126 };
1127
1128 template <int header_offset, int base_offset> class compact_string
1129 {
1130 public:
1131 compact_string(): _data(0)
1132 {
1133 }
1134
1135 void operator=(const compact_string& rhs)
1136 {
1137 *this = rhs + 0;
1138 }
1139
1140 void operator=(char_t* value)
1141 {
1142 if (value)
1143 {
1144 // 获取与当前对象关联的内存页面
1145 xml_memory_page* page = compact_get_page(this, header_offset);
1146 // 如果compact_string_base为0(即尚未初始化或没有存储任何字符串),则将其设置为value
1147 if (PUGI__UNLIKELY(page->compact_string_base == 0))
1148 page->compact_string_base = value;
1149 // 计算value相对于compact_string_base的偏移量
1150 ptrdiff_t offset = value - page->compact_string_base;
1151 // 如果偏移量小于65535 * 128(即小于16位带符号整数能表示的最大正偏移量,但这里实际上只使用了15位加上一个标志位)
1152 if (static_cast<uintptr_t>(offset) < (65535 << 7))
1153 {
1154 // round-trip through void* to silence 'cast increases required alignment of target type' warnings
1155 uint16_t* base = reinterpret_cast<uint16_t*>(static_cast<void*>(reinterpret_cast<char*>(this) - base_offset));
1156 // 如果base指向的值为0(即尚未存储任何偏移量)
1157 if (*base == 0)
1158 {
1159 // 存储偏移量(右移7位以适应15位的存储,并加1作为标志)到base指向的位置
1160 // 同时,将偏移量的低7位(加1作为标志)存储到_data中
1161 *base = static_cast<uint16_t>((offset >> 7) + 1);
1162 _data = static_cast<unsigned char>((offset & 127) + 1);
1163 }
1164 else
1165 {
1166 // 如果已经存储了一个偏移量,则计算新的偏移量是否仍然可以用当前的方式存储
1167 ptrdiff_t remainder = offset - ((*base - 1) << 7);
1168 // 如果新的偏移量的低7位(加1后)小于等于253(即实际偏移量小于等于252,因为加1作为标志)
1169 if (static_cast<uintptr_t>(remainder) <= 253)
1170 {
1171 // 更新_data以存储新的低7位偏移量(加1作为标志)
1172 _data = static_cast<unsigned char>(remainder + 1);
1173 }
1174 else
1175 {
1176 // 如果新的偏移量太大,无法用当前的方式存储,则使用另一种存储方法(可能是动态分配)
1177 compact_set_value<header_offset>(this, value);
1178 // 设置_data为255,作为使用特殊存储方法的标志
1179 _data = 255;
1180 }
1181 }
1182 }
1183 else
1184 {
1185 // 如果偏移量太大,无法用当前的方式存储,则同样使用另一种存储方法
1186 compact_set_value<header_offset>(this, value);
1187 // 设置_data为255,作为使用特殊存储方法的标志
1188 _data = 255;
1189 }
1190 }
1191 else
1192 {
1193 // 如果提供的值为空,则将_data设置为0,表示没有存储任何值
1194 _data = 0;
1195 }
1196 }
1197
1198 operator char_t*() const
1199 {
1200 if (_data)
1201 {
1202 if (_data < 255)
1203 {
1204 xml_memory_page* page = compact_get_page(this, header_offset);
1205
1206 // round-trip through void* to silence 'cast increases required alignment of target type' warnings
1207 const uint16_t* base = reinterpret_cast<const uint16_t*>(static_cast<const void*>(reinterpret_cast<const char*>(this) - base_offset));
1208 assert(*base);
1209
1210 ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1);
1211
1212 return page->compact_string_base + offset;
1213 }
1214 else
1215 {
1216 return compact_get_value<header_offset, char_t>(this);
1217 }
1218 }
1219 else
1220 return 0;
1221 }
1222
1223 private:
1224 unsigned char _data;
1225 };
1226// PUGI__NS_END 和 PUGI__NS_BEGIN 是命名空间结束和开始的宏,用于创建一个命名空间。
1227// 这样可以避免全局命名冲突,并组织代码结构。
1229#endif
1230
1231#ifdef PUGIXML_COMPACT
1232namespace pugi
1233{
1234 // xml_attribute_struct 结构体表示一个 XML 属性。
1235 struct xml_attribute_struct
1236 {
1237 // 构造函数初始化属性结构。
1238 xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0)
1239 {
1240 PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); // 确保结构体大小为 8 字节。
1241 }
1242
1243 impl::compact_header header; // 紧凑模式下的头部信息。
1244
1245 uint16_t namevalue_base; // 名称和值的基础偏移量。
1246
1247 impl::compact_string<4, 2> name; // 紧凑字符串,用于存储属性名。
1248 impl::compact_string<5, 3> value; // 紧凑字符串,用于存储属性值。
1249
1250 impl::compact_pointer<xml_attribute_struct, 6> prev_attribute_c; // 前一个属性的指针。
1251 impl::compact_pointer<xml_attribute_struct, 7, 0> next_attribute; // 下一个属性的指针。
1252 };
1253
1254 // xml_node_struct 结构体表示一个 XML 节点。
1255 struct xml_node_struct
1256 {
1257 // 构造函数初始化节点结构。
1258 xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0)
1259 {
1260 PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12); // 确保结构体大小为 12 字节。
1261 }
1262
1263 impl::compact_header header; // 紧凑模式下的头部信息。
1264
1265 uint16_t namevalue_base; // 名称和值的基础偏移量。
1266
1267 impl::compact_string<4, 2> name; // 紧凑字符串,用于存储节点名。
1268 impl::compact_string<5, 3> value; // 紧凑字符串,用于存储节点值。
1269
1270 impl::compact_pointer_parent<xml_node_struct, 6> parent; // 父节点的指针。
1271
1272 impl::compact_pointer<xml_node_struct, 8, 0> first_child; // 第一个子节点的指针。
1273
1274 impl::compact_pointer<xml_node_struct, 9> prev_sibling_c; // 前一个兄弟节点的指针。
1275 impl::compact_pointer<xml_node_struct, 10, 0> next_sibling; // 下一个兄弟节点的指针。
1276
1277 impl::compact_pointer<xml_attribute_struct, 11, 0> first_attribute; // 第一个属性的指针。
1278 };
1279}
1280#else
1281namespace pugi
1282{
1283 // 在非紧凑模式下,xml_attribute_struct 和 xml_node_struct 结构体的定义与紧凑模式略有不同。
1284 // 它们使用常规指针和简单的成员变量,而不是紧凑模式下的紧凑指针和字符串。
1286 {
1287 xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0)
1288 {
1289 header = PUGI__GETHEADER_IMPL(this, page, 0); // 初始化头部信息。
1290 }
1291
1292 uintptr_t header; // 头部信息。
1293
1294 char_t* name; // 属性名。
1295 char_t* value; // 属性值。
1296
1297 xml_attribute_struct* prev_attribute_c; // 前一个属性的指针。
1298 xml_attribute_struct* next_attribute; // 下一个属性的指针。
1299 };
1300
1302 {
1303 xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
1304 {
1305 header = PUGI__GETHEADER_IMPL(this, page, type); // 初始化头部信息。
1306 }
1307
1308 uintptr_t header; // 头部信息。
1309
1310 char_t* name; // 节点名。
1311 char_t* value; // 节点值。
1312
1313 xml_node_struct* parent; // 父节点的指针。
1314
1315 xml_node_struct* first_child; // 第一个子节点的指针。
1316
1317 xml_node_struct* prev_sibling_c; // 前一个兄弟节点的指针。
1318 xml_node_struct* next_sibling; // 下一个兄弟节点的指针。
1319
1320 xml_attribute_struct* first_attribute; // 第一个属性的指针。
1321 };
1322}
1323#endif
1324
1326 // xml_extra_buffer 结构体表示一个额外的缓冲区,用于存储 XML 文档中的字符数据。
1328 {
1329 char_t* buffer; // 缓冲区指针。
1330 xml_extra_buffer* next; // 下一个额外缓冲区的指针。
1331 };
1332
1333 // xml_document_struct 结构体表示一个 XML 文档,继承自 xml_node_struct 和 xml_allocator。
1335 {
1337 {
1338 }
1339
1340 const char_t* buffer; // 文档缓冲区指针。
1341
1342 xml_extra_buffer* extra_buffers; // 额外缓冲区指针。
1343
1344 #ifdef PUGIXML_COMPACT
1345 compact_hash_table hash; // 紧凑模式下的哈希表。
1346 #endif
1347 };
1348
1349 // get_allocator 模板函数返回给定对象的分配器。
1350 template <typename Object> inline xml_allocator& get_allocator(const Object* object)
1351 {
1352 assert(object); // 确保对象非空。
1353
1354 return *PUGI__GETPAGE(object)->allocator; // 返回对象所在页面的分配器。
1355 }
1356
1357 // get_document 模板函数返回给定对象所属的文档。
1358 template <typename Object> inline xml_document_struct& get_document(const Object* object)
1359 {
1360 assert(object); // 确保对象非空。
1361
1362 return *static_cast<xml_document_struct*>(PUGI__GETPAGE(object)->allocator); // 返回对象所在页面的文档。
1363 }
1365
1366// Low-level DOM operations
1368 inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)// 分配一个属性节点
1369 {
1370 xml_memory_page* page; // 声明内存页指针
1371 void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page);// 分配属性节点所需的内存
1372 if (!memory) return 0;// 如果内存分配失败,返回空指针
1373
1374 return new (memory) xml_attribute_struct(page); // 在分配的内存上构造属性节点对象,并返回指针
1375 }
1376
1377 inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type)// 分配一个节点
1378 {
1379 xml_memory_page* page; // 声明内存页指针
1380 void* memory = alloc.allocate_object(sizeof(xml_node_struct), page);// 分配节点所需的内存
1381 if (!memory) return 0;// 如果内存分配失败,返回空指针
1382
1383 return new (memory) xml_node_struct(page, type);// 在分配的内存上构造节点对象,并返回指针
1384 }
1385
1386 inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)// 销毁属性节点
1387 {
1388 if (a->header & impl::xml_memory_page_name_allocated_mask) // 检查属性名是否需要释放
1389 alloc.deallocate_string(a->name); // 释放属性名所占的内存
1390
1391 if (a->header & impl::xml_memory_page_value_allocated_mask)// 检查属性值是否需要释放
1392 alloc.deallocate_string(a->value);// 释放属性值所占的内存
1393
1394 alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a)); // 释放属性节点本身所占的内存
1395 }
1396
1397 inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) // 销毁节点
1398 {
1399 if (n->header & impl::xml_memory_page_name_allocated_mask) // 检查节点名称是否需要释放
1400 alloc.deallocate_string(n->name); // 释放节点名称所占的内存
1401
1402 if (n->header & impl::xml_memory_page_value_allocated_mask)// 检查节点值是否需要释放
1403 alloc.deallocate_string(n->value);// 释放节点值所占的内存
1404
1405 for (xml_attribute_struct* attr = n->first_attribute; attr; )// 遍历节点的所有属性
1406 {
1407 xml_attribute_struct* next = attr->next_attribute;// 保存下一个属性的指针
1408
1409 destroy_attribute(attr, alloc); // 销毁当前属性
1410
1411 attr = next;// 移动到下一个属性
1412 }
1413
1414 for (xml_node_struct* child = n->first_child; child; ) // 遍历节点的所有子节点
1415 {
1416 xml_node_struct* next = child->next_sibling;// 保存下一个子节点的指针
1417
1418 destroy_node(child, alloc);// 销毁当前子节点
1419
1420 child = next;// 移动到下一个子节点
1421 }
1422
1423 alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n));// 释放当前节点本身所占的内存
1424 }
1425
1426 inline void append_node(xml_node_struct* child, xml_node_struct* node) // 将子节点添加到父节点的子节点列表末尾
1427 {
1428 child->parent = node;// 设置子节点的父节点指针为当前节点
1429
1430 xml_node_struct* head = node->first_child; // 获取当前节点的第一个子节点
1431
1432 if (head)// 如果当前节点已经有子节点
1433 {
1434 xml_node_struct* tail = head->prev_sibling_c;// 获取当前子节点列表的最后一个节点
1435
1436 tail->next_sibling = child;// 将当前子节点列表的最后一个节点的下一个指针指向新的子节点
1437 child->prev_sibling_c = tail;// 将新的子节点的前一个指针指向当前子节点列表的最后一个节点
1438 head->prev_sibling_c = child;// 更新子节点列表的第一个节点的前一个指针为新的子节点
1439 }
1440 else// 如果当前节点没有子节点
1441 {
1442 node->first_child = child;// 将新的子节点设置为第一个子节点
1443 child->prev_sibling_c = child;// 子节点的前一个指针指向自身(形成循环链表)
1444 }
1445 }
1446
1447 inline void prepend_node(xml_node_struct* child, xml_node_struct* node)// 将子节点添加到父节点的子节点列表开头
1448 {
1449 child->parent = node; // 设置子节点的父节点指针为当前节点
1450
1451 xml_node_struct* head = node->first_child;// 获取当前节点的第一个子节点
1452
1453 if (head)// 如果当前节点已经有子节点
1454 {
1455 child->prev_sibling_c = head->prev_sibling_c;// 将新子节点的前一个指针设置为当前子节点列表的最后一个节点
1456 head->prev_sibling_c = child; // 更新子节点列表的第一个节点的前一个指针为新的子节点
1457 }
1458 else// 如果当前节点没有子节点
1459 child->prev_sibling_c = child;// 新子节点的前一个指针指向自身(形成循环链表)
1460
1461 child->next_sibling = head; // 将新子节点的下一个指针设置为当前的第一个子节点
1462 node->first_child = child;// 更新父节点的第一个子节点为新的子节点
1463 }
1464
1465 inline void insert_node_after(xml_node_struct* child, xml_node_struct* node)// 在指定节点之后插入一个子节点
1466 {
1467 xml_node_struct* parent = node->parent;// 获取指定节点的父节点
1468
1469 child->parent = parent;// 设置新节点的父节点指针为当前父节点
1470
1471 if (node->next_sibling)// 如果指定节点有下一个兄弟节点
1472 node->next_sibling->prev_sibling_c = child;// 更新下一个兄弟节点的前一个指针为新节点
1473 else// 如果指定节点是最后一个节点
1474 parent->first_child->prev_sibling_c = child;// 更新父节点子节点列表的最后一个节点的前一个指针为新节点
1475
1476 child->next_sibling = node->next_sibling;// 设置新节点的下一个指针为指定节点的下一个节点
1477 child->prev_sibling_c = node;// 设置新节点的前一个指针为指定节点
1478
1479 node->next_sibling = child;// 更新指定节点的下一个指针为新节点
1480 }
1481
1482 inline void insert_node_before(xml_node_struct* child, xml_node_struct* node)// 在指定节点之前插入一个子节点
1483 {
1484 xml_node_struct* parent = node->parent;// 获取指定节点的父节点
1485
1486 child->parent = parent;// 设置新节点的父节点指针为当前父节点
1487
1488 if (node->prev_sibling_c->next_sibling) // 如果指定节点的前一个节点存在并有下一个指针
1489 node->prev_sibling_c->next_sibling = child; // 更新前一个节点的下一个指针为新节点
1490 else// 如果指定节点是第一个节点
1491 parent->first_child = child; // 更新父节点的第一个子节点为新节点
1492
1493 child->prev_sibling_c = node->prev_sibling_c;// 设置新节点的前一个指针为指定节点的前一个节点
1494 child->next_sibling = node;// 设置新节点的下一个指针为指定节点
1495
1496 node->prev_sibling_c = child;// 更新指定节点的前一个指针为新节点
1497 }
1498
1499 inline void remove_node(xml_node_struct* node)// 从父节点的子节点列表中移除指定节点
1500 {
1501 xml_node_struct* parent = node->parent;// 获取当前节点的父节点
1502
1503 if (node->next_sibling) // 如果当前节点有下一个兄弟节点
1504 node->next_sibling->prev_sibling_c = node->prev_sibling_c;// 更新下一个兄弟节点的前一个指针为当前节点的前一个兄弟节点
1505 else// 如果当前节点是最后一个节点
1506 parent->first_child->prev_sibling_c = node->prev_sibling_c;// 更新父节点子节点列表的最后一个节点为当前节点的前一个兄弟节点
1507
1508 if (node->prev_sibling_c->next_sibling) // 如果当前节点有前一个兄弟节点
1509 node->prev_sibling_c->next_sibling = node->next_sibling;// 更新前一个兄弟节点的下一个指针为当前节点的下一个兄弟节点
1510 else// 如果当前节点是第一个节点
1511 parent->first_child = node->next_sibling;// 更新父节点的第一个子节点为当前节点的下一个兄弟节点
1512
1513 node->parent = 0; // 将当前节点的父节点指针清空
1514 node->prev_sibling_c = 0;// 将当前节点的前一个兄弟节点指针清空
1515 node->next_sibling = 0;// 将当前节点的下一个兄弟节点指针清空
1516 }
1517
1518 inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node)// 将属性添加到节点的属性列表末尾
1519 {
1520 xml_attribute_struct* head = node->first_attribute;// 获取节点的第一个属性
1521
1522 if (head)// 如果节点已经有属性
1523 {
1524 xml_attribute_struct* tail = head->prev_attribute_c;// 获取属性列表的最后一个属性
1525
1526 tail->next_attribute = attr;// 将最后一个属性的下一个指针指向新属性
1527 attr->prev_attribute_c = tail;// 将新属性的前一个指针指向最后一个属性
1528 head->prev_attribute_c = attr;// 更新属性列表的第一个属性的前一个指针为新属性
1529 }
1530 else// 如果节点没有属性
1531 {
1532 node->first_attribute = attr;// 将新属性设置为第一个属性
1533 attr->prev_attribute_c = attr;// 新属性的前一个指针指向自身(形成循环链表)
1534 }
1535 }
1536
1537 inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node)// 将属性添加到节点的属性列表开头
1538 {
1539 xml_attribute_struct* head = node->first_attribute;// 获取节点的第一个属性
1540
1541 if (head)// 如果节点已经有属性
1542 {
1543 attr->prev_attribute_c = head->prev_attribute_c;// 将新属性的前一个指针设置为属性列表的最后一个属性
1544 head->prev_attribute_c = attr;// 更新属性列表第一个属性的前一个指针为新属性
1545 }
1546 else// 如果节点没有属性
1547 attr->prev_attribute_c = attr;// 新属性的前一个指针指向自身(形成循环链表)
1548
1549 attr->next_attribute = head;// 将新属性的下一个指针指向原来的第一个属性
1550 node->first_attribute = attr;// 更新节点的第一个属性为新属性
1551 }
1552
1553 inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)// 在指定属性之后插入新属性
1554 {
1555 if (place->next_attribute)// 如果指定属性有下一个属性
1556 place->next_attribute->prev_attribute_c = attr; // 更新下一个属性的前一个指针为新属性
1557 else// 如果指定属性是最后一个属性
1558 node->first_attribute->prev_attribute_c = attr;// 更新属性列表最后一个属性的前一个指针为新属性
1559
1560 attr->next_attribute = place->next_attribute;// 设置新属性的下一个指针为指定属性的下一个属性
1561 attr->prev_attribute_c = place; // 设置新属性的前一个指针为指定属性
1562 place->next_attribute = attr;// 更新指定属性的下一个指针为新属性
1563 }
1564
1565 inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)// 在指定属性之前插入新属性
1566 {
1567 if (place->prev_attribute_c->next_attribute)// 如果指定属性的前一个属性有下一个指针
1568 place->prev_attribute_c->next_attribute = attr;// 更新前一个属性的下一个指针为新属性
1569 else// 如果指定属性是第一个属性
1570 node->first_attribute = attr;// 更新节点的第一个属性为新属性
1571
1572 attr->prev_attribute_c = place->prev_attribute_c;// 设置新属性的前一个指针为指定属性的前一个属性
1573 attr->next_attribute = place;// 设置新属性的下一个指针为指定属性
1574 place->prev_attribute_c = attr;// 更新指定属性的前一个指针为新属性
1575 }
1576
1577 inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node)// 从节点的属性列表中移除指定属性
1578 {
1579 if (attr->next_attribute)// 如果指定属性有下一个属性
1580 attr->next_attribute->prev_attribute_c = attr->prev_attribute_c;// 更新下一个属性的前一个指针为指定属性的前一个属性
1581 else// 如果指定属性是最后一个属性
1582 node->first_attribute->prev_attribute_c = attr->prev_attribute_c;// 更新属性列表最后一个属性为指定属性的前一个属性
1583
1584 if (attr->prev_attribute_c->next_attribute)// 如果指定属性有前一个属性
1585 attr->prev_attribute_c->next_attribute = attr->next_attribute;// 更新前一个属性的下一个指针为指定属性的下一个属性
1586 else// 如果指定属性是第一个属性
1587 node->first_attribute = attr->next_attribute;// 更新节点的第一个属性为指定属性的下一个属性
1588
1589 attr->prev_attribute_c = 0;// 将指定属性的前一个指针清空
1590 attr->next_attribute = 0;// 将指定属性的下一个指针清空
1591 }
1592
1593 PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)// 在节点末尾添加一个新节点
1594 {
1595 if (!alloc.reserve()) return 0;// 检查内存分配器是否有足够的空间,若没有返回空指针
1596
1597 xml_node_struct* child = allocate_node(alloc, type);// 分配一个新的节点
1598 if (!child) return 0; // 如果分配失败,返回空指针
1599
1600 append_node(child, node);// 将新节点添加到指定节点的子节点列表末尾
1601
1602 return child;// 返回新创建的节点
1603 }
1604
1605 PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc)// 在节点末尾添加一个新属性
1606 {
1607 if (!alloc.reserve()) return 0; // 检查内存分配器是否有足够的空间,若没有返回空指针
1608
1609 xml_attribute_struct* attr = allocate_attribute(alloc);// 分配一个新的属性
1610 if (!attr) return 0;// 如果分配失败,返回空指针
1611
1612 append_attribute(attr, node); // 将新属性添加到指定节点的属性列表末尾
1613
1614 return attr;// 返回新创建的属性
1615 }
1617
1618// Helper classes for code generation
1620 struct opt_false // 定义一个表示逻辑假值的结构体
1621 {
1622 enum { value = 0 };// 静态成员变量,值为0
1623 };
1624
1625 struct opt_true// 定义一个表示逻辑真值的结构体
1626 {
1627 enum { value = 1 };// 静态成员变量,值为1
1628 };
1630
1631// Unicode utilities
1633 inline uint16_t endian_swap(uint16_t value)// 交换16位整数的字节顺序(大小端转换)
1634 {
1635 return static_cast<uint16_t>(((value & 0xff) << 8) | (value >> 8));// 将低8位移到高8位,将高8位移到低8位
1636 }
1637
1638 inline uint32_t endian_swap(uint32_t value)// 交换32位整数的字节顺序(大小端转换)
1639 {
1640 return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24);
1641 }
1642
1644 {
1645 typedef size_t value_type;
1646
1647 static value_type low(value_type result, uint32_t ch)
1648 {
1649 // U+0000..U+007F
1650 if (ch < 0x80) return result + 1;
1651 // U+0080..U+07FF
1652 else if (ch < 0x800) return result + 2;
1653 // U+0800..U+FFFF
1654 else return result + 3;
1655 }
1656
1657 static value_type high(value_type result, uint32_t)
1658 {
1659 // U+10000..U+10FFFF
1660 return result + 4;
1661 }
1662 };
1663
1665 {
1666 typedef uint8_t* value_type;
1667
1668 static value_type low(value_type result, uint32_t ch)
1669 {
1670 // U+0000..U+007F
1671 if (ch < 0x80)
1672 {
1673 *result = static_cast<uint8_t>(ch);
1674 return result + 1;
1675 }
1676 // U+0080..U+07FF
1677 else if (ch < 0x800)
1678 {
1679 result[0] = static_cast<uint8_t>(0xC0 | (ch >> 6));
1680 result[1] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
1681 return result + 2;
1682 }
1683 // U+0800..U+FFFF
1684 else
1685 {
1686 result[0] = static_cast<uint8_t>(0xE0 | (ch >> 12));
1687 result[1] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));
1688 result[2] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
1689 return result + 3;
1690 }
1691 }
1692
1693 static value_type high(value_type result, uint32_t ch)
1694 {
1695 // U+10000..U+10FFFF
1696 result[0] = static_cast<uint8_t>(0xF0 | (ch >> 18));
1697 result[1] = static_cast<uint8_t>(0x80 | ((ch >> 12) & 0x3F));
1698 result[2] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));
1699 result[3] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
1700 return result + 4;
1701 }
1702
1703 static value_type any(value_type result, uint32_t ch)
1704 {
1705 return (ch < 0x10000) ? low(result, ch) : high(result, ch);
1706 }
1707 };
1708
1709 struct utf16_counter// 用于计数 UTF-16 编码的结构体
1710 {
1711 typedef size_t value_type;// 定义 value_type 为 size_t 类型
1712
1713 static value_type low(value_type result, uint32_t)// 处理低位字符时增加计数
1714 {
1715 return result + 1;// 返回当前计数加 1
1716 }
1717
1718 static value_type high(value_type result, uint32_t)// 处理高位字符时增加计数
1719 {
1720 return result + 2; // 返回当前计数加 2,因为高位字符占用 2 个字节
1721 }
1722 };
1723
1724 struct utf16_writer// 用于将字符写入 UTF-16 编码的结构体
1725 {
1726 typedef uint16_t* value_type;// 定义 value_type 为 uint16_t 指针类型
1727
1728 static value_type low(value_type result, uint32_t ch)// 写入低位字符
1729 {
1730 *result = static_cast<uint16_t>(ch);// 将字符 ch 转换为 uint16_t 并写入 result
1731
1732 return result + 1;// 返回指向下一个位置的指针
1733 }
1734
1735 static value_type high(value_type result, uint32_t ch)// 写入高位字符
1736 {
1737 uint32_t msh = static_cast<uint32_t>(ch - 0x10000) >> 10;// 获取高 10 位
1738 uint32_t lsh = static_cast<uint32_t>(ch - 0x10000) & 0x3ff;// 获取低 10 位
1739
1740 result[0] = static_cast<uint16_t>(0xD800 + msh);// 计算并写入高位字符
1741 result[1] = static_cast<uint16_t>(0xDC00 + lsh);// 计算并写入低位字符
1742
1743 return result + 2;// 返回指向下一个位置的指针,已经写入两个字节
1744 }
1745
1746 static value_type any(value_type result, uint32_t ch)// 根据字符 ch 判断是写入低位字符还是高位字符
1747 {
1748 return (ch < 0x10000) ? low(result, ch) : high(result, ch);// 如果字符小于 0x10000,调用 low,否则调用 high
1749 }
1750 };
1751
1752 struct utf32_counter// 用于计数 UTF-32 编码的结构体
1753 {
1754 typedef size_t value_type;// 定义 value_type 为 size_t 类型
1755
1756 static value_type low(value_type result, uint32_t)// 处理低位字符时增加计数
1757 {
1758 return result + 1;// 返回当前计数加 1
1759 }
1760
1761 static value_type high(value_type result, uint32_t)// 处理高位字符时增加计数
1762 {
1763 return result + 1;// 返回当前计数加 1,UTF-32 中每个字符都是 1 个单位
1764 }
1765 };
1766
1767 struct utf32_writer// 用于将字符写入 UTF-32 编码的结构体
1768 {
1769 typedef uint32_t* value_type;// 定义 value_type 为 uint32_t 指针类型
1770
1771 static value_type low(value_type result, uint32_t ch)// 写入低位字符
1772 {
1773 *result = ch;// 将字符 ch 直接赋值给 result
1774
1775 return result + 1;// 返回指向下一个位置的指针
1776 }
1777
1778 static value_type high(value_type result, uint32_t ch)// 写入高位字符
1779 {
1780 *result = ch;// 将字符 ch 直接赋值给 result
1781
1782 return result + 1;// 返回指向下一个位置的指针
1783 }
1784
1785 static value_type any(value_type result, uint32_t ch)// 写入任何字符
1786 {
1787 *result = ch; // 将字符 ch 直接赋值给 result
1788
1789 return result + 1;// 返回指向下一个位置的指针
1790 }
1791 };
1792
1793 struct latin1_writer// 用于将字符写入 Latin-1 编码的结构体
1794 {
1795 typedef uint8_t* value_type; // 定义 value_type 为 uint8_t 指针类型
1796
1797 static value_type low(value_type result, uint32_t ch)// 写入低位字符
1798 {
1799 *result = static_cast<uint8_t>(ch > 255 ? '?' : ch);// 如果字符值大于 255,则写入 '?',否则写入字符本身(转换为 uint8_t)
1800
1801 return result + 1;// 返回指向下一个位置的指针
1802 }
1803
1804 static value_type high(value_type result, uint32_t ch)// 写入高位字符
1805 {
1806 (void)ch;// 忽略传入的 ch 参数,因为 Latin-1 只能处理单字节字符
1807
1808 *result = '?';// 始终写入 '?' 作为占位符
1809
1810 return result + 1;// 返回指向下一个位置的指针
1811 }
1812 };
1813
1814 struct utf8_decoder// 用于解码 UTF-8 的结构体
1815 {
1816 typedef uint8_t type;// 定义 type 为 uint8_t 类型
1817
1818 template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)// 解码过程函数
1819 {
1820 const uint8_t utf8_byte_mask = 0x3f;// 定义用于提取有效位的掩码
1821
1822 while (size)// 遍历输入数据
1823 {
1824 uint8_t lead = *data;// 获取当前字节作为首字节
1825
1826
1827 // 0xxxxxxx -> U+0000..U+007F
1828 if (lead < 0x80)// 单字节 ASCII 字符处理
1829 {
1830 result = Traits::low(result, lead);// 通过 Traits 处理单字节字符
1831 data += 1;// 移动到下一个字节
1832 size -= 1;// 减少字节计数
1833
1834 // process aligned single-byte (ascii) blocks
1835 if ((reinterpret_cast<uintptr_t>(data) & 3) == 0)// 检查数据是否对齐到 4 字节边界
1836 {
1837 // round-trip through void* to silence 'cast increases required alignment of target type' warnings
1838 while (size >= 4 && (*static_cast<const uint32_t*>(static_cast<const void*>(data)) & 0x80808080) == 0)// 快速处理连续的 ASCII 字符块
1839 {
1840 result = Traits::low(result, data[0]);// 处理第一个字节
1841 result = Traits::low(result, data[1]);// 处理第二个字节
1842 result = Traits::low(result, data[2]);// 处理第三个字节
1843 result = Traits::low(result, data[3]);// 处理第四个字节
1844 data += 4;// 移动到下一个 4 字节块
1845 size -= 4;// 减少字节计数
1846 }
1847 }
1848 }
1849 // 110xxxxx -> U+0080..U+07FF
1850 else if (static_cast<unsigned int>(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80)// 两字节 UTF-8 字符处理
1851 {
1852 result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask));// 解码并处理两字节字符
1853 data += 2;// 移动到下一个字节
1854 size -= 2;// 减少字节计数
1855 }
1856 // 1110xxxx -> U+0800-U+FFFF
1857 else if (static_cast<unsigned int>(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80)// 三字节 UTF-8 字符处理
1858 {
1859 result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask));// 解码并处理三字节字符
1860 data += 3;// 移动到下一个字节
1861 size -= 3;// 减少字节计数
1862 }
1863 // 11110xxx -> U+10000..U+10FFFF
1864 else if (static_cast<unsigned int>(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80)// 四字节 UTF-8 字符处理
1865 {
1866 result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask));// 解码并处理四字节字符
1867 data += 4; // 移动到下一个字节
1868 size -= 4; // 减少字节计数
1869 }
1870 // 10xxxxxx or 11111xxx -> invalid
1871 else// 非法字节处理
1872 {
1873 data += 1;// 跳过非法字节
1874 size -= 1;// 减少字节计数
1875 }
1876 }
1877
1878 return result;// 返回处理结果
1879 }
1880 };
1881
1882 template <typename opt_swap> struct utf16_decoder
1883 {
1884 typedef uint16_t type; // 定义解码器处理的数据类型为uint16_t
1885// 定义一个模板函数process,用于处理UTF-16编码的数据
1886 template <typename Traits> static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits)
1887 {
1888 while (size)// 当还有数据需要处理时
1889 {
1890 uint16_t lead = opt_swap::value ? endian_swap(*data) : *data;// 根据opt_swap决定是否需要字节顺序交换
1891
1892 // U+0000..U+D7FF
1893 if (lead < 0xD800)
1894 {
1895 result = Traits::low(result, lead); // 处理非代理项高位字符
1896 data += 1;// 移动到下一个数据
1897 size -= 1;// 数据大小减一
1898 }
1899 // U+E000..U+FFFF
1900 else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000)
1901 {
1902 result = Traits::low(result, lead);// 处理直接映射的单个字符
1903 data += 1;// 移动到下一个数据
1904 size -= 1;// 数据大小减一
1905 }
1906 // surrogate pair lead
1907 else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2)
1908 {
1909 uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];// 根据opt_swap决定是否需要字节顺序交换下一个数据
1910// 检查是否是一个有效的代理项对低位(U+DC00到U+DFFF)
1911 if (static_cast<unsigned int>(next - 0xDC00) < 0x400)
1912 {// 合并代理项对高位和低位为一个Unicode码点(U+10000到U+10FFFF)
1913 result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));
1914 data += 2;// 移动到下一个数据对之后
1915 size -= 2;// 数据大小减二
1916 }
1917 else
1918 {
1919 data += 1;// 如果不是有效的代理项对低位,只移动到一个数据之后
1920 size -= 1;// 数据大小减一
1921 }
1922 }
1923 else
1924 {
1925 data += 1;// 对于不符合上述条件的字符,只移动到一个数据之后
1926 size -= 1;// 数据大小减一
1927 }
1928 }
1929
1930 return result;// 返回处理后的结果
1931 }
1932 };
1933
1934 template <typename opt_swap> struct utf32_decoder
1935 {// 定义一个类型别名 type,表示 UTF-32 编码的基本单元,即 uint32_t 类型。
1936 typedef uint32_t type;// type 是 uint32_t 的别名
1937// 定义一个静态模板方法 process,用于处理 UTF-32 编码的数据。
1938 template <typename Traits> static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits)
1939 {
1940 while (size)
1941 {
1942 // 根据 opt_swap::value 的值决定是否对当前数据进行字节序交换
1943 // 如果 opt_swap::value 为 true,则调用 endian_swap 函数交换字节序
1944 // 否则,直接使用原始数据
1945 uint32_t lead = opt_swap::value ? endian_swap(*data) : *data;// 获取当前 UTF-32 字符,可能已进行字节序交换
1946
1947 // U+0000..U+FFFF
1948 if (lead < 0x10000)
1949 {
1950 // 使用 Traits 的 low 方法处理低代理对或单独的 BMP 字符
1951 // low 方法应返回更新后的结果值
1952 result = Traits::low(result, lead); // 处理低代理对或 BMP 字符,并更新结果
1953 data += 1;// 指针后移,指向下一个 UTF-32 字符
1954 size -= 1;// 数据大小减一
1955 }
1956 // U+10000..U+10FFFF
1957 else
1958 {
1959 result = Traits::high(result, lead);// 处理高代理对,并更新结果
1960 data += 1;// 指针后移,指向下一个 UTF-32 字符
1961 size -= 1;// 数据大小减一
1962 }
1963 }
1964
1965 return result;// 返回最终结果
1966 }
1967 };
1968
1970 {
1971 typedef uint8_t type;// 定义一个类型别名 `type`,等价于 `uint8_t`。
1972
1973 template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)
1974 {
1975 while (size)// 当 `size` 不为 0 时循环。
1976 {
1977 result = Traits::low(result, *data);// 调用 `Traits` 的 `low` 函数处理数据并更新
1978 data += 1;// 指针向后移动一个字节。
1979 size -= 1;// 数据大小减 1。
1980 }
1981
1982 return result;// 返回最终的 `result`。
1983 }
1984 };
1985
1986 template <size_t size> struct wchar_selector;// 根据模板参数 `size` 定义 `wchar_selector` 的特化结构。
1987
1988 template <> struct wchar_selector<2>
1989 {
1990 typedef uint16_t type; // 定义类型别名 `type`,等价于 `uint16_t`。
1991 typedef utf16_counter counter; // 定义类型别名 `counter`,等价于 `utf16_counter`。
1992 typedef utf16_writer writer;// 定义类型别名 `writer`,等价于 `utf16_writer`。
1993 typedef utf16_decoder<opt_false> decoder; // 定义类型别名 `decoder`,等价于 `utf16_decoder<opt_false>`。
1994 };
1995
1996 template <> struct wchar_selector<4>
1997 {
1998 typedef uint32_t type; // 定义类型别名 `type`,等价于 `uint32_t`。
1999 typedef utf32_counter counter;// 定义类型别名 `counter`,等价于 `utf32_counter`。
2000 typedef utf32_writer writer;// 定义类型别名 `writer`,等价于 `utf32_writer`。
2001 typedef utf32_decoder<opt_false> decoder;// 定义类型别名 `decoder`,等价于 `utf32_decoder<opt_false>`。
2002 };
2003
2004 typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;// 定义类型别名 `wchar_counter`,等价于根据 `wchar_t` 大小选择的 `counter` 类型。
2005 typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer; // 定义类型别名 `wchar_writer`,等价于根据 `wchar_t` 大小选择的 `writer` 类型。
2006
2008 {
2009 typedef wchar_t type;// 定义类型别名 `type`,等价于 `wchar_t`。
2010
2011 template <typename Traits> static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits)
2012 {
2013 typedef wchar_selector<sizeof(wchar_t)>::decoder decoder;// 定义类型别名 `decoder`,根据 `wchar_t` 大小选择合适的解码器。
2014
2015 return decoder::process(reinterpret_cast<const typename decoder::type*>(data), size, result, traits);// 调用 `decoder` 的 `process` 函数,将 `data` 转换为解码器的类型进行处理,并返回处理结果。
2016 }
2017 };
2018
2019#ifdef PUGIXML_WCHAR_MODE// 如果定义了 `PUGIXML_WCHAR_MODE` 宏。
2020 PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)
2021 {
2022 for (size_t i = 0; i < length; ++i)// 遍历输入数据。
2023 result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));// 将输入数据的每个元素转换为选择器类型,进行字节序交换后再转换为 `wchar_t` 存储到结果数组中。
2024 }
2025#endif
2027
2030 {
2031 ct_parse_pcdata = 1, // \0, &, \r, <
2032 ct_parse_attr = 2, // \0, &, \r, ', "
2033 ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab
2034 ct_space = 8, // \r, \n, space, tab
2035 ct_parse_cdata = 16, // \0, ], >, \r
2036 ct_parse_comment = 32, // \0, -, >, \r
2037 ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .
2038 ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, :
2040
2041 static const unsigned char chartype_table[256] =
2042 {
2043 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15
2044 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31
2045 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47
2046 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63
2047 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79
2048 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95
2049 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111
2050 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127
2051
2052 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+
2053 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
2054 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
2055 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
2056 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
2057 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
2058 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192,
2059 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192
2060 };
2061
2063 {
2064 ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, >
2065 ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, "
2066 ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _
2067 ctx_digit = 8, // 0-9
2068 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, .
2070
2071 static const unsigned char chartypex_table[256] =
2072 {
2073 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15
2074 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31
2075 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47
2076 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63
2077
2078 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79
2079 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95
2080 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111
2081 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127
2082
2083 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+
2084 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
2085 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
2086 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
2087 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
2088 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
2089 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
2090 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20
2091 };
2092
2093#ifdef PUGIXML_WCHAR_MODE
2094 #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct))
2095#else
2096 #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(c)] & (ct))
2097#endif
2098
2099 #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table)
2100 #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table)
2101
2103 {
2104 unsigned int ui = 1;// 定义一个无符号整型变量 `ui`,并将其设置为 1。
2105
2106 return *reinterpret_cast<unsigned char*>(&ui) == 1; // 检查 `ui` 的最低字节是否为 1,如果是,则当前系统是小端序(little-endian)。
2107 }
2108
2110 {
2111 PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);// 静态断言,确保 `wchar_t` 的大小为 2 或 4 字节。
2112
2113 if (sizeof(wchar_t) == 2)// 如果 `wchar_t` 的大小为 2 字节。
2114 return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;// 根据是否为小端序,返回对应的 UTF-16 编码格式。
2115 else// 如果 `wchar_t` 的大小为 4 字节。
2116 return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;// 根据是否为小端序,返回对应的 UTF-32 编码格式。
2117 }
2118
2119 PUGI__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length)
2120 {
2121 #define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; }// 定义一个宏,用于检查当前位置的字符是否等于指定字符 `ch`,如果不匹配则返回 false。
2122 #define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__IS_CHARTYPE(data[offset], ct)) offset++;// 定义一个宏,用于跳过所有匹配特定字符类型 `ct` 的字符。 }
2123
2124 // check if we have a non-empty XML declaration
2125 if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI__IS_CHARTYPE(data[5], ct_space)))
2126 return false;// 如果数据长度小于 6 或者数据前 6 个字符不是合法的 XML 声明格式,返回 false。
2127
2128 // scan XML declaration until the encoding field
2129 for (size_t i = 6; i + 1 < size; ++i)// 声明中不允许在引号内包含 `?` 字符。
2130 {
2131 // declaration can not contain ? in quoted values
2132 if (data[i] == '?')
2133 return false;
2134
2135 if (data[i] == 'e' && data[i + 1] == 'n')// 找到可能的 `encoding` 字段开头。
2136 {
2137 size_t offset = i;
2138
2139 // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed
2142
2143 // S? = S?
2145 PUGI__SCANCHAR('=');
2147
2148 // the only two valid delimiters are ' and "
2149 uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\'';
2150
2151 PUGI__SCANCHAR(delimiter);
2152
2153 size_t start = offset;// 记录编码字段的起始位置。
2154
2155 out_encoding = data + offset;// 设置输出编码字段的起始位置。
2156
2157 PUGI__SCANCHARTYPE(ct_symbol);// 跳过所有符号字符
2158
2159 out_length = offset - start;// 计算编码字段的长度。
2160
2161 PUGI__SCANCHAR(delimiter);// 检查结束分隔符。
2162
2163 return true;// 成功找到并解析编码字段,返回 true。
2164 }
2165 }
2166
2167 return false;// 如果未找到 `encoding` 字段,返回 false。
2168
2169 #undef PUGI__SCANCHAR
2170 #undef PUGI__SCANCHARTYPE
2171 }
2172
2173 PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size)
2174 {// 如果输入缓冲区太小,无法进行编码自动检测,默认为 UTF-8。
2175 // skip encoding autodetection if input buffer is too small
2176 if (size < 4) return encoding_utf8;
2177
2178 uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3];// 读取缓冲区前四个字节。
2179
2180 // look for BOM in first few bytes
2181 if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be;// UTF-32 大端序。
2182 if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le;// UTF-32 小端序。
2183 if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be;// UTF-16 大端序。
2184 if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le;// UTF-16 小端序。
2185 if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8;
2186
2187 // look for <, <? or <?xm in various encodings
2188 if (d0 == 0 && d1 == 0 && d2 == 0 && d3 == 0x3c) return encoding_utf32_be;// UTF-32 大端序中的 `<`。
2189 if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le;// UTF-32 小端序中的 `<`。
2190 if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be; // UTF-16 大端序中的 `<?`。
2191 if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le;// UTF-16 小端序中的 `<?`。
2192
2193 // look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early)
2194 if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be;// UTF-16 大端序中的 `<`。
2195 if (d0 == 0x3c && d1 == 0) return encoding_utf16_le;// UTF-16 小端序中的 `<`。
2196
2197 // no known BOM detected; parse declaration
2198 const uint8_t* enc = 0;// 用于存储编码字段的起始位置。
2199 size_t enc_length = 0;// 用于存储编码字段的长度。
2200
2201 if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d && parse_declaration_encoding(data, size, enc, enc_length))
2202 {
2203 // iso-8859-1 (case-insensitive)
2204 if (enc_length == 10
2205 && (enc[0] | ' ') == 'i' && (enc[1] | ' ') == 's' && (enc[2] | ' ') == 'o'
2206 && enc[3] == '-' && enc[4] == '8' && enc[5] == '8' && enc[6] == '5' && enc[7] == '9'
2207 && enc[8] == '-' && enc[9] == '1')
2208 return encoding_latin1;
2209
2210 // latin1 (case-insensitive)
2211 if (enc_length == 6
2212 && (enc[0] | ' ') == 'l' && (enc[1] | ' ') == 'a' && (enc[2] | ' ') == 't'
2213 && (enc[3] | ' ') == 'i' && (enc[4] | ' ') == 'n'
2214 && enc[5] == '1')
2215 return encoding_latin1;
2216 }// 如果未匹配到任何已知编码,默认为 UTF-8。
2217
2218 return encoding_utf8;
2219 }
2220
2221 PUGI__FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size)
2222 {
2223 // replace wchar encoding with utf implementation
2224 if (encoding == encoding_wchar) return get_wchar_encoding();
2225
2226 // replace utf16 encoding with utf16 with specific endianness
2227 if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
2228
2229 // replace utf32 encoding with utf32 with specific endianness
2230 if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
2231
2232 // only do autodetection if no explicit encoding is requested
2233 if (encoding != encoding_auto) return encoding;
2234
2235 // try to guess encoding (based on XML specification, Appendix F.1)
2236 const uint8_t* data = static_cast<const uint8_t*>(contents);
2237
2238 return guess_buffer_encoding(data, size);
2239 }
2240
2241 PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
2242 {
2243 size_t length = size / sizeof(char_t);
2244// 计算缓冲区内容的字符长度,字节数除以每个字符的大小。
2245 if (is_mutable)
2246 {// 如果缓冲区是可变的,直接使用输入的内容。
2247 out_buffer = static_cast<char_t*>(const_cast<void*>(contents)); // 将输入的内容指针转换为可修改的 `char_t` 类型指针。
2248 out_length = length;// 设置输出缓冲区的长度。
2249 }
2250 else
2251 {
2252 char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); // 为缓冲区分配内存,多分配一个字符用于添加终止符。
2253 if (!buffer) return false;// 如果内存分配失败,返回 false。
2254
2255 if (contents)
2256 memcpy(buffer, contents, length * sizeof(char_t));// 如果内容不为空,将输入的内容复制到新分配的缓冲区中。
2257 else
2258 assert(length == 0);
2259// 如果内容为空,断言长度为 0,确保数据一致性。
2260 buffer[length] = 0;
2261// 在缓冲区的末尾添加终止符。
2262 out_buffer = buffer; // 将新分配的缓冲区地址赋值给输出指针。
2263 out_length = length + 1;// 设置输出缓冲区的长度,包含终止符。
2264 }
2265
2266 return true; // 返回 true,表示操作成功。
2267 }
2268
2269#ifdef PUGIXML_WCHAR_MODE
2270 PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re)
2271 {
2272 return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) ||
2273 (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be);
2274 // 判断是否需要进行字节序转换。当源编码和目标编码的字节序不一致时(大端序与小端序互换),需要转换。}
2275
2276 PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
2277 {
2278 const char_t* data = static_cast<const char_t*>(contents); // 将输入内容指针转换为 `const char_t*` 类型。
2279 size_t length = size / sizeof(char_t);// 计算缓冲区的字符长度。
2280
2281 if (is_mutable)
2282 {
2283 char_t* buffer = const_cast<char_t*>(data); // 将只读指针转换为可修改的指针。
2284
2285 convert_wchar_endian_swap(buffer, data, length);// 对缓冲区进行字节序转换。
2286
2287 out_buffer = buffer; // 设置输出缓冲区指针。
2288 out_length = length; // 设置输出缓冲区的长度。
2289 }
2290 else
2291 {// 如果缓冲区不可变,需要分配新的缓冲区进行字节序转换。
2292 char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t))); // 分配内存,多分配一个字符用于添加终止符。
2293 if (!buffer) return false; // 如果内存分配失败,返回 false。
2294
2295 convert_wchar_endian_swap(buffer, data, length);// 对新分配的缓冲区进行字节序转换。
2296 buffer[length] = 0;// 在缓冲区末尾添加终止符。
2297
2298 out_buffer = buffer;// 设置输出缓冲区指针。
2299 out_length = length + 1; // 设置输出缓冲区的长度,包括终止符。
2300 }
2301
2302 return true; // 返回 true,表示转换成功。
2303 }
2304
2305 template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
2306 {
2307 const typename D::type* data = static_cast<const typename D::type*>(contents);
2308 size_t data_length = size / sizeof(typename D::type);
2309
2310 // first pass: get length in wchar_t units
2311 size_t length = D::process(data, data_length, 0, wchar_counter());
2312
2313 // allocate buffer of suitable length
2314 char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
2315 if (!buffer) return false;
2316
2317 // second pass: convert utf16 input to wchar_t
2318 wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);
2319 wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer());
2320
2321 assert(oend == obegin + length);
2322 *oend = 0;
2323
2324 out_buffer = buffer;
2325 out_length = length + 1;
2326
2327 return true;
2328 }
2329
2330 PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
2331 {
2332 // get native encoding
2333 xml_encoding wchar_encoding = get_wchar_encoding();
2334
2335 // fast path: no conversion required
2336 if (encoding == wchar_encoding)
2337 return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
2338
2339 // only endian-swapping is required
2340 if (need_endian_swap_utf(encoding, wchar_encoding))
2341 return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);
2342
2343 // source encoding is utf8
2344 if (encoding == encoding_utf8)
2345 return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder());
2346
2347 // source encoding is utf16
2348 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
2349 {
2350 xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
2351
2352 return (native_encoding == encoding) ?
2353 convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :
2354 convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());
2355 }
2356
2357 // source encoding is utf32
2358 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
2359 {
2360 xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
2361
2362 return (native_encoding == encoding) ?
2363 convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :
2364 convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());
2365 }
2366
2367 // source encoding is latin1
2368 if (encoding == encoding_latin1)
2369 return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder());
2370
2371 assert(false && "Invalid encoding"); // unreachable
2372 return false;
2373 }
2374#else
2375 template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
2376 {
2377 const typename D::type* data = static_cast<const typename D::type*>(contents);
2378 size_t data_length = size / sizeof(typename D::type);
2379
2380 // first pass: get length in utf8 units
2381 size_t length = D::process(data, data_length, 0, utf8_counter());
2382
2383 // allocate buffer of suitable length
2384 char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
2385 if (!buffer) return false;
2386
2387 // second pass: convert utf16 input to utf8
2388 uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
2389 uint8_t* oend = D::process(data, data_length, obegin, utf8_writer());
2390
2391 assert(oend == obegin + length);
2392 *oend = 0;
2393
2394 out_buffer = buffer;
2395 out_length = length + 1;
2396
2397 return true;
2398 }
2399
2400 PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size)
2401 {
2402 for (size_t i = 0; i < size; ++i)
2403 if (data[i] > 127)
2404 return i;
2405
2406 return size;
2407 }
2408
2409 PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
2410 {
2411 const uint8_t* data = static_cast<const uint8_t*>(contents);
2412 size_t data_length = size;
2413
2414 // get size of prefix that does not need utf8 conversion
2415 size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length);
2416 assert(prefix_length <= data_length);
2417
2418 const uint8_t* postfix = data + prefix_length;
2419 size_t postfix_length = data_length - prefix_length;
2420
2421 // if no conversion is needed, just return the original buffer
2422 if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
2423
2424 // first pass: get length in utf8 units
2425 size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter());
2426
2427 // allocate buffer of suitable length
2428 char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
2429 if (!buffer) return false;
2430
2431 // second pass: convert latin1 input to utf8
2432 memcpy(buffer, data, prefix_length);
2433
2434 uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
2435 uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer());
2436
2437 assert(oend == obegin + length);
2438 *oend = 0;
2439
2440 out_buffer = buffer;
2441 out_length = length + 1;
2442
2443 return true;
2444 }
2445
2446 PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
2447 {
2448 // fast path: no conversion required
2449 if (encoding == encoding_utf8)
2450 return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
2451
2452 // source encoding is utf16
2453 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
2454 {
2455 xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
2456
2457 return (native_encoding == encoding) ?
2458 convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :
2459 convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());
2460 }
2461
2462 // source encoding is utf32
2463 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
2464 {
2465 xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
2466
2467 return (native_encoding == encoding) ?
2468 convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :
2469 convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());
2470 }
2471
2472 // source encoding is latin1
2473 if (encoding == encoding_latin1)
2474 return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
2475
2476 assert(false && "Invalid encoding"); // unreachable
2477 return false;
2478 }
2479#endif
2480
2481 PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length)
2482 {
2483 // get length in utf8 characters
2484 return wchar_decoder::process(str, length, 0, utf8_counter());
2485 }
2486
2487 PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)
2488 {
2489 // convert to utf8
2490 uint8_t* begin = reinterpret_cast<uint8_t*>(buffer);
2491 uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer());
2492
2493 assert(begin + size == end);
2494 (void)!end;
2495 (void)!size;
2496 }
2497
2498#ifndef PUGIXML_NO_STL
2499 PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length)
2500 {
2501 // first pass: get length in utf8 characters
2502 size_t size = as_utf8_begin(str, length);
2503
2504 // allocate resulting string
2505 std::string result;
2506 result.resize(size);
2507
2508 // second pass: convert to utf8
2509 if (size > 0) as_utf8_end(&result[0], size, str, length);
2510
2511 return result;
2512 }
2513
2514 PUGI__FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size)
2515 {
2516 const uint8_t* data = reinterpret_cast<const uint8_t*>(str);
2517
2518 // first pass: get length in wchar_t units
2519 size_t length = utf8_decoder::process(data, size, 0, wchar_counter());
2520
2521 // allocate resulting string
2522 std::basic_string<wchar_t> result;
2523 result.resize(length);
2524
2525 // second pass: convert to wchar_t
2526 if (length > 0)
2527 {
2528 wchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]);
2529 wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer());
2530
2531 assert(begin + length == end);
2532 (void)!end;
2533 }
2534
2535 return result;
2536 }
2537#endif
2538
2539 template <typename Header>
2540 inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target)
2541 {
2542 // never reuse shared memory
2543 if (header & xml_memory_page_contents_shared_mask) return false;
2544
2545 size_t target_length = strlength(target);
2546
2547 // always reuse document buffer memory if possible
2548 if ((header & header_mask) == 0) return target_length >= length;
2549
2550 // reuse heap memory if waste is not too great
2551 const size_t reuse_threshold = 32;
2552
2553 return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2);
2554 }
2555
2556 template <typename String, typename Header>
2557 PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length)
2558 {
2559 if (source_length == 0)
2560 {
2561 // empty string and null pointer are equivalent, so just deallocate old memory
2562 xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
2563
2564 if (header & header_mask) alloc->deallocate_string(dest);
2565
2566 // mark the string as not allocated
2567 dest = 0;
2568 header &= ~header_mask;
2569
2570 return true;
2571 }
2572 else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest))
2573 {
2574 // we can reuse old buffer, so just copy the new data (including zero terminator)
2575 memcpy(dest, source, source_length * sizeof(char_t));
2576 dest[source_length] = 0;
2577
2578 return true;
2579 }
2580 else
2581 {
2582 xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
2583
2584 if (!alloc->reserve()) return false;
2585
2586 // allocate new buffer
2587 char_t* buf = alloc->allocate_string(source_length + 1);
2588 if (!buf) return false;
2589
2590 // copy the string (including zero terminator)
2591 memcpy(buf, source, source_length * sizeof(char_t));
2592 buf[source_length] = 0;
2593
2594 // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures)
2595 if (header & header_mask) alloc->deallocate_string(dest);
2596
2597 // the string is now allocated, so set the flag
2598 dest = buf;
2599 header |= header_mask;
2600
2601 return true;
2602 }
2603 }
2604
2605 struct gap
2606 {
2607 char_t* end;
2608 size_t size;
2609
2610 gap(): end(0), size(0)
2611 {
2612 }
2613
2614 // Push new gap, move s count bytes further (skipping the gap).
2615 // Collapse previous gap.
2616 void push(char_t*& s, size_t count)
2617 {
2618 if (end) // there was a gap already; collapse it
2619 {
2620 // Move [old_gap_end, new_gap_start) to [old_gap_start, ...)
2621 assert(s >= end);
2622 memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));
2623 }
2624
2625 s += count; // end of current gap
2626
2627 // "merge" two gaps
2628 end = s;
2629 size += count;
2630 }
2631
2632 // Collapse all gaps, return past-the-end pointer
2633 char_t* flush(char_t* s)
2634 {
2635 if (end)
2636 {
2637 // Move [old_gap_end, current_pos) to [old_gap_start, ...)
2638 assert(s >= end);
2639 memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));
2640
2641 return s - size;
2642 }
2643 else return s;
2644 }
2645 };
2646
2647 PUGI__FN char_t* strconv_escape(char_t* s, gap& g)
2648 {
2649 char_t* stre = s + 1;
2650
2651 switch (*stre)
2652 {
2653 case '#': // &#...
2654 {
2655 unsigned int ucsc = 0;
2656
2657 if (stre[1] == 'x') // &#x... (hex code)
2658 {
2659 stre += 2;
2660
2661 char_t ch = *stre;
2662
2663 if (ch == ';') return stre;
2664
2665 for (;;)
2666 {
2667 if (static_cast<unsigned int>(ch - '0') <= 9)
2668 ucsc = 16 * ucsc + (ch - '0');
2669 else if (static_cast<unsigned int>((ch | ' ') - 'a') <= 5)
2670 ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10);
2671 else if (ch == ';')
2672 break;
2673 else // cancel
2674 return stre;
2675
2676 ch = *++stre;
2677 }
2678
2679 ++stre;
2680 }
2681 else // &#... (dec code)
2682 {
2683 char_t ch = *++stre;
2684
2685 if (ch == ';') return stre;
2686
2687 for (;;)
2688 {
2689 if (static_cast<unsigned int>(ch - '0') <= 9)
2690 ucsc = 10 * ucsc + (ch - '0');
2691 else if (ch == ';')
2692 break;
2693 else // cancel
2694 return stre;
2695
2696 ch = *++stre;
2697 }
2698
2699 ++stre;
2700 }
2701
2702 #ifdef PUGIXML_WCHAR_MODE
2703 s = reinterpret_cast<char_t*>(wchar_writer::any(reinterpret_cast<wchar_writer::value_type>(s), ucsc));
2704 #else
2705 s = reinterpret_cast<char_t*>(utf8_writer::any(reinterpret_cast<uint8_t*>(s), ucsc));
2706 #endif
2707
2708 g.push(s, stre - s);
2709 return stre;
2710 }
2711
2712 case 'a': // &a
2713 {
2714 ++stre;
2715
2716 if (*stre == 'm') // &am
2717 {
2718 if (*++stre == 'p' && *++stre == ';') // &amp;
2719 {
2720 *s++ = '&';
2721 ++stre;
2722
2723 g.push(s, stre - s);
2724 return stre;
2725 }
2726 }
2727 else if (*stre == 'p') // &ap
2728 {
2729 if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // &apos;
2730 {
2731 *s++ = '\'';
2732 ++stre;
2733
2734 g.push(s, stre - s);
2735 return stre;
2736 }
2737 }
2738 break;
2739 }
2740
2741 case 'g': // &g
2742 {
2743 if (*++stre == 't' && *++stre == ';') // &gt;
2744 {
2745 *s++ = '>';
2746 ++stre;
2747
2748 g.push(s, stre - s);
2749 return stre;
2750 }
2751 break;
2752 }
2753
2754 case 'l': // &l
2755 {
2756 if (*++stre == 't' && *++stre == ';') // &lt;
2757 {
2758 *s++ = '<';
2759 ++stre;
2760
2761 g.push(s, stre - s);
2762 return stre;
2763 }
2764 break;
2765 }
2766
2767 case 'q': // &q
2768 {
2769 if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // &quot;
2770 {
2771 *s++ = '"';
2772 ++stre;
2773
2774 g.push(s, stre - s);
2775 return stre;
2776 }
2777 break;
2778 }
2779
2780 default:
2781 break;
2782 }
2783
2784 return stre;
2785 }
2786
2787 // Parser utilities
2788 #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e)))
2789 #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
2790 #define PUGI__OPTSET(OPT) ( optmsk & (OPT) )
2791 #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
2792 #define PUGI__POPNODE() { cursor = cursor->parent; }
2793 #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; }
2794 #define PUGI__SCANWHILE(X) { while (X) ++s; }
2795 #define PUGI__SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } }
2796 #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; }
2797 #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast<char_t*>(0)
2798 #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); }
2799
2800 PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)
2801 {
2802 gap g;
2803
2804 while (true)
2805 {
2807
2808 if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
2809 {
2810 *s++ = '\n'; // replace first one with 0x0a
2811
2812 if (*s == '\n') g.push(s, 1);
2813 }
2814 else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here
2815 {
2816 *g.flush(s) = 0;
2817
2818 return s + (s[2] == '>' ? 3 : 2);
2819 }
2820 else if (*s == 0)
2821 {
2822 return 0;
2823 }
2824 else ++s;
2825 }
2826 }
2827
2828 PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch)
2829 {
2830 gap g;
2831
2832 while (true)
2833 {
2835
2836 if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
2837 {
2838 *s++ = '\n'; // replace first one with 0x0a
2839
2840 if (*s == '\n') g.push(s, 1);
2841 }
2842 else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here
2843 {
2844 *g.flush(s) = 0;
2845
2846 return s + 1;
2847 }
2848 else if (*s == 0)
2849 {
2850 return 0;
2851 }
2852 else ++s;
2853 }
2854 }
2855
2856 typedef char_t* (*strconv_pcdata_t)(char_t*);
2857
2858 template <typename opt_trim, typename opt_eol, typename opt_escape> struct strconv_pcdata_impl
2859 {
2860 static char_t* parse(char_t* s)
2861 {
2862 gap g;
2863
2864 char_t* begin = s;
2865
2866 while (true)
2867 {
2869
2870 if (*s == '<') // PCDATA ends here
2871 {
2872 char_t* end = g.flush(s);
2873
2874 if (opt_trim::value)
2875 while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
2876 --end;
2877
2878 *end = 0;
2879
2880 return s + 1;
2881 }
2882 else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
2883 {
2884 *s++ = '\n'; // replace first one with 0x0a
2885
2886 if (*s == '\n') g.push(s, 1);
2887 }
2888 else if (opt_escape::value && *s == '&')
2889 {
2890 s = strconv_escape(s, g);
2891 }
2892 else if (*s == 0)
2893 {
2894 char_t* end = g.flush(s);
2895
2896 if (opt_trim::value)
2897 while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
2898 --end;
2899
2900 *end = 0;
2901
2902 return s;
2903 }
2904 else ++s;
2905 }
2906 }
2907 };
2908
2910 {
2911 PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800);
2912
2913 switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim)
2914 {
2923 default: assert(false); return 0; // unreachable
2924 }
2925 }
2926
2927 typedef char_t* (*strconv_attribute_t)(char_t*, char_t);
2928
2929 template <typename opt_escape> struct strconv_attribute_impl
2930 {
2931 static char_t* parse_wnorm(char_t* s, char_t end_quote)
2932 {
2933 gap g;
2934
2935 // trim leading whitespaces
2936 if (PUGI__IS_CHARTYPE(*s, ct_space))
2937 {
2938 char_t* str = s;
2939
2940 do ++str;
2941 while (PUGI__IS_CHARTYPE(*str, ct_space));
2942
2943 g.push(s, str - s);
2944 }
2945
2946 while (true)
2947 {
2949
2950 if (*s == end_quote)
2951 {
2952 char_t* str = g.flush(s);
2953
2954 do *str-- = 0;
2955 while (PUGI__IS_CHARTYPE(*str, ct_space));
2956
2957 return s + 1;
2958 }
2959 else if (PUGI__IS_CHARTYPE(*s, ct_space))
2960 {
2961 *s++ = ' ';
2962
2963 if (PUGI__IS_CHARTYPE(*s, ct_space))
2964 {
2965 char_t* str = s + 1;
2966 while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str;
2967
2968 g.push(s, str - s);
2969 }
2970 }
2971 else if (opt_escape::value && *s == '&')
2972 {
2973 s = strconv_escape(s, g);
2974 }
2975 else if (!*s)
2976 {
2977 return 0;
2978 }
2979 else ++s;
2980 }
2981 }
2982
2983 static char_t* parse_wconv(char_t* s, char_t end_quote)
2984 {
2985 gap g;
2986
2987 while (true)
2988 {
2990
2991 if (*s == end_quote)
2992 {
2993 *g.flush(s) = 0;
2994
2995 return s + 1;
2996 }
2997 else if (PUGI__IS_CHARTYPE(*s, ct_space))
2998 {
2999 if (*s == '\r')
3000 {
3001 *s++ = ' ';
3002
3003 if (*s == '\n') g.push(s, 1);
3004 }
3005 else *s++ = ' ';
3006 }
3007 else if (opt_escape::value && *s == '&')
3008 {
3009 s = strconv_escape(s, g);
3010 }
3011 else if (!*s)
3012 {
3013 return 0;
3014 }
3015 else ++s;
3016 }
3017 }
3018
3019 static char_t* parse_eol(char_t* s, char_t end_quote)
3020 {
3021 gap g;
3022
3023 while (true)
3024 {
3026
3027 if (*s == end_quote)
3028 {
3029 *g.flush(s) = 0;
3030
3031 return s + 1;
3032 }
3033 else if (*s == '\r')
3034 {
3035 *s++ = '\n';
3036
3037 if (*s == '\n') g.push(s, 1);
3038 }
3039 else if (opt_escape::value && *s == '&')
3040 {
3041 s = strconv_escape(s, g);
3042 }
3043 else if (!*s)
3044 {
3045 return 0;
3046 }
3047 else ++s;
3048 }
3049 }
3050
3051 static char_t* parse_simple(char_t* s, char_t end_quote)
3052 {
3053 gap g;
3054
3055 while (true)
3056 {
3058
3059 if (*s == end_quote)
3060 {
3061 *g.flush(s) = 0;
3062
3063 return s + 1;
3064 }
3065 else if (opt_escape::value && *s == '&')
3066 {
3067 s = strconv_escape(s, g);
3068 }
3069 else if (!*s)
3070 {
3071 return 0;
3072 }
3073 else ++s;
3074 }
3075 }
3076 };
3077
3079 {
3080 PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
3081
3082 switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes)
3083 {
3100 default: assert(false); return 0; // unreachable
3101 }
3102 }
3103
3104 inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0)
3105 {
3106 xml_parse_result result;
3107 result.status = status;
3108 result.offset = offset;
3109
3110 return result;
3111 }
3112
3114 {
3117 xml_parse_status error_status;
3118
3119 xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
3120 {
3121 }
3122
3123 // DOCTYPE consists of nested sections of the following possible types:
3124 // <!-- ... -->, <? ... ?>, "...", '...'
3125 // <![...]]>
3126 // <!...>
3127 // First group can not contain nested groups
3128 // Second group can contain nested groups of the same type
3129 // Third group can contain all other groups
3130 char_t* parse_doctype_primitive(char_t* s)
3131 {
3132 if (*s == '"' || *s == '\'')
3133 {
3134 // quoted string
3135 char_t ch = *s++;
3136 PUGI__SCANFOR(*s == ch);
3137 if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
3138
3139 s++;
3140 }
3141 else if (s[0] == '<' && s[1] == '?')
3142 {
3143 // <? ... ?>
3144 s += 2;
3145 PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype
3146 if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
3147
3148 s += 2;
3149 }
3150 else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-')
3151 {
3152 s += 4;
3153 PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype
3154 if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
3155
3156 s += 3;
3157 }
3158 else PUGI__THROW_ERROR(status_bad_doctype, s);
3159
3160 return s;
3161 }
3162
3163 char_t* parse_doctype_ignore(char_t* s)
3164 {
3165 size_t depth = 0;
3166
3167 assert(s[0] == '<' && s[1] == '!' && s[2] == '[');
3168 s += 3;
3169
3170 while (*s)
3171 {
3172 if (s[0] == '<' && s[1] == '!' && s[2] == '[')
3173 {
3174 // nested ignore section
3175 s += 3;
3176 depth++;
3177 }
3178 else if (s[0] == ']' && s[1] == ']' && s[2] == '>')
3179 {
3180 // ignore section end
3181 s += 3;
3182
3183 if (depth == 0)
3184 return s;
3185
3186 depth--;
3187 }
3188 else s++;
3189 }
3190
3191 PUGI__THROW_ERROR(status_bad_doctype, s);
3192 }
3193
3194 char_t* parse_doctype_group(char_t* s, char_t endch)
3195 {
3196 size_t depth = 0;
3197
3198 assert((s[0] == '<' || s[0] == 0) && s[1] == '!');
3199 s += 2;
3200
3201 while (*s)
3202 {
3203 if (s[0] == '<' && s[1] == '!' && s[2] != '-')
3204 {
3205 if (s[2] == '[')
3206 {
3207 // ignore
3208 s = parse_doctype_ignore(s);
3209 if (!s) return s;
3210 }
3211 else
3212 {
3213 // some control group
3214 s += 2;
3215 depth++;
3216 }
3217 }
3218 else if (s[0] == '<' || s[0] == '"' || s[0] == '\'')
3219 {
3220 // unknown tag (forbidden), or some primitive group
3222 if (!s) return s;
3223 }
3224 else if (*s == '>')
3225 {
3226 if (depth == 0)
3227 return s;
3228
3229 depth--;
3230 s++;
3231 }
3232 else s++;
3233 }
3234
3235 if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
3236
3237 return s;
3238 }
3239
3240 char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch)
3241 {
3242 // parse node contents, starting with exclamation mark
3243 ++s;
3244
3245 if (*s == '-') // '<!-...'
3246 {
3247 ++s;
3248
3249 if (*s == '-') // '<!--...'
3250 {
3251 ++s;
3252
3253 if (PUGI__OPTSET(parse_comments))
3254 {
3255 PUGI__PUSHNODE(node_comment); // Append a new node on the tree.
3256 cursor->value = s; // Save the offset.
3257 }
3258
3259 if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments))
3260 {
3261 s = strconv_comment(s, endch);
3262
3263 if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value);
3264 }
3265 else
3266 {
3267 // Scan for terminating '-->'.
3268 PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>'));
3269 PUGI__CHECK_ERROR(status_bad_comment, s);
3270
3271 if (PUGI__OPTSET(parse_comments))
3272 *s = 0; // Zero-terminate this segment at the first terminating '-'.
3273
3274 s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'.
3275 }
3276 }
3277 else PUGI__THROW_ERROR(status_bad_comment, s);
3278 }
3279 else if (*s == '[')
3280 {
3281 // '<![CDATA[...'
3282 if (*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[')
3283 {
3284 ++s;
3285
3286 if (PUGI__OPTSET(parse_cdata))
3287 {
3288 PUGI__PUSHNODE(node_cdata); // Append a new node on the tree.
3289 cursor->value = s; // Save the offset.
3290
3291 if (PUGI__OPTSET(parse_eol))
3292 {
3293 s = strconv_cdata(s, endch);
3294
3295 if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value);
3296 }
3297 else
3298 {
3299 // Scan for terminating ']]>'.
3300 PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
3301 PUGI__CHECK_ERROR(status_bad_cdata, s);
3302
3303 *s++ = 0; // Zero-terminate this segment.
3304 }
3305 }
3306 else // Flagged for discard, but we still have to scan for the terminator.
3307 {
3308 // Scan for terminating ']]>'.
3309 PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
3310 PUGI__CHECK_ERROR(status_bad_cdata, s);
3311
3312 ++s;
3313 }
3314
3315 s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'.
3316 }
3317 else PUGI__THROW_ERROR(status_bad_cdata, s);
3318 }
3319 else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E'))
3320 {
3321 s -= 2;
3322
3323 if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s);
3324
3325 char_t* mark = s + 9;
3326
3327 s = parse_doctype_group(s, endch);
3328 if (!s) return s;
3329
3330 assert((*s == 0 && endch == '>') || *s == '>');
3331 if (*s) *s++ = 0;
3332
3333 if (PUGI__OPTSET(parse_doctype))
3334 {
3335 while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark;
3336
3337 PUGI__PUSHNODE(node_doctype);
3338
3339 cursor->value = mark;
3340 }
3341 }
3342 else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s);
3343 else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s);
3344 else PUGI__THROW_ERROR(status_unrecognized_tag, s);
3345
3346 return s;
3347 }
3348
3349 char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch)
3350 {
3351 // load into registers
3352 xml_node_struct* cursor = ref_cursor;
3353 char_t ch = 0;
3354
3355 // parse node contents, starting with question mark
3356 ++s;
3357
3358 // read PI target
3359 char_t* target = s;
3360
3361 if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s);
3362
3364 PUGI__CHECK_ERROR(status_bad_pi, s);
3365
3366 // determine node type; stricmp / strcasecmp is not portable
3367 bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s;
3368
3369 if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi))
3370 {
3371 if (declaration)
3372 {
3373 // disallow non top-level declarations
3374 if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s);
3375
3376 PUGI__PUSHNODE(node_declaration);
3377 }
3378 else
3379 {
3380 PUGI__PUSHNODE(node_pi);
3381 }
3382
3383 cursor->name = target;
3384
3385 PUGI__ENDSEG();
3386
3387 // parse value/attributes
3388 if (ch == '?')
3389 {
3390 // empty node
3391 if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
3392 s += (*s == '>');
3393
3394 PUGI__POPNODE();
3395 }
3396 else if (PUGI__IS_CHARTYPE(ch, ct_space))
3397 {
3398 PUGI__SKIPWS();
3399
3400 // scan for tag end
3401 char_t* value = s;
3402
3403 PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
3404 PUGI__CHECK_ERROR(status_bad_pi, s);
3405
3406 if (declaration)
3407 {
3408 // replace ending ? with / so that 'element' terminates properly
3409 *s = '/';
3410
3411 // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES
3412 s = value;
3413 }
3414 else
3415 {
3416 // store value and step over >
3417 cursor->value = value;
3418
3419 PUGI__POPNODE();
3420
3421 PUGI__ENDSEG();
3422
3423 s += (*s == '>');
3424 }
3425 }
3426 else PUGI__THROW_ERROR(status_bad_pi, s);
3427 }
3428 else
3429 {
3430 // scan for tag end
3431 PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
3432 PUGI__CHECK_ERROR(status_bad_pi, s);
3433
3434 s += (s[1] == '>' ? 2 : 1);
3435 }
3436
3437 // store from registers
3438 ref_cursor = cursor;
3439
3440 return s;
3441 }
3442
3443 char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch)
3444 {
3445 strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk);
3446 strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk);
3447
3448 char_t ch = 0;
3449 xml_node_struct* cursor = root;
3450 char_t* mark = s;
3451
3452 while (*s != 0)
3453 {
3454 if (*s == '<')
3455 {
3456 ++s;
3457
3458 LOC_TAG:
3459 if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...'
3460 {
3461 PUGI__PUSHNODE(node_element); // Append a new node to the tree.
3462
3463 cursor->name = s;
3464
3465 PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
3466 PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
3467
3468 if (ch == '>')
3469 {
3470 // end of tag
3471 }
3472 else if (PUGI__IS_CHARTYPE(ch, ct_space))
3473 {
3474 LOC_ATTRIBUTES:
3475 while (true)
3476 {
3477 PUGI__SKIPWS(); // Eat any whitespace.
3478
3479 if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
3480 {
3481 xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute.
3482 if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
3483
3484 a->name = s; // Save the offset.
3485
3486 PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
3487 PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
3488
3489 if (PUGI__IS_CHARTYPE(ch, ct_space))
3490 {
3491 PUGI__SKIPWS(); // Eat any whitespace.
3492
3493 ch = *s;
3494 ++s;
3495 }
3496
3497 if (ch == '=') // '<... #=...'
3498 {
3499 PUGI__SKIPWS(); // Eat any whitespace.
3500
3501 if (*s == '"' || *s == '\'') // '<... #="...'
3502 {
3503 ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.
3504 ++s; // Step over the quote.
3505 a->value = s; // Save the offset.
3506
3507 s = strconv_attribute(s, ch);
3508
3509 if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value);
3510
3511 // After this line the loop continues from the start;
3512 // Whitespaces, / and > are ok, symbols and EOF are wrong,
3513 // everything else will be detected
3514 if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s);
3515 }
3516 else PUGI__THROW_ERROR(status_bad_attribute, s);
3517 }
3518 else PUGI__THROW_ERROR(status_bad_attribute, s);
3519 }
3520 else if (*s == '/')
3521 {
3522 ++s;
3523
3524 if (*s == '>')
3525 {
3526 PUGI__POPNODE();
3527 s++;
3528 break;
3529 }
3530 else if (*s == 0 && endch == '>')
3531 {
3532 PUGI__POPNODE();
3533 break;
3534 }
3535 else PUGI__THROW_ERROR(status_bad_start_element, s);
3536 }
3537 else if (*s == '>')
3538 {
3539 ++s;
3540
3541 break;
3542 }
3543 else if (*s == 0 && endch == '>')
3544 {
3545 break;
3546 }
3547 else PUGI__THROW_ERROR(status_bad_start_element, s);
3548 }
3549
3550 // !!!
3551 }
3552 else if (ch == '/') // '<#.../'
3553 {
3554 if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
3555
3556 PUGI__POPNODE(); // Pop.
3557
3558 s += (*s == '>');
3559 }
3560 else if (ch == 0)
3561 {
3562 // we stepped over null terminator, backtrack & handle closing tag
3563 --s;
3564
3565 if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s);
3566 }
3567 else PUGI__THROW_ERROR(status_bad_start_element, s);
3568 }
3569 else if (*s == '/')
3570 {
3571 ++s;
3572
3573 mark = s;
3574
3575 char_t* name = cursor->name;
3576 if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
3577
3578 while (PUGI__IS_CHARTYPE(*s, ct_symbol))
3579 {
3580 if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
3581 }
3582
3583 if (*name)
3584 {
3585 if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s);
3586 else PUGI__THROW_ERROR(status_end_element_mismatch, mark);
3587 }
3588
3589 PUGI__POPNODE(); // Pop.
3590
3591 PUGI__SKIPWS();
3592
3593 if (*s == 0)
3594 {
3595 if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
3596 }
3597 else
3598 {
3599 if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
3600 ++s;
3601 }
3602 }
3603 else if (*s == '?') // '<?...'
3604 {
3605 s = parse_question(s, cursor, optmsk, endch);
3606 if (!s) return s;
3607
3608 assert(cursor);
3609 if (PUGI__NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES;
3610 }
3611 else if (*s == '!') // '<!...'
3612 {
3613 s = parse_exclamation(s, cursor, optmsk, endch);
3614 if (!s) return s;
3615 }
3616 else if (*s == 0 && endch == '?') PUGI__THROW_ERROR(status_bad_pi, s);
3617 else PUGI__THROW_ERROR(status_unrecognized_tag, s);
3618 }
3619 else
3620 {
3621 mark = s; // Save this offset while searching for a terminator.
3622
3623 PUGI__SKIPWS(); // Eat whitespace if no genuine PCDATA here.
3624
3625 if (*s == '<' || !*s)
3626 {
3627 // We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one
3628 assert(mark != s);
3629
3630 if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI__OPTSET(parse_trim_pcdata))
3631 {
3632 continue;
3633 }
3634 else if (PUGI__OPTSET(parse_ws_pcdata_single))
3635 {
3636 if (s[0] != '<' || s[1] != '/' || cursor->first_child) continue;
3637 }
3638 }
3639
3640 if (!PUGI__OPTSET(parse_trim_pcdata))
3641 s = mark;
3642
3643 if (cursor->parent || PUGI__OPTSET(parse_fragment))
3644 {
3645 if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value)
3646 {
3647 cursor->value = s; // Save the offset.
3648 }
3649 else
3650 {
3651 PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
3652
3653 cursor->value = s; // Save the offset.
3654
3655 PUGI__POPNODE(); // Pop since this is a standalone.
3656 }
3657
3658 s = strconv_pcdata(s);
3659
3660 if (!*s) break;
3661 }
3662 else
3663 {
3664 PUGI__SCANFOR(*s == '<'); // '...<'
3665 if (!*s) break;
3666
3667 ++s;
3668 }
3669
3670 // We're after '<'
3671 goto LOC_TAG;
3672 }
3673 }
3674
3675 // check that last tag is closed
3676 if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s);
3677
3678 return s;
3679 }
3680
3681 #ifdef PUGIXML_WCHAR_MODE
3682 static char_t* parse_skip_bom(char_t* s)
3683 {
3684 unsigned int bom = 0xfeff;
3685 return (s[0] == static_cast<wchar_t>(bom)) ? s + 1 : s;
3686 }
3687 #else
3688 static char_t* parse_skip_bom(char_t* s)
3689 {
3690 return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s;
3691 }
3692 #endif
3693
3695 {
3696 while (node)
3697 {
3698 if (PUGI__NODETYPE(node) == node_element) return true;
3699
3700 node = node->next_sibling;
3701 }
3702
3703 return false;
3704 }
3705
3706 static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk)
3707 {
3708 // early-out for empty documents
3709 if (length == 0)
3710 return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);
3711
3712 // get last child of the root before parsing
3713 xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0;
3714
3715 // create parser on stack
3716 xml_parser parser(static_cast<xml_allocator*>(xmldoc));
3717
3718 // save last character and make buffer zero-terminated (speeds up parsing)
3719 char_t endch = buffer[length - 1];
3720 buffer[length - 1] = 0;
3721
3722 // skip BOM to make sure it does not end up as part of parse output
3723 char_t* buffer_data = parse_skip_bom(buffer);
3724
3725 // perform actual parsing
3726 parser.parse_tree(buffer_data, root, optmsk, endch);
3727
3728 xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
3729 assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);
3730
3731 if (result)
3732 {
3733 // since we removed last character, we have to handle the only possible false positive (stray <)
3734 if (endch == '<')
3735 return make_parse_result(status_unrecognized_tag, length - 1);
3736
3737 // check if there are any element nodes parsed
3738 xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child+ 0;
3739
3740 if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed))
3741 return make_parse_result(status_no_document_element, length - 1);
3742 }
3743 else
3744 {
3745 // roll back offset if it occurs on a null terminator in the source buffer
3746 if (result.offset > 0 && static_cast<size_t>(result.offset) == length - 1 && endch == 0)
3747 result.offset--;
3748 }
3749
3750 return result;
3751 }
3752 };
3753
3754 // Output facilities
3756 {
3757 #ifdef PUGIXML_WCHAR_MODE
3758 return get_wchar_encoding();
3759 #else
3760 return encoding_utf8;
3761 #endif
3762 }
3763
3764 PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding)
3765 {
3766 // replace wchar encoding with utf implementation
3767 if (encoding == encoding_wchar) return get_wchar_encoding();
3768
3769 // replace utf16 encoding with utf16 with specific endianness
3770 if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
3771
3772 // replace utf32 encoding with utf32 with specific endianness
3773 if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
3774
3775 // only do autodetection if no explicit encoding is requested
3776 if (encoding != encoding_auto) return encoding;
3777
3778 // assume utf8 encoding
3779 return encoding_utf8;
3780 }
3781
3782 template <typename D, typename T> PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T)
3783 {
3784 PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));
3785
3786 typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());
3787
3788 return static_cast<size_t>(end - dest) * sizeof(*dest);
3789 }
3790
3791 template <typename D, typename T> PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap)
3792 {
3793 PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));
3794
3795 typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());
3796
3797 if (opt_swap)
3798 {
3799 for (typename T::value_type i = dest; i != end; ++i)
3800 *i = endian_swap(*i);
3801 }
3802
3803 return static_cast<size_t>(end - dest) * sizeof(*dest);
3804 }
3805
3806#ifdef PUGIXML_WCHAR_MODE
3807 PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
3808 {
3809 if (length < 1) return 0;
3810
3811 // discard last character if it's the lead of a surrogate pair
3812 return (sizeof(wchar_t) == 2 && static_cast<unsigned int>(static_cast<uint16_t>(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length;
3813 }
3814
3815 PUGI__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)
3816 {
3817 // only endian-swapping is required
3818 if (need_endian_swap_utf(encoding, get_wchar_encoding()))
3819 {
3820 convert_wchar_endian_swap(r_char, data, length);
3821
3822 return length * sizeof(char_t);
3823 }
3824
3825 // convert to utf8
3826 if (encoding == encoding_utf8)
3827 return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer());
3828
3829 // convert to utf16
3830 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
3831 {
3832 xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
3833
3834 return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding);
3835 }
3836
3837 // convert to utf32
3838 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
3839 {
3840 xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
3841
3842 return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding);
3843 }
3844
3845 // convert to latin1
3846 if (encoding == encoding_latin1)
3847 return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer());
3848
3849 assert(false && "Invalid encoding"); // unreachable
3850 return 0;
3851 }
3852#else
3853 PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
3854 {
3855 if (length < 5) return 0;
3856
3857 for (size_t i = 1; i <= 4; ++i)
3858 {
3859 uint8_t ch = static_cast<uint8_t>(data[length - i]);
3860
3861 // either a standalone character or a leading one
3862 if ((ch & 0xc0) != 0x80) return length - i;
3863 }
3864
3865 // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk
3866 return length;
3867 }
3868
3869 PUGI__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)
3870 {
3871 if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
3872 {
3873 xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
3874
3875 return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding);
3876 }
3877
3878 if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
3879 {
3880 xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
3881
3882 return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding);
3883 }
3884
3885 if (encoding == encoding_latin1)
3886 return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer());
3887
3888 assert(false && "Invalid encoding"); // unreachable
3889 return 0;
3890 }
3891#endif
3892
3894 {
3897
3898 public:
3899 xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding))
3900 {
3902 }
3903
3904 size_t flush()
3905 {
3907 bufsize = 0;
3908 return 0;
3909 }
3910
3911 void flush(const char_t* data, size_t size)
3912 {
3913 if (size == 0) return;
3914
3915 // fast path, just write data
3917 writer.write(data, size * sizeof(char_t));
3918 else
3919 {
3920 // convert chunk
3921 size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);
3922 assert(result <= sizeof(scratch));
3923
3924 // write data
3925 writer.write(scratch.data_u8, result);
3926 }
3927 }
3928
3929 void write_direct(const char_t* data, size_t length)
3930 {
3931 // flush the remaining buffer contents
3932 flush();
3933
3934 // handle large chunks
3935 if (length > bufcapacity)
3936 {
3938 {
3939 // fast path, can just write data chunk
3940 writer.write(data, length * sizeof(char_t));
3941 return;
3942 }
3943
3944 // need to convert in suitable chunks
3945 while (length > bufcapacity)
3946 {
3947 // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
3948 // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
3949 size_t chunk_size = get_valid_length(data, bufcapacity);
3950 assert(chunk_size);
3951
3952 // convert chunk and write
3953 flush(data, chunk_size);
3954
3955 // iterate
3956 data += chunk_size;
3957 length -= chunk_size;
3958 }
3959
3960 // small tail is copied below
3961 bufsize = 0;
3962 }
3963
3964 memcpy(buffer + bufsize, data, length * sizeof(char_t));
3965 bufsize += length;
3966 }
3967
3968 void write_buffer(const char_t* data, size_t length)
3969 {
3970 size_t offset = bufsize;
3971
3972 if (offset + length <= bufcapacity)
3973 {
3974 memcpy(buffer + offset, data, length * sizeof(char_t));
3975 bufsize = offset + length;
3976 }
3977 else
3978 {
3979 write_direct(data, length);
3980 }
3981 }
3982
3983 void write_string(const char_t* data)
3984 {
3985 // write the part of the string that fits in the buffer
3986 size_t offset = bufsize;
3987
3988 while (*data && offset < bufcapacity)
3989 buffer[offset++] = *data++;
3990
3991 // write the rest
3992 if (offset < bufcapacity)
3993 {
3994 bufsize = offset;
3995 }
3996 else
3997 {
3998 // backtrack a bit if we have split the codepoint
3999 size_t length = offset - bufsize;
4000 size_t extra = length - get_valid_length(data - length, length);
4001
4002 bufsize = offset - extra;
4003
4004 write_direct(data - extra, strlength(data) + extra);
4005 }
4006 }
4007
4008 void write(char_t d0)
4009 {
4010 size_t offset = bufsize;
4011 if (offset > bufcapacity - 1) offset = flush();
4012
4013 buffer[offset + 0] = d0;
4014 bufsize = offset + 1;
4015 }
4016
4017 void write(char_t d0, char_t d1)
4018 {
4019 size_t offset = bufsize;
4020 if (offset > bufcapacity - 2) offset = flush();
4021
4022 buffer[offset + 0] = d0;
4023 buffer[offset + 1] = d1;
4024 bufsize = offset + 2;
4025 }
4026
4027 void write(char_t d0, char_t d1, char_t d2)
4028 {
4029 size_t offset = bufsize;
4030 if (offset > bufcapacity - 3) offset = flush();
4031
4032 buffer[offset + 0] = d0;
4033 buffer[offset + 1] = d1;
4034 buffer[offset + 2] = d2;
4035 bufsize = offset + 3;
4036 }
4037
4038 void write(char_t d0, char_t d1, char_t d2, char_t d3)
4039 {
4040 size_t offset = bufsize;
4041 if (offset > bufcapacity - 4) offset = flush();
4042
4043 buffer[offset + 0] = d0;
4044 buffer[offset + 1] = d1;
4045 buffer[offset + 2] = d2;
4046 buffer[offset + 3] = d3;
4047 bufsize = offset + 4;
4048 }
4049
4050 void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)
4051 {
4052 size_t offset = bufsize;
4053 if (offset > bufcapacity - 5) offset = flush();
4054
4055 buffer[offset + 0] = d0;
4056 buffer[offset + 1] = d1;
4057 buffer[offset + 2] = d2;
4058 buffer[offset + 3] = d3;
4059 buffer[offset + 4] = d4;
4060 bufsize = offset + 5;
4061 }
4062
4063 void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)
4064 {
4065 size_t offset = bufsize;
4066 if (offset > bufcapacity - 6) offset = flush();
4067
4068 buffer[offset + 0] = d0;
4069 buffer[offset + 1] = d1;
4070 buffer[offset + 2] = d2;
4071 buffer[offset + 3] = d3;
4072 buffer[offset + 4] = d4;
4073 buffer[offset + 5] = d5;
4074 bufsize = offset + 6;
4075 }
4076
4077 // utf8 maximum expansion: x4 (-> utf32)
4078 // utf16 maximum expansion: x2 (-> utf32)
4079 // utf32 maximum expansion: x1
4080 enum
4081 {
4083 #ifdef PUGIXML_MEMORY_OUTPUT_STACK
4084 PUGIXML_MEMORY_OUTPUT_STACK
4085 #else
4086 10240
4087 #endif
4089 bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4)
4091
4093
4094 union
4095 {
4096 uint8_t data_u8[4 * bufcapacity];
4097 uint16_t data_u16[2 * bufcapacity];
4101
4102 xml_writer& writer;
4103 size_t bufsize;
4104 xml_encoding encoding;
4105 };
4106
4108 {
4109 while (*s)
4110 {
4111 const char_t* prev = s;
4112
4113 // While *s is a usual symbol
4115
4116 writer.write_buffer(prev, static_cast<size_t>(s - prev));
4117
4118 switch (*s)
4119 {
4120 case 0: break;
4121 case '&':
4122 writer.write('&', 'a', 'm', 'p', ';');
4123 ++s;
4124 break;
4125 case '<':
4126 writer.write('&', 'l', 't', ';');
4127 ++s;
4128 break;
4129 case '>':
4130 writer.write('&', 'g', 't', ';');
4131 ++s;
4132 break;
4133 case '"':
4134 writer.write('&', 'q', 'u', 'o', 't', ';');
4135 ++s;
4136 break;
4137 default: // s is not a usual symbol
4138 {
4139 unsigned int ch = static_cast<unsigned int>(*s++);
4140 assert(ch < 32);
4141
4142 writer.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';');
4143 }
4144 }
4145 }
4146 }
4147
4148 PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
4149 {
4150 if (flags & format_no_escapes)
4151 writer.write_string(s);
4152 else
4153 text_output_escaped(writer, s, type);
4154 }
4155
4156 PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)
4157 {
4158 do
4159 {
4160 writer.write('<', '!', '[', 'C', 'D');
4161 writer.write('A', 'T', 'A', '[');
4162
4163 const char_t* prev = s;
4164
4165 // look for ]]> sequence - we can't output it as is since it terminates CDATA
4166 while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s;
4167
4168 // skip ]] if we stopped at ]]>, > will go to the next CDATA section
4169 if (*s) s += 2;
4170
4171 writer.write_buffer(prev, static_cast<size_t>(s - prev));
4172
4173 writer.write(']', ']', '>');
4174 }
4175 while (*s);
4176 }
4177
4178 PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth)
4179 {
4180 switch (indent_length)
4181 {
4182 case 1:
4183 {
4184 for (unsigned int i = 0; i < depth; ++i)
4185 writer.write(indent[0]);
4186 break;
4187 }
4188
4189 case 2:
4190 {
4191 for (unsigned int i = 0; i < depth; ++i)
4192 writer.write(indent[0], indent[1]);
4193 break;
4194 }
4195
4196 case 3:
4197 {
4198 for (unsigned int i = 0; i < depth; ++i)
4199 writer.write(indent[0], indent[1], indent[2]);
4200 break;
4201 }
4202
4203 case 4:
4204 {
4205 for (unsigned int i = 0; i < depth; ++i)
4206 writer.write(indent[0], indent[1], indent[2], indent[3]);
4207 break;
4208 }
4209
4210 default:
4211 {
4212 for (unsigned int i = 0; i < depth; ++i)
4213 writer.write_buffer(indent, indent_length);
4214 }
4215 }
4216 }
4217
4218 PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s)
4219 {
4220 writer.write('<', '!', '-', '-');
4221
4222 while (*s)
4223 {
4224 const char_t* prev = s;
4225
4226 // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body
4227 while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s;
4228
4229 writer.write_buffer(prev, static_cast<size_t>(s - prev));
4230
4231 if (*s)
4232 {
4233 assert(*s == '-');
4234
4235 writer.write('-', ' ');
4236 ++s;
4237 }
4238 }
4239
4240 writer.write('-', '-', '>');
4241 }
4242
4244 {
4245 while (*s)
4246 {
4247 const char_t* prev = s;
4248
4249 // look for ?> sequence - we can't output it since ?> terminates PI
4250 while (*s && !(s[0] == '?' && s[1] == '>')) ++s;
4251
4252 writer.write_buffer(prev, static_cast<size_t>(s - prev));
4253
4254 if (*s)
4255 {
4256 assert(s[0] == '?' && s[1] == '>');
4257
4258 writer.write('?', ' ', '>');
4259 s += 2;
4260 }
4261 }
4262 }
4263
4264 PUGI__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)
4265 {
4266 const char_t* default_name = PUGIXML_TEXT(":anonymous");
4267
4268 for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
4269 {
4270 if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes)
4271 {
4272 writer.write('\n');
4273
4274 text_output_indent(writer, indent, indent_length, depth + 1);
4275 }
4276 else
4277 {
4278 writer.write(' ');
4279 }
4280
4281 writer.write_string(a->name ? a->name + 0 : default_name);
4282 writer.write('=', '"');
4283
4284 if (a->value)
4285 text_output(writer, a->value, ctx_special_attr, flags);
4286
4287 writer.write('"');
4288 }
4289 }
4290
4291 PUGI__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)
4292 {
4293 const char_t* default_name = PUGIXML_TEXT(":anonymous");
4294 const char_t* name = node->name ? node->name + 0 : default_name;
4295
4296 writer.write('<');
4297 writer.write_string(name);
4298
4299 if (node->first_attribute)
4300 node_output_attributes(writer, node, indent, indent_length, flags, depth);
4301
4302 // element nodes can have value if parse_embed_pcdata was used
4303 if (!node->value)
4304 {
4305 if (!node->first_child)
4306 {
4307 if (flags & format_no_empty_element_tags)
4308 {
4309 writer.write('>', '<', '/');
4310 writer.write_string(name);
4311 writer.write('>');
4312
4313 return false;
4314 }
4315 else
4316 {
4317 if ((flags & format_raw) == 0)
4318 writer.write(' ');
4319
4320 writer.write('/', '>');
4321
4322 return false;
4323 }
4324 }
4325 else
4326 {
4327 writer.write('>');
4328
4329 return true;
4330 }
4331 }
4332 else
4333 {
4334 writer.write('>');
4335
4336 text_output(writer, node->value, ctx_special_pcdata, flags);
4337
4338 if (!node->first_child)
4339 {
4340 writer.write('<', '/');
4341 writer.write_string(name);
4342 writer.write('>');
4343
4344 return false;
4345 }
4346 else
4347 {
4348 return true;
4349 }
4350 }
4351 }
4352
4354 {
4355 const char_t* default_name = PUGIXML_TEXT(":anonymous");
4356 const char_t* name = node->name ? node->name + 0 : default_name;
4357
4358 writer.write('<', '/');
4359 writer.write_string(name);
4360 writer.write('>');
4361 }
4362
4363 PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
4364 {
4365 const char_t* default_name = PUGIXML_TEXT(":anonymous");
4366
4367 switch (PUGI__NODETYPE(node))
4368 {
4369 case node_pcdata:
4370 text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
4371 break;
4372
4373 case node_cdata:
4374 text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
4375 break;
4376
4377 case node_comment:
4378 node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
4379 break;
4380
4381 case node_pi:
4382 writer.write('<', '?');
4383 writer.write_string(node->name ? node->name + 0 : default_name);
4384
4385 if (node->value)
4386 {
4387 writer.write(' ');
4388 node_output_pi_value(writer, node->value);
4389 }
4390
4391 writer.write('?', '>');
4392 break;
4393
4394 case node_declaration:
4395 writer.write('<', '?');
4396 writer.write_string(node->name ? node->name + 0 : default_name);
4397 node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0);
4398 writer.write('?', '>');
4399 break;
4400
4401 case node_doctype:
4402 writer.write('<', '!', 'D', 'O', 'C');
4403 writer.write('T', 'Y', 'P', 'E');
4404
4405 if (node->value)
4406 {
4407 writer.write(' ');
4408 writer.write_string(node->value);
4409 }
4410
4411 writer.write('>');
4412 break;
4413
4414 default:
4415 assert(false && "Invalid node type"); // unreachable
4416 }
4417 }
4418
4424
4425 PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth)
4426 {
4427 size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0;
4428 unsigned int indent_flags = indent_indent;
4429
4430 xml_node_struct* node = root;
4431
4432 do
4433 {
4434 assert(node);
4435
4436 // begin writing current node
4437 if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata)
4438 {
4439 node_output_simple(writer, node, flags);
4440
4441 indent_flags = 0;
4442 }
4443 else
4444 {
4445 if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
4446 writer.write('\n');
4447
4448 if ((indent_flags & indent_indent) && indent_length)
4449 text_output_indent(writer, indent, indent_length, depth);
4450
4451 if (PUGI__NODETYPE(node) == node_element)
4452 {
4453 indent_flags = indent_newline | indent_indent;
4454
4455 if (node_output_start(writer, node, indent, indent_length, flags, depth))
4456 {
4457 // element nodes can have value if parse_embed_pcdata was used
4458 if (node->value)
4459 indent_flags = 0;
4460
4461 node = node->first_child;
4462 depth++;
4463 continue;
4464 }
4465 }
4466 else if (PUGI__NODETYPE(node) == node_document)
4467 {
4468 indent_flags = indent_indent;
4469
4470 if (node->first_child)
4471 {
4472 node = node->first_child;
4473 continue;
4474 }
4475 }
4476 else
4477 {
4478 node_output_simple(writer, node, flags);
4479
4480 indent_flags = indent_newline | indent_indent;
4481 }
4482 }
4483
4484 // continue to the next node
4485 while (node != root)
4486 {
4487 if (node->next_sibling)
4488 {
4489 node = node->next_sibling;
4490 break;
4491 }
4492
4493 node = node->parent;
4494
4495 // write closing node
4496 if (PUGI__NODETYPE(node) == node_element)
4497 {
4498 depth--;
4499
4500 if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
4501 writer.write('\n');
4502
4503 if ((indent_flags & indent_indent) && indent_length)
4504 text_output_indent(writer, indent, indent_length, depth);
4505
4506 node_output_end(writer, node);
4507
4508 indent_flags = indent_newline | indent_indent;
4509 }
4510 }
4511 }
4512 while (node != root);
4513
4514 if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
4515 writer.write('\n');
4516 }
4517
4519 {
4520 for (xml_node_struct* child = node->first_child; child; child = child->next_sibling)
4521 {
4522 xml_node_type type = PUGI__NODETYPE(child);
4523
4524 if (type == node_declaration) return true;
4525 if (type == node_element) return false;
4526 }
4527
4528 return false;
4529 }
4530
4531 PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node)
4532 {
4533 for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
4534 if (a == attr)
4535 return true;
4536
4537 return false;
4538 }
4539
4540 PUGI__FN bool allow_insert_attribute(xml_node_type parent)
4541 {
4542 return parent == node_element || parent == node_declaration;
4543 }
4544
4545 PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child)
4546 {
4547 if (parent != node_document && parent != node_element) return false;
4548 if (child == node_document || child == node_null) return false;
4549 if (parent != node_document && (child == node_declaration || child == node_doctype)) return false;
4550
4551 return true;
4552 }
4553
4554 PUGI__FN bool allow_move(xml_node parent, xml_node child)
4555 {
4556 // check that child can be a child of parent
4557 if (!allow_insert_child(parent.type(), child.type()))
4558 return false;
4559
4560 // check that node is not moved between documents
4561 if (parent.root() != child.root())
4562 return false;
4563
4564 // check that new parent is not in the child subtree
4565 xml_node cur = parent;
4566
4567 while (cur)
4568 {
4569 if (cur == child)
4570 return false;
4571
4572 cur = cur.parent();
4573 }
4574
4575 return true;
4576 }
4577
4578 template <typename String, typename Header>
4579 PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc)
4580 {
4581 assert(!dest && (header & header_mask) == 0);
4582
4583 if (source)
4584 {
4585 if (alloc && (source_header & header_mask) == 0)
4586 {
4587 dest = source;
4588
4589 // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared
4591 source_header |= xml_memory_page_contents_shared_mask;
4592 }
4593 else
4594 strcpy_insitu(dest, header, header_mask, source, strlength(source));
4595 }
4596 }
4597
4599 {
4600 node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc);
4601 node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc);
4602
4603 for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute)
4604 {
4605 xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn));
4606
4607 if (da)
4608 {
4609 node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
4610 node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
4611 }
4612 }
4613 }
4614
4616 {
4617 xml_allocator& alloc = get_allocator(dn);
4618 xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0;
4619
4620 node_copy_contents(dn, sn, shared_alloc);
4621
4622 xml_node_struct* dit = dn;
4623 xml_node_struct* sit = sn->first_child;
4624
4625 while (sit && sit != sn)
4626 {
4627 // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop
4628 if (sit != dn)
4629 {
4630 xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit));
4631
4632 if (copy)
4633 {
4634 node_copy_contents(copy, sit, shared_alloc);
4635
4636 if (sit->first_child)
4637 {
4638 dit = copy;
4639 sit = sit->first_child;
4640 continue;
4641 }
4642 }
4643 }
4644
4645 // continue to the next node
4646 do
4647 {
4648 if (sit->next_sibling)
4649 {
4650 sit = sit->next_sibling;
4651 break;
4652 }
4653
4654 sit = sit->parent;
4655 dit = dit->parent;
4656 }
4657 while (sit != sn);
4658 }
4659 }
4660
4661 PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa)
4662 {
4663 xml_allocator& alloc = get_allocator(da);
4664 xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0;
4665
4666 node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
4667 node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
4668 }
4669
4671 {
4672 xml_node_type type = PUGI__NODETYPE(node);
4673
4674 return type == node_pcdata || type == node_cdata;
4675 }
4676
4677 // get value with conversion functions
4678 template <typename U> PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv)
4679 {
4680 U result = 0;
4681 const char_t* s = value;
4682
4683 while (PUGI__IS_CHARTYPE(*s, ct_space))
4684 s++;
4685
4686 bool negative = (*s == '-');
4687
4688 s += (*s == '+' || *s == '-');
4689
4690 bool overflow = false;
4691
4692 if (s[0] == '0' && (s[1] | ' ') == 'x')
4693 {
4694 s += 2;
4695
4696 // since overflow detection relies on length of the sequence skip leading zeros
4697 while (*s == '0')
4698 s++;
4699
4700 const char_t* start = s;
4701
4702 for (;;)
4703 {
4704 if (static_cast<unsigned>(*s - '0') < 10)
4705 result = result * 16 + (*s - '0');
4706 else if (static_cast<unsigned>((*s | ' ') - 'a') < 6)
4707 result = result * 16 + ((*s | ' ') - 'a' + 10);
4708 else
4709 break;
4710
4711 s++;
4712 }
4713
4714 size_t digits = static_cast<size_t>(s - start);
4715
4716 overflow = digits > sizeof(U) * 2;
4717 }
4718 else
4719 {
4720 // since overflow detection relies on length of the sequence skip leading zeros
4721 while (*s == '0')
4722 s++;
4723
4724 const char_t* start = s;
4725
4726 for (;;)
4727 {
4728 if (static_cast<unsigned>(*s - '0') < 10)
4729 result = result * 10 + (*s - '0');
4730 else
4731 break;
4732
4733 s++;
4734 }
4735
4736 size_t digits = static_cast<size_t>(s - start);
4737
4738 PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2);
4739
4740 const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5;
4741 const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6';
4742 const size_t high_bit = sizeof(U) * 8 - 1;
4743
4744 overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit)));
4745 }
4746
4747 if (negative)
4748 {
4749 // Workaround for crayc++ CC-3059: Expected no overflow in routine.
4750 #ifdef _CRAYC
4751 return (overflow || result > ~minv + 1) ? minv : ~result + 1;
4752 #else
4753 return (overflow || result > 0 - minv) ? minv : 0 - result;
4754 #endif
4755 }
4756 else
4757 return (overflow || result > maxv) ? maxv : result;
4758 }
4759
4760 PUGI__FN int get_value_int(const char_t* value)
4761 {
4762 return string_to_integer<unsigned int>(value, static_cast<unsigned int>(INT_MIN), INT_MAX);
4763 }
4764
4765 PUGI__FN unsigned int get_value_uint(const char_t* value)
4766 {
4767 return string_to_integer<unsigned int>(value, 0, UINT_MAX);
4768 }
4769
4770 PUGI__FN double get_value_double(const char_t* value)
4771 {
4772 #ifdef PUGIXML_WCHAR_MODE
4773 return wcstod(value, 0);
4774 #else
4775 return strtod(value, 0);
4776 #endif
4777 }
4778
4779 PUGI__FN float get_value_float(const char_t* value)
4780 {
4781 #ifdef PUGIXML_WCHAR_MODE
4782 return static_cast<float>(wcstod(value, 0));
4783 #else
4784 return static_cast<float>(strtod(value, 0));
4785 #endif
4786 }
4787
4788 PUGI__FN bool get_value_bool(const char_t* value)
4789 {
4790 // only look at first char
4791 char_t first = *value;
4792
4793 // 1*, t* (true), T* (True), y* (yes), Y* (YES)
4794 return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y');
4795 }
4796
4797#ifdef PUGIXML_HAS_LONG_LONG
4798 PUGI__FN long long get_value_llong(const char_t* value)
4799 {
4800 return string_to_integer<unsigned long long>(value, static_cast<unsigned long long>(LLONG_MIN), LLONG_MAX);
4801 }
4802
4803 PUGI__FN unsigned long long get_value_ullong(const char_t* value)
4804 {
4805 return string_to_integer<unsigned long long>(value, 0, ULLONG_MAX);
4806 }
4807#endif
4808
4809 template <typename U> PUGI__FN PUGI__UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
4810 {
4811 char_t* result = end - 1;
4812 U rest = negative ? 0 - value : value;
4813
4814 do
4815 {
4816 *result-- = static_cast<char_t>('0' + (rest % 10));
4817 rest /= 10;
4818 }
4819 while (rest);
4820
4821 assert(result >= begin);
4822 (void)begin;
4823
4824 *result = '-';
4825
4826 return result + !negative;
4827 }
4828
4829 // set value with conversion functions
4830 template <typename String, typename Header>
4831 PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf)
4832 {
4833 #ifdef PUGIXML_WCHAR_MODE
4834 char_t wbuf[128];
4835 assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0]));
4836
4837 size_t offset = 0;
4838 for (; buf[offset]; ++offset) wbuf[offset] = buf[offset];
4839
4840 return strcpy_insitu(dest, header, header_mask, wbuf, offset);
4841 #else
4842 return strcpy_insitu(dest, header, header_mask, buf, strlen(buf));
4843 #endif
4844 }
4845
4846 template <typename U, typename String, typename Header>
4847 PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative)
4848 {
4849 char_t buf[64];
4850 char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
4851 char_t* begin = integer_to_string(buf, end, value, negative);
4852
4853 return strcpy_insitu(dest, header, header_mask, begin, end - begin);
4854 }
4855
4856 template <typename String, typename Header>
4857 PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value)
4858 {
4859 char buf[128];
4860 PUGI__SNPRINTF(buf, "%.9g", value);
4861
4862 return set_value_ascii(dest, header, header_mask, buf);
4863 }
4864
4865 template <typename String, typename Header>
4866 PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value)
4867 {
4868 char buf[128];
4869 PUGI__SNPRINTF(buf, "%.17g", value);
4870
4871 return set_value_ascii(dest, header, header_mask, buf);
4872 }
4873
4874 template <typename String, typename Header>
4875 PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value)
4876 {
4877 return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5);
4878 }
4879
4880 PUGI__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)
4881 {
4882 // check input buffer
4883 if (!contents && size) return make_parse_result(status_io_error);
4884
4885 // get actual encoding
4886 xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
4887
4888 // get private buffer
4889 char_t* buffer = 0;
4890 size_t length = 0;
4891
4892 if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);
4893
4894 // delete original buffer if we performed a conversion
4895 if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
4896
4897 // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself
4898 if (own || buffer != contents) *out_buffer = buffer;
4899
4900 // store buffer for offset_debug
4901 doc->buffer = buffer;
4902
4903 // parse
4904 xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options);
4905
4906 // remember encoding
4907 res.encoding = buffer_encoding;
4908
4909 return res;
4910 }
4911
4912 // 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
4913 PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result)
4914 {
4915 #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
4916 // there are 64-bit versions of fseek/ftell, let's use them
4917 typedef __int64 length_type;
4918
4919 _fseeki64(file, 0, SEEK_END);
4920 length_type length = _ftelli64(file);
4921 _fseeki64(file, 0, SEEK_SET);
4922 #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))
4923 // there are 64-bit versions of fseek/ftell, let's use them
4924 typedef off64_t length_type;
4925
4926 fseeko64(file, 0, SEEK_END);
4927 length_type length = ftello64(file);
4928 fseeko64(file, 0, SEEK_SET);
4929 #else
4930 // 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.
4931 typedef long length_type;
4932
4933 fseek(file, 0, SEEK_END);
4934 length_type length = ftell(file);
4935 fseek(file, 0, SEEK_SET);
4936 #endif
4937
4938 // check for I/O errors
4939 if (length < 0) return status_io_error;
4940
4941 // check for overflow
4942 size_t result = static_cast<size_t>(length);
4943
4944 if (static_cast<length_type>(result) != length) return status_out_of_memory;
4945
4946 // finalize
4947 out_result = result;
4948
4949 return status_ok;
4950 }
4951
4952 // This function assumes that buffer has extra sizeof(char_t) writable bytes after size
4953 PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding)
4954 {
4955 // We only need to zero-terminate if encoding conversion does not do it for us
4956 #ifdef PUGIXML_WCHAR_MODE
4957 xml_encoding wchar_encoding = get_wchar_encoding();
4958
4959 if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding))
4960 {
4961 size_t length = size / sizeof(char_t);
4962
4963 static_cast<char_t*>(buffer)[length] = 0;
4964 return (length + 1) * sizeof(char_t);
4965 }
4966 #else
4967 if (encoding == encoding_utf8)
4968 {
4969 static_cast<char*>(buffer)[size] = 0;
4970 return size + 1;
4971 }
4972 #endif
4973
4974 return size;
4975 }
4976
4977 PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer)
4978 {
4979 if (!file) return make_parse_result(status_file_not_found);
4980
4981 // get file size (can result in I/O errors)
4982 size_t size = 0;
4983 xml_parse_status size_status = get_file_size(file, size);
4984 if (size_status != status_ok) return make_parse_result(size_status);
4985
4986 size_t max_suffix_size = sizeof(char_t);
4987
4988 // allocate buffer for the whole file
4989 char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));
4990 if (!contents) return make_parse_result(status_out_of_memory);
4991
4992 // read file in memory
4993 size_t read_size = fread(contents, 1, size, file);
4994
4995 if (read_size != size)
4996 {
4997 xml_memory::deallocate(contents);
4998 return make_parse_result(status_io_error);
4999 }
5000
5001 xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size);
5002
5003 return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer);
5004 }
5005
5006 PUGI__FN void close_file(FILE* file)
5007 {
5008 fclose(file);
5009 }
5010
5011#ifndef PUGIXML_NO_STL
5012 template <typename T> struct xml_stream_chunk
5013 {
5015 {
5016 void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
5017 if (!memory) return 0;
5018
5019 return new (memory) xml_stream_chunk();
5020 }
5021
5022 static void destroy(xml_stream_chunk* chunk)
5023 {
5024 // free chunk chain
5025 while (chunk)
5026 {
5027 xml_stream_chunk* next_ = chunk->next;
5028
5030
5031 chunk = next_;
5032 }
5033 }
5034
5036 {
5037 }
5038
5040 size_t size;
5041
5043 };
5044
5045 template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
5046 {
5048
5049 // read file to a chunk list
5050 size_t total = 0;
5051 xml_stream_chunk<T>* last = 0;
5052
5053 while (!stream.eof())
5054 {
5055 // allocate new chunk
5057 if (!chunk) return status_out_of_memory;
5058
5059 // append chunk to list
5060 if (last) last = last->next = chunk;
5061 else chunks.data = last = chunk;
5062
5063 // read data to chunk
5064 stream.read(chunk->data, static_cast<std::streamsize>(sizeof(chunk->data) / sizeof(T)));
5065 chunk->size = static_cast<size_t>(stream.gcount()) * sizeof(T);
5066
5067 // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors
5068 if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
5069
5070 // guard against huge files (chunk size is small enough to make this overflow check work)
5071 if (total + chunk->size < total) return status_out_of_memory;
5072 total += chunk->size;
5073 }
5074
5075 size_t max_suffix_size = sizeof(char_t);
5076
5077 // copy chunk list to a contiguous buffer
5078 char* buffer = static_cast<char*>(xml_memory::allocate(total + max_suffix_size));
5079 if (!buffer) return status_out_of_memory;
5080
5081 char* write = buffer;
5082
5083 for (xml_stream_chunk<T>* chunk = chunks.data; chunk; chunk = chunk->next)
5084 {
5085 assert(write + chunk->size <= buffer + total);
5086 memcpy(write, chunk->data, chunk->size);
5087 write += chunk->size;
5088 }
5089
5090 assert(write == buffer + total);
5091
5092 // return buffer
5093 *out_buffer = buffer;
5094 *out_size = total;
5095
5096 return status_ok;
5097 }
5098
5099 template <typename T> PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
5100 {
5101 // get length of remaining data in stream
5102 typename std::basic_istream<T>::pos_type pos = stream.tellg();
5103 stream.seekg(0, std::ios::end);
5104 std::streamoff length = stream.tellg() - pos;
5105 stream.seekg(pos);
5106
5107 if (stream.fail() || pos < 0) return status_io_error;
5108
5109 // guard against huge files
5110 size_t read_length = static_cast<size_t>(length);
5111
5112 if (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory;
5113
5114 size_t max_suffix_size = sizeof(char_t);
5115
5116 // read stream data into memory (guard against stream exceptions with buffer holder)
5117 auto_deleter<void> buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);
5118 if (!buffer.data) return status_out_of_memory;
5119
5120 stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length));
5121
5122 // 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
5123 if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
5124
5125 // return buffer
5126 size_t actual_length = static_cast<size_t>(stream.gcount());
5127 assert(actual_length <= read_length);
5128
5129 *out_buffer = buffer.release();
5130 *out_size = actual_length * sizeof(T);
5131
5132 return status_ok;
5133 }
5134
5135 template <typename T> PUGI__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)
5136 {
5137 void* buffer = 0;
5138 size_t size = 0;
5139 xml_parse_status status = status_ok;
5140
5141 // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits)
5142 if (stream.fail()) return make_parse_result(status_io_error);
5143
5144 // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)
5145 if (stream.tellg() < 0)
5146 {
5147 stream.clear(); // clear error flags that could be set by a failing tellg
5148 status = load_stream_data_noseek(stream, &buffer, &size);
5149 }
5150 else
5151 status = load_stream_data_seek(stream, &buffer, &size);
5152
5153 if (status != status_ok) return make_parse_result(status);
5154
5155 xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size);
5156
5157 return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer);
5158 }
5159#endif
5160
5161#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)))
5162 PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
5163 {
5164 return _wfopen(path, mode);
5165 }
5166#else
5167 PUGI__FN char* convert_path_heap(const wchar_t* str)
5168 {
5169 assert(str);
5170
5171 // first pass: get length in utf8 characters
5172 size_t length = strlength_wide(str);
5173 size_t size = as_utf8_begin(str, length);
5174
5175 // allocate resulting string
5176 char* result = static_cast<char*>(xml_memory::allocate(size + 1));
5177 if (!result) return 0;
5178
5179 // second pass: convert to utf8
5180 as_utf8_end(result, size, str, length);
5181
5182 // zero-terminate
5183 result[size] = 0;
5184
5185 return result;
5186 }
5187
5188 PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
5189 {
5190 // there is no standard function to open wide paths, so our best bet is to try utf8 path
5191 char* path_utf8 = convert_path_heap(path);
5192 if (!path_utf8) return 0;
5193
5194 // convert mode to ASCII (we mirror _wfopen interface)
5195 char mode_ascii[4] = {0};
5196 for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]);
5197
5198 // try to open the utf8 path
5199 FILE* result = fopen(path_utf8, mode_ascii);
5200
5201 // free dummy buffer
5202 xml_memory::deallocate(path_utf8);
5203
5204 return result;
5205 }
5206#endif
5207
5208 PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
5209 {
5210 if (!file) return false;
5211
5212 xml_writer_file writer(file);
5213 doc.save(writer, indent, flags, encoding);
5214
5215 return ferror(file) == 0;
5216 }
5217
5219 {
5221 char_t* name;
5222
5224 {
5225 node->name = 0;
5226 }
5227
5229 {
5230 node->name = name;
5231 }
5232 };
5234
5235namespace pugi
5236{
5238 {
5239 }
5240
5241 PUGI__FN void xml_writer_file::write(const void* data, size_t size)
5242 {
5243 size_t result = fwrite(data, 1, size, static_cast<FILE*>(file));
5244 (void)!result; // unfortunately we can't do proper error handling here
5245 }
5246
5247#ifndef PUGIXML_NO_STL
5248 PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)
5249 {
5250 }
5251
5252 PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream): narrow_stream(0), wide_stream(&stream)
5253 {
5254 }
5255
5256 PUGI__FN void xml_writer_stream::write(const void* data, size_t size)
5257 {
5258 if (narrow_stream)
5259 {
5260 assert(!wide_stream);
5261 narrow_stream->write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size));
5262 }
5263 else
5264 {
5265 assert(wide_stream);
5266 assert(size % sizeof(wchar_t) == 0);
5267
5268 wide_stream->write(reinterpret_cast<const wchar_t*>(data), static_cast<std::streamsize>(size / sizeof(wchar_t)));
5269 }
5270 }
5271#endif
5272
5274 {
5275 }
5276
5280
5282 {
5283 return _depth;
5284 }
5285
5287 {
5288 return true;
5289 }
5290
5292 {
5293 return true;
5294 }
5295
5297 {
5298 }
5299
5303
5307
5308 PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const
5309 {
5311 }
5312
5314 {
5315 return !_attr;
5316 }
5317
5319 {
5320 return (_attr == r._attr);
5321 }
5322
5324 {
5325 return (_attr != r._attr);
5326 }
5327
5329 {
5330 return (_attr < r._attr);
5331 }
5332
5334 {
5335 return (_attr > r._attr);
5336 }
5337
5339 {
5340 return (_attr <= r._attr);
5341 }
5342
5344 {
5345 return (_attr >= r._attr);
5346 }
5347
5352
5357
5359 {
5360 return (_attr && _attr->value) ? _attr->value + 0 : def;
5361 }
5362
5364 {
5365 return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def;
5366 }
5367
5368 PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
5369 {
5370 return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def;
5371 }
5372
5373 PUGI__FN double xml_attribute::as_double(double def) const
5374 {
5375 return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def;
5376 }
5377
5378 PUGI__FN float xml_attribute::as_float(float def) const
5379 {
5380 return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def;
5381 }
5382
5384 {
5385 return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def;
5386 }
5387
5388#ifdef PUGIXML_HAS_LONG_LONG
5389 PUGI__FN long long xml_attribute::as_llong(long long def) const
5390 {
5391 return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def;
5392 }
5393
5394 PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
5395 {
5396 return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def;
5397 }
5398#endif
5399
5401 {
5402 return !_attr;
5403 }
5404
5406 {
5407 return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT("");
5408 }
5409
5411 {
5412 return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT("");
5413 }
5414
5416 {
5417 return static_cast<size_t>(reinterpret_cast<uintptr_t>(_attr) / sizeof(xml_attribute_struct));
5418 }
5419
5424
5426 {
5427 set_value(rhs);
5428 return *this;
5429 }
5430
5432 {
5433 set_value(rhs);
5434 return *this;
5435 }
5436
5438 {
5439 set_value(rhs);
5440 return *this;
5441 }
5442
5444 {
5445 set_value(rhs);
5446 return *this;
5447 }
5448
5450 {
5451 set_value(rhs);
5452 return *this;
5453 }
5454
5456 {
5457 set_value(rhs);
5458 return *this;
5459 }
5460
5462 {
5463 set_value(rhs);
5464 return *this;
5465 }
5466
5468 {
5469 set_value(rhs);
5470 return *this;
5471 }
5472
5473#ifdef PUGIXML_HAS_LONG_LONG
5475 {
5476 set_value(rhs);
5477 return *this;
5478 }
5479
5480 PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs)
5481 {
5482 set_value(rhs);
5483 return *this;
5484 }
5485#endif
5486
5488 {
5489 if (!_attr) return false;
5490
5491 return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
5492 }
5493
5495 {
5496 if (!_attr) return false;
5497
5498 return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
5499 }
5500
5502 {
5503 if (!_attr) return false;
5504
5505 return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
5506 }
5507
5508 PUGI__FN bool xml_attribute::set_value(unsigned int rhs)
5509 {
5510 if (!_attr) return false;
5511
5512 return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
5513 }
5514
5516 {
5517 if (!_attr) return false;
5518
5519 return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
5520 }
5521
5522 PUGI__FN bool xml_attribute::set_value(unsigned long rhs)
5523 {
5524 if (!_attr) return false;
5525
5526 return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
5527 }
5528
5530 {
5531 if (!_attr) return false;
5532
5533 return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
5534 }
5535
5537 {
5538 if (!_attr) return false;
5539
5540 return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
5541 }
5542
5544 {
5545 if (!_attr) return false;
5546
5547 return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
5548 }
5549
5550#ifdef PUGIXML_HAS_LONG_LONG
5551 PUGI__FN bool xml_attribute::set_value(long long rhs)
5552 {
5553 if (!_attr) return false;
5554
5555 return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
5556 }
5557
5558 PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
5559 {
5560 if (!_attr) return false;
5561
5562 return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
5563 }
5564#endif
5565
5566#ifdef __BORLANDC__
5567 PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs)
5568 {
5569 return (bool)lhs && rhs;
5570 }
5571
5572 PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs)
5573 {
5574 return (bool)lhs || rhs;
5575 }
5576#endif
5577
5579 {
5580 }
5581
5583 {
5584 }
5585
5587 {
5588 }
5589
5591 {
5592 return _root ? unspecified_bool_xml_node : 0;
5593 }
5594
5596 {
5597 return !_root;
5598 }
5599
5601 {
5602 return iterator(_root ? _root->first_child + 0 : 0, _root);
5603 }
5604
5606 {
5607 return iterator(0, _root);
5608 }
5609
5614
5619
5624
5629
5634
5636 {
5637 return (_root == r._root);
5638 }
5639
5641 {
5642 return (_root != r._root);
5643 }
5644
5646 {
5647 return (_root < r._root);
5648 }
5649
5651 {
5652 return (_root > r._root);
5653 }
5654
5656 {
5657 return (_root <= r._root);
5658 }
5659
5661 {
5662 return (_root >= r._root);
5663 }
5664
5666 {
5667 return !_root;
5668 }
5669
5671 {
5672 return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT("");
5673 }
5674
5679
5681 {
5682 return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT("");
5683 }
5684
5686 {
5687 if (!_root) return xml_node();
5688
5689 for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
5690 if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
5691
5692 return xml_node();
5693 }
5694
5696 {
5697 if (!_root) return xml_attribute();
5698
5699 for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
5700 if (i->name && impl::strequal(name_, i->name))
5701 return xml_attribute(i);
5702
5703 return xml_attribute();
5704 }
5705
5707 {
5708 if (!_root) return xml_node();
5709
5710 for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
5711 if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
5712
5713 return xml_node();
5714 }
5715
5720
5722 {
5723 if (!_root) return xml_node();
5724
5725 for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)
5726 if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
5727
5728 return xml_node();
5729 }
5730
5732 {
5733 xml_attribute_struct* hint = hint_._attr;
5734
5735 // if hint is not an attribute of node, behavior is not defined
5736 assert(!hint || (_root && impl::is_attribute_of(hint, _root)));
5737
5738 if (!_root) return xml_attribute();
5739
5740 // optimistically search from hint up until the end
5741 for (xml_attribute_struct* i = hint; i; i = i->next_attribute)
5742 if (i->name && impl::strequal(name_, i->name))
5743 {
5744 // update hint to maximize efficiency of searching for consecutive attributes
5745 hint_._attr = i->next_attribute;
5746
5747 return xml_attribute(i);
5748 }
5749
5750 // wrap around and search from the first attribute until the hint
5751 // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails
5752 for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)
5753 if (j->name && impl::strequal(name_, j->name))
5754 {
5755 // update hint to maximize efficiency of searching for consecutive attributes
5756 hint_._attr = j->next_attribute;
5757
5758 return xml_attribute(j);
5759 }
5760
5761 return xml_attribute();
5762 }
5763
5765 {
5766 if (!_root) return xml_node();
5767
5769 else return xml_node();
5770 }
5771
5773 {
5774 return _root ? xml_node(_root->parent) : xml_node();
5775 }
5776
5778 {
5779 return _root ? xml_node(&impl::get_document(_root)) : xml_node();
5780 }
5781
5783 {
5784 return xml_text(_root);
5785 }
5786
5788 {
5789 if (!_root) return PUGIXML_TEXT("");
5790
5791 // element nodes can have value if parse_embed_pcdata was used
5793 return _root->value;
5794
5795 for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
5796 if (impl::is_text_node(i) && i->value)
5797 return i->value;
5798
5799 return PUGIXML_TEXT("");
5800 }
5801
5802 PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const
5803 {
5804 return child(name_).child_value();
5805 }
5806
5811
5816
5821
5826
5828 {
5830
5831 if (type_ != node_element && type_ != node_pi && type_ != node_declaration)
5832 return false;
5833
5834 return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
5835 }
5836
5838 {
5840
5841 if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)
5842 return false;
5843
5844 return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
5845 }
5846
5848 {
5849 if (!impl::allow_insert_attribute(type())) return xml_attribute();
5850
5851 impl::xml_allocator& alloc = impl::get_allocator(_root);
5852 if (!alloc.reserve()) return xml_attribute();
5853
5854 xml_attribute a(impl::allocate_attribute(alloc));
5855 if (!a) return xml_attribute();
5856
5857 impl::append_attribute(a._attr, _root);
5858
5859 a.set_name(name_);
5860
5861 return a;
5862 }
5863
5865 {
5866 if (!impl::allow_insert_attribute(type())) return xml_attribute();
5867
5868 impl::xml_allocator& alloc = impl::get_allocator(_root);
5869 if (!alloc.reserve()) return xml_attribute();
5870
5871 xml_attribute a(impl::allocate_attribute(alloc));
5872 if (!a) return xml_attribute();
5873
5874 impl::prepend_attribute(a._attr, _root);
5875
5876 a.set_name(name_);
5877
5878 return a;
5879 }
5880
5882 {
5883 if (!impl::allow_insert_attribute(type())) return xml_attribute();
5884 if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
5885
5886 impl::xml_allocator& alloc = impl::get_allocator(_root);
5887 if (!alloc.reserve()) return xml_attribute();
5888
5889 xml_attribute a(impl::allocate_attribute(alloc));
5890 if (!a) return xml_attribute();
5891
5892 impl::insert_attribute_after(a._attr, attr._attr, _root);
5893
5894 a.set_name(name_);
5895
5896 return a;
5897 }
5898
5900 {
5901 if (!impl::allow_insert_attribute(type())) return xml_attribute();
5902 if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
5903
5904 impl::xml_allocator& alloc = impl::get_allocator(_root);
5905 if (!alloc.reserve()) return xml_attribute();
5906
5907 xml_attribute a(impl::allocate_attribute(alloc));
5908 if (!a) return xml_attribute();
5909
5910 impl::insert_attribute_before(a._attr, attr._attr, _root);
5911
5912 a.set_name(name_);
5913
5914 return a;
5915 }
5916
5918 {
5919 if (!proto) return xml_attribute();
5920 if (!impl::allow_insert_attribute(type())) return xml_attribute();
5921
5922 impl::xml_allocator& alloc = impl::get_allocator(_root);
5923 if (!alloc.reserve()) return xml_attribute();
5924
5925 xml_attribute a(impl::allocate_attribute(alloc));
5926 if (!a) return xml_attribute();
5927
5928 impl::append_attribute(a._attr, _root);
5929 impl::node_copy_attribute(a._attr, proto._attr);
5930
5931 return a;
5932 }
5933
5935 {
5936 if (!proto) return xml_attribute();
5937 if (!impl::allow_insert_attribute(type())) return xml_attribute();
5938
5939 impl::xml_allocator& alloc = impl::get_allocator(_root);
5940 if (!alloc.reserve()) return xml_attribute();
5941
5942 xml_attribute a(impl::allocate_attribute(alloc));
5943 if (!a) return xml_attribute();
5944
5945 impl::prepend_attribute(a._attr, _root);
5946 impl::node_copy_attribute(a._attr, proto._attr);
5947
5948 return a;
5949 }
5950
5952 {
5953 if (!proto) return xml_attribute();
5954 if (!impl::allow_insert_attribute(type())) return xml_attribute();
5955 if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
5956
5957 impl::xml_allocator& alloc = impl::get_allocator(_root);
5958 if (!alloc.reserve()) return xml_attribute();
5959
5960 xml_attribute a(impl::allocate_attribute(alloc));
5961 if (!a) return xml_attribute();
5962
5963 impl::insert_attribute_after(a._attr, attr._attr, _root);
5964 impl::node_copy_attribute(a._attr, proto._attr);
5965
5966 return a;
5967 }
5968
5970 {
5971 if (!proto) return xml_attribute();
5972 if (!impl::allow_insert_attribute(type())) return xml_attribute();
5973 if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
5974
5975 impl::xml_allocator& alloc = impl::get_allocator(_root);
5976 if (!alloc.reserve()) return xml_attribute();
5977
5978 xml_attribute a(impl::allocate_attribute(alloc));
5979 if (!a) return xml_attribute();
5980
5981 impl::insert_attribute_before(a._attr, attr._attr, _root);
5982 impl::node_copy_attribute(a._attr, proto._attr);
5983
5984 return a;
5985 }
5986
5988 {
5989 if (!impl::allow_insert_child(type(), type_)) return xml_node();
5990
5991 impl::xml_allocator& alloc = impl::get_allocator(_root);
5992 if (!alloc.reserve()) return xml_node();
5993
5994 xml_node n(impl::allocate_node(alloc, type_));
5995 if (!n) return xml_node();
5996
5997 impl::append_node(n._root, _root);
5998
5999 if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
6000
6001 return n;
6002 }
6003
6005 {
6006 if (!impl::allow_insert_child(type(), type_)) return xml_node();
6007
6008 impl::xml_allocator& alloc = impl::get_allocator(_root);
6009 if (!alloc.reserve()) return xml_node();
6010
6011 xml_node n(impl::allocate_node(alloc, type_));
6012 if (!n) return xml_node();
6013
6014 impl::prepend_node(n._root, _root);
6015
6016 if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
6017
6018 return n;
6019 }
6020
6022 {
6023 if (!impl::allow_insert_child(type(), type_)) return xml_node();
6024 if (!node._root || node._root->parent != _root) return xml_node();
6025
6026 impl::xml_allocator& alloc = impl::get_allocator(_root);
6027 if (!alloc.reserve()) return xml_node();
6028
6029 xml_node n(impl::allocate_node(alloc, type_));
6030 if (!n) return xml_node();
6031
6032 impl::insert_node_before(n._root, node._root);
6033
6034 if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
6035
6036 return n;
6037 }
6038
6040 {
6041 if (!impl::allow_insert_child(type(), type_)) return xml_node();
6042 if (!node._root || node._root->parent != _root) return xml_node();
6043
6044 impl::xml_allocator& alloc = impl::get_allocator(_root);
6045 if (!alloc.reserve()) return xml_node();
6046
6047 xml_node n(impl::allocate_node(alloc, type_));
6048 if (!n) return xml_node();
6049
6050 impl::insert_node_after(n._root, node._root);
6051
6052 if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
6053
6054 return n;
6055 }
6056
6058 {
6060
6061 result.set_name(name_);
6062
6063 return result;
6064 }
6065
6067 {
6069
6070 result.set_name(name_);
6071
6072 return result;
6073 }
6074
6076 {
6078
6079 result.set_name(name_);
6080
6081 return result;
6082 }
6083
6085 {
6087
6088 result.set_name(name_);
6089
6090 return result;
6091 }
6092
6094 {
6095 xml_node_type type_ = proto.type();
6096 if (!impl::allow_insert_child(type(), type_)) return xml_node();
6097
6098 impl::xml_allocator& alloc = impl::get_allocator(_root);
6099 if (!alloc.reserve()) return xml_node();
6100
6101 xml_node n(impl::allocate_node(alloc, type_));
6102 if (!n) return xml_node();
6103
6104 impl::append_node(n._root, _root);
6105 impl::node_copy_tree(n._root, proto._root);
6106
6107 return n;
6108 }
6109
6111 {
6112 xml_node_type type_ = proto.type();
6113 if (!impl::allow_insert_child(type(), type_)) return xml_node();
6114
6115 impl::xml_allocator& alloc = impl::get_allocator(_root);
6116 if (!alloc.reserve()) return xml_node();
6117
6118 xml_node n(impl::allocate_node(alloc, type_));
6119 if (!n) return xml_node();
6120
6121 impl::prepend_node(n._root, _root);
6122 impl::node_copy_tree(n._root, proto._root);
6123
6124 return n;
6125 }
6126
6128 {
6129 xml_node_type type_ = proto.type();
6130 if (!impl::allow_insert_child(type(), type_)) return xml_node();
6131 if (!node._root || node._root->parent != _root) return xml_node();
6132
6133 impl::xml_allocator& alloc = impl::get_allocator(_root);
6134 if (!alloc.reserve()) return xml_node();
6135
6136 xml_node n(impl::allocate_node(alloc, type_));
6137 if (!n) return xml_node();
6138
6139 impl::insert_node_after(n._root, node._root);
6140 impl::node_copy_tree(n._root, proto._root);
6141
6142 return n;
6143 }
6144
6146 {
6147 xml_node_type type_ = proto.type();
6148 if (!impl::allow_insert_child(type(), type_)) return xml_node();
6149 if (!node._root || node._root->parent != _root) return xml_node();
6150
6151 impl::xml_allocator& alloc = impl::get_allocator(_root);
6152 if (!alloc.reserve()) return xml_node();
6153
6154 xml_node n(impl::allocate_node(alloc, type_));
6155 if (!n) return xml_node();
6156
6157 impl::insert_node_before(n._root, node._root);
6158 impl::node_copy_tree(n._root, proto._root);
6159
6160 return n;
6161 }
6162
6164 {
6165 if (!impl::allow_move(*this, moved)) return xml_node();
6166
6167 impl::xml_allocator& alloc = impl::get_allocator(_root);
6168 if (!alloc.reserve()) return xml_node();
6169
6170 // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
6171 impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
6172
6173 impl::remove_node(moved._root);
6174 impl::append_node(moved._root, _root);
6175
6176 return moved;
6177 }
6178
6180 {
6181 if (!impl::allow_move(*this, moved)) return xml_node();
6182
6183 impl::xml_allocator& alloc = impl::get_allocator(_root);
6184 if (!alloc.reserve()) return xml_node();
6185
6186 // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
6187 impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
6188
6189 impl::remove_node(moved._root);
6190 impl::prepend_node(moved._root, _root);
6191
6192 return moved;
6193 }
6194
6196 {
6197 if (!impl::allow_move(*this, moved)) return xml_node();
6198 if (!node._root || node._root->parent != _root) return xml_node();
6199 if (moved._root == node._root) return xml_node();
6200
6201 impl::xml_allocator& alloc = impl::get_allocator(_root);
6202 if (!alloc.reserve()) return xml_node();
6203
6204 // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
6205 impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
6206
6207 impl::remove_node(moved._root);
6208 impl::insert_node_after(moved._root, node._root);
6209
6210 return moved;
6211 }
6212
6214 {
6215 if (!impl::allow_move(*this, moved)) return xml_node();
6216 if (!node._root || node._root->parent != _root) return xml_node();
6217 if (moved._root == node._root) return xml_node();
6218
6219 impl::xml_allocator& alloc = impl::get_allocator(_root);
6220 if (!alloc.reserve()) return xml_node();
6221
6222 // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
6223 impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
6224
6225 impl::remove_node(moved._root);
6226 impl::insert_node_before(moved._root, node._root);
6227
6228 return moved;
6229 }
6230
6232 {
6233 return remove_attribute(attribute(name_));
6234 }
6235
6237 {
6238 if (!_root || !a._attr) return false;
6239 if (!impl::is_attribute_of(a._attr, _root)) return false;
6240
6241 impl::xml_allocator& alloc = impl::get_allocator(_root);
6242 if (!alloc.reserve()) return false;
6243
6244 impl::remove_attribute(a._attr, _root);
6245 impl::destroy_attribute(a._attr, alloc);
6246
6247 return true;
6248 }
6249
6251 {
6252 return remove_child(child(name_));
6253 }
6254
6256 {
6257 if (!_root || !n._root || n._root->parent != _root) return false;
6258
6259 impl::xml_allocator& alloc = impl::get_allocator(_root);
6260 if (!alloc.reserve()) return false;
6261
6262 impl::remove_node(n._root);
6263 impl::destroy_node(n._root, alloc);
6264
6265 return true;
6266 }
6267
6268 PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
6269 {
6270 // append_buffer is only valid for elements/documents
6271 if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root);
6272
6273 // get document node
6274 impl::xml_document_struct* doc = &impl::get_document(_root);
6275
6276 // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense
6277 doc->header |= impl::xml_memory_page_contents_shared_mask;
6278
6279 // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later)
6280 impl::xml_memory_page* page = 0;
6281 impl::xml_extra_buffer* extra = static_cast<impl::xml_extra_buffer*>(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page));
6282 (void)page;
6283
6284 if (!extra) return impl::make_parse_result(status_out_of_memory);
6285
6286 #ifdef PUGIXML_COMPACT
6287 // align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned
6288 // note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account
6289 extra = reinterpret_cast<impl::xml_extra_buffer*>((reinterpret_cast<uintptr_t>(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1));
6290 #endif
6291
6292 // add extra buffer to the list
6293 extra->buffer = 0;
6294 extra->next = doc->extra_buffers;
6295 doc->extra_buffers = extra;
6296
6297 // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level
6298 impl::name_null_sentry sentry(_root);
6299
6300 return impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &extra->buffer);
6301 }
6302
6303 PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
6304 {
6305 if (!_root) return xml_node();
6306
6307 for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
6308 if (i->name && impl::strequal(name_, i->name))
6309 {
6310 for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
6311 if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
6312 return xml_node(i);
6313 }
6314
6315 return xml_node();
6316 }
6317
6318 PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const
6319 {
6320 if (!_root) return xml_node();
6321
6322 for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
6323 for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
6324 if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
6325 return xml_node(i);
6326
6327 return xml_node();
6328 }
6329
6330#ifndef PUGIXML_NO_STL
6332 {
6333 if (!_root) return string_t();
6334
6335 size_t offset = 0;
6336
6337 for (xml_node_struct* i = _root; i; i = i->parent)
6338 {
6339 offset += (i != _root);
6340 offset += i->name ? impl::strlength(i->name) : 0;
6341 }
6342
6343 string_t result;
6344 result.resize(offset);
6345
6346 for (xml_node_struct* j = _root; j; j = j->parent)
6347 {
6348 if (j != _root)
6349 result[--offset] = delimiter;
6350
6351 if (j->name)
6352 {
6353 size_t length = impl::strlength(j->name);
6354
6355 offset -= length;
6356 memcpy(&result[offset], j->name, length * sizeof(char_t));
6357 }
6358 }
6359
6360 assert(offset == 0);
6361
6362 return result;
6363 }
6364#endif
6365
6367 {
6368 xml_node found = *this; // Current search context.
6369
6370 if (!_root || !path_[0]) return found;
6371
6372 if (path_[0] == delimiter)
6373 {
6374 // Absolute path; e.g. '/foo/bar'
6375 found = found.root();
6376 ++path_;
6377 }
6378
6379 const char_t* path_segment = path_;
6380
6381 while (*path_segment == delimiter) ++path_segment;
6382
6383 const char_t* path_segment_end = path_segment;
6384
6385 while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end;
6386
6387 if (path_segment == path_segment_end) return found;
6388
6389 const char_t* next_segment = path_segment_end;
6390
6391 while (*next_segment == delimiter) ++next_segment;
6392
6393 if (*path_segment == '.' && path_segment + 1 == path_segment_end)
6394 return found.first_element_by_path(next_segment, delimiter);
6395 else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end)
6396 return found.parent().first_element_by_path(next_segment, delimiter);
6397 else
6398 {
6399 for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling)
6400 {
6401 if (j->name && impl::strequalrange(j->name, path_segment, static_cast<size_t>(path_segment_end - path_segment)))
6402 {
6403 xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);
6404
6405 if (subsearch) return subsearch;
6406 }
6407 }
6408
6409 return xml_node();
6410 }
6411 }
6412
6414 {
6415 walker._depth = -1;
6416
6417 xml_node arg_begin(_root);
6418 if (!walker.begin(arg_begin)) return false;
6419
6420 xml_node_struct* cur = _root ? _root->first_child + 0 : 0;
6421
6422 if (cur)
6423 {
6424 ++walker._depth;
6425
6426 do
6427 {
6428 xml_node arg_for_each(cur);
6429 if (!walker.for_each(arg_for_each))
6430 return false;
6431
6432 if (cur->first_child)
6433 {
6434 ++walker._depth;
6435 cur = cur->first_child;
6436 }
6437 else if (cur->next_sibling)
6438 cur = cur->next_sibling;
6439 else
6440 {
6441 while (!cur->next_sibling && cur != _root && cur->parent)
6442 {
6443 --walker._depth;
6444 cur = cur->parent;
6445 }
6446
6447 if (cur != _root)
6448 cur = cur->next_sibling;
6449 }
6450 }
6451 while (cur && cur != _root);
6452 }
6453
6454 assert(walker._depth == -1);
6455
6456 xml_node arg_end(_root);
6457 return walker.end(arg_end);
6458 }
6459
6461 {
6462 return static_cast<size_t>(reinterpret_cast<uintptr_t>(_root) / sizeof(xml_node_struct));
6463 }
6464
6466 {
6467 return _root;
6468 }
6469
6470 PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
6471 {
6472 if (!_root) return;
6473
6474 impl::xml_buffered_writer buffered_writer(writer, encoding);
6475
6476 impl::node_output(buffered_writer, _root, indent, flags, depth);
6477
6478 buffered_writer.flush();
6479 }
6480
6481#ifndef PUGIXML_NO_STL
6482 PUGI__FN void xml_node::print(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
6483 {
6484 xml_writer_stream writer(stream);
6485
6486 print(writer, indent, flags, encoding, depth);
6487 }
6488
6489 PUGI__FN void xml_node::print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const
6490 {
6491 xml_writer_stream writer(stream);
6492
6493 print(writer, indent, flags, encoding_wchar, depth);
6494 }
6495#endif
6496
6498 {
6499 if (!_root) return -1;
6500
6501 impl::xml_document_struct& doc = impl::get_document(_root);
6502
6503 // we can determine the offset reliably only if there is exactly once parse buffer
6504 if (!doc.buffer || doc.extra_buffers) return -1;
6505
6506 switch (type())
6507 {
6508 case node_document:
6509 return 0;
6510
6511 case node_element:
6512 case node_declaration:
6513 case node_pi:
6514 return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1;
6515
6516 case node_pcdata:
6517 case node_cdata:
6518 case node_comment:
6519 case node_doctype:
6520 return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1;
6521
6522 default:
6523 assert(false && "Invalid node type"); // unreachable
6524 return -1;
6525 }
6526 }
6527
6528#ifdef __BORLANDC__
6529 PUGI__FN bool operator&&(const xml_node& lhs, bool rhs)
6530 {
6531 return (bool)lhs && rhs;
6532 }
6533
6534 PUGI__FN bool operator||(const xml_node& lhs, bool rhs)
6535 {
6536 return (bool)lhs || rhs;
6537 }
6538#endif
6539
6541 {
6542 }
6543
6545 {
6546 if (!_root || impl::is_text_node(_root)) return _root;
6547
6548 // element nodes can have value if parse_embed_pcdata was used
6550 return _root;
6551
6552 for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling)
6553 if (impl::is_text_node(node))
6554 return node;
6555
6556 return 0;
6557 }
6558
6560 {
6561 xml_node_struct* d = _data();
6562 if (d) return d;
6563
6564 return xml_node(_root).append_child(node_pcdata).internal_object();
6565 }
6566
6568 {
6569 }
6570
6572 {
6573 }
6574
6576 {
6577 return _data() ? unspecified_bool_xml_text : 0;
6578 }
6579
6581 {
6582 return !_data();
6583 }
6584
6586 {
6587 return _data() == 0;
6588 }
6589
6591 {
6592 xml_node_struct* d = _data();
6593
6594 return (d && d->value) ? d->value + 0 : PUGIXML_TEXT("");
6595 }
6596
6598 {
6599 xml_node_struct* d = _data();
6600
6601 return (d && d->value) ? d->value + 0 : def;
6602 }
6603
6604 PUGI__FN int xml_text::as_int(int def) const
6605 {
6606 xml_node_struct* d = _data();
6607
6608 return (d && d->value) ? impl::get_value_int(d->value) : def;
6609 }
6610
6611 PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const
6612 {
6613 xml_node_struct* d = _data();
6614
6615 return (d && d->value) ? impl::get_value_uint(d->value) : def;
6616 }
6617
6618 PUGI__FN double xml_text::as_double(double def) const
6619 {
6620 xml_node_struct* d = _data();
6621
6622 return (d && d->value) ? impl::get_value_double(d->value) : def;
6623 }
6624
6625 PUGI__FN float xml_text::as_float(float def) const
6626 {
6627 xml_node_struct* d = _data();
6628
6629 return (d && d->value) ? impl::get_value_float(d->value) : def;
6630 }
6631
6632 PUGI__FN bool xml_text::as_bool(bool def) const
6633 {
6634 xml_node_struct* d = _data();
6635
6636 return (d && d->value) ? impl::get_value_bool(d->value) : def;
6637 }
6638
6639#ifdef PUGIXML_HAS_LONG_LONG
6640 PUGI__FN long long xml_text::as_llong(long long def) const
6641 {
6642 xml_node_struct* d = _data();
6643
6644 return (d && d->value) ? impl::get_value_llong(d->value) : def;
6645 }
6646
6647 PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const
6648 {
6649 xml_node_struct* d = _data();
6650
6651 return (d && d->value) ? impl::get_value_ullong(d->value) : def;
6652 }
6653#endif
6654
6656 {
6657 xml_node_struct* dn = _data_new();
6658
6659 return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false;
6660 }
6661
6663 {
6664 xml_node_struct* dn = _data_new();
6665
6666 return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
6667 }
6668
6669 PUGI__FN bool xml_text::set(unsigned int rhs)
6670 {
6671 xml_node_struct* dn = _data_new();
6672
6673 return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
6674 }
6675
6677 {
6678 xml_node_struct* dn = _data_new();
6679
6680 return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
6681 }
6682
6683 PUGI__FN bool xml_text::set(unsigned long rhs)
6684 {
6685 xml_node_struct* dn = _data_new();
6686
6687 return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
6688 }
6689
6690 PUGI__FN bool xml_text::set(float rhs)
6691 {
6692 xml_node_struct* dn = _data_new();
6693
6694 return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
6695 }
6696
6697 PUGI__FN bool xml_text::set(double rhs)
6698 {
6699 xml_node_struct* dn = _data_new();
6700
6701 return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
6702 }
6703
6705 {
6706 xml_node_struct* dn = _data_new();
6707
6708 return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
6709 }
6710
6711#ifdef PUGIXML_HAS_LONG_LONG
6712 PUGI__FN bool xml_text::set(long long rhs)
6713 {
6714 xml_node_struct* dn = _data_new();
6715
6716 return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
6717 }
6718
6719 PUGI__FN bool xml_text::set(unsigned long long rhs)
6720 {
6721 xml_node_struct* dn = _data_new();
6722
6723 return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
6724 }
6725#endif
6726
6728 {
6729 set(rhs);
6730 return *this;
6731 }
6732
6734 {
6735 set(rhs);
6736 return *this;
6737 }
6738
6740 {
6741 set(rhs);
6742 return *this;
6743 }
6744
6746 {
6747 set(rhs);
6748 return *this;
6749 }
6750
6752 {
6753 set(rhs);
6754 return *this;
6755 }
6756
6758 {
6759 set(rhs);
6760 return *this;
6761 }
6762
6764 {
6765 set(rhs);
6766 return *this;
6767 }
6768
6770 {
6771 set(rhs);
6772 return *this;
6773 }
6774
6775#ifdef PUGIXML_HAS_LONG_LONG
6776 PUGI__FN xml_text& xml_text::operator=(long long rhs)
6777 {
6778 set(rhs);
6779 return *this;
6780 }
6781
6782 PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs)
6783 {
6784 set(rhs);
6785 return *this;
6786 }
6787#endif
6788
6790 {
6791 return xml_node(_data());
6792 }
6793
6794#ifdef __BORLANDC__
6795 PUGI__FN bool operator&&(const xml_text& lhs, bool rhs)
6796 {
6797 return (bool)lhs && rhs;
6798 }
6799
6800 PUGI__FN bool operator||(const xml_text& lhs, bool rhs)
6801 {
6802 return (bool)lhs || rhs;
6803 }
6804#endif
6805
6809
6810 PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent())
6811 {
6812 }
6813
6815 {
6816 }
6817
6819 {
6820 return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
6821 }
6822
6824 {
6825 return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
6826 }
6827
6829 {
6830 assert(_wrap._root);
6831 return _wrap;
6832 }
6833
6835 {
6836 assert(_wrap._root);
6837 return const_cast<xml_node*>(&_wrap); // BCC5 workaround
6838 }
6839
6841 {
6842 assert(_wrap._root);
6844 return *this;
6845 }
6846
6848 {
6849 xml_node_iterator temp = *this;
6850 ++*this;
6851 return temp;
6852 }
6853
6859
6861 {
6862 xml_node_iterator temp = *this;
6863 --*this;
6864 return temp;
6865 }
6866
6870
6871 PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent)
6872 {
6873 }
6874
6876 {
6877 }
6878
6880 {
6881 return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root;
6882 }
6883
6885 {
6886 return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root;
6887 }
6888
6890 {
6891 assert(_wrap._attr);
6892 return _wrap;
6893 }
6894
6896 {
6897 assert(_wrap._attr);
6898 return const_cast<xml_attribute*>(&_wrap); // BCC5 workaround
6899 }
6900
6902 {
6903 assert(_wrap._attr);
6905 return *this;
6906 }
6907
6909 {
6910 xml_attribute_iterator temp = *this;
6911 ++*this;
6912 return temp;
6913 }
6914
6920
6922 {
6923 xml_attribute_iterator temp = *this;
6924 --*this;
6925 return temp;
6926 }
6927
6931
6932 PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name)
6933 {
6934 }
6935
6936 PUGI__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)
6937 {
6938 }
6939
6941 {
6942 return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
6943 }
6944
6946 {
6947 return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
6948 }
6949
6951 {
6952 assert(_wrap._root);
6953 return _wrap;
6954 }
6955
6957 {
6958 assert(_wrap._root);
6959 return const_cast<xml_node*>(&_wrap); // BCC5 workaround
6960 }
6961
6963 {
6964 assert(_wrap._root);
6966 return *this;
6967 }
6968
6970 {
6971 xml_named_node_iterator temp = *this;
6972 ++*this;
6973 return temp;
6974 }
6975
6977 {
6978 if (_wrap._root)
6980 else
6981 {
6983
6984 if (!impl::strequal(_wrap.name(), _name))
6986 }
6987
6988 return *this;
6989 }
6990
6992 {
6993 xml_named_node_iterator temp = *this;
6994 --*this;
6995 return temp;
6996 }
6997
7001
7002 PUGI__FN xml_parse_result::operator bool() const
7003 {
7004 return status == status_ok;
7005 }
7006
7008 {
7009 switch (status)
7010 {
7011 case status_ok: return "No error";
7012
7013 case status_file_not_found: return "File was not found";
7014 case status_io_error: return "Error reading from file/stream";
7015 case status_out_of_memory: return "Could not allocate memory";
7016 case status_internal_error: return "Internal error occurred";
7017
7018 case status_unrecognized_tag: return "Could not determine tag type";
7019
7020 case status_bad_pi: return "Error parsing document declaration/processing instruction";
7021 case status_bad_comment: return "Error parsing comment";
7022 case status_bad_cdata: return "Error parsing CDATA section";
7023 case status_bad_doctype: return "Error parsing document type declaration";
7024 case status_bad_pcdata: return "Error parsing PCDATA section";
7025 case status_bad_start_element: return "Error parsing start element tag";
7026 case status_bad_attribute: return "Error parsing element attribute";
7027 case status_bad_end_element: return "Error parsing end element tag";
7028 case status_end_element_mismatch: return "Start-end tags mismatch";
7029
7030 case status_append_invalid_root: return "Unable to append nodes: root is not an element or document";
7031
7032 case status_no_document_element: return "No document element found";
7033
7034 default: return "Unknown error";
7035 }
7036 }
7037
7039 {
7040 _create();
7041 }
7042
7047
7048#ifdef PUGIXML_HAS_MOVE
7050 {
7051 _create();
7052 _move(rhs);
7053 }
7054
7055 PUGI__FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
7056 {
7057 if (this == &rhs) return *this;
7058
7059 _destroy();
7060 _create();
7061 _move(rhs);
7062
7063 return *this;
7064 }
7065#endif
7066
7068 {
7069 _destroy();
7070 _create();
7071 }
7072
7074 {
7075 reset();
7076
7077 for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling())
7078 append_copy(cur);
7079 }
7080
7082 {
7083 assert(!_root);
7084
7085 #ifdef PUGIXML_COMPACT
7086 // space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit
7087 const size_t page_offset = sizeof(void*);
7088 #else
7089 const size_t page_offset = 0;
7090 #endif
7091
7092 // initialize sentinel page
7093 PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory));
7094
7095 // prepare page structure
7096 impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory);
7097 assert(page);
7098
7099 page->busy_size = impl::xml_memory_page_size;
7100
7101 // setup first page marker
7102 #ifdef PUGIXML_COMPACT
7103 // round-trip through void* to avoid 'cast increases required alignment of target type' warning
7104 page->compact_page_marker = reinterpret_cast<uint32_t*>(static_cast<void*>(reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page)));
7105 *page->compact_page_marker = sizeof(impl::xml_memory_page);
7106 #endif
7107
7108 // allocate new root
7109 _root = new (reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page);
7111
7112 // setup sentinel page
7113 page->allocator = static_cast<impl::xml_document_struct*>(_root);
7114
7115 // setup hash table pointer in allocator
7116 #ifdef PUGIXML_COMPACT
7117 page->allocator->_hash = &static_cast<impl::xml_document_struct*>(_root)->hash;
7118 #endif
7119
7120 // verify the document allocation
7121 assert(reinterpret_cast<char*>(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory));
7122 }
7123
7125 {
7126 assert(_root);
7127
7128 // destroy static storage
7129 if (_buffer)
7130 {
7131 impl::xml_memory::deallocate(_buffer);
7132 _buffer = 0;
7133 }
7134
7135 // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator)
7136 for (impl::xml_extra_buffer* extra = static_cast<impl::xml_document_struct*>(_root)->extra_buffers; extra; extra = extra->next)
7137 {
7138 if (extra->buffer) impl::xml_memory::deallocate(extra->buffer);
7139 }
7140
7141 // destroy dynamic storage, leave sentinel page (it's in static memory)
7142 impl::xml_memory_page* root_page = PUGI__GETPAGE(_root);
7143 assert(root_page && !root_page->prev);
7144 assert(reinterpret_cast<char*>(root_page) >= _memory && reinterpret_cast<char*>(root_page) < _memory + sizeof(_memory));
7145
7146 for (impl::xml_memory_page* page = root_page->next; page; )
7147 {
7148 impl::xml_memory_page* next = page->next;
7149
7150 impl::xml_allocator::deallocate_page(page);
7151
7152 page = next;
7153 }
7154
7155 #ifdef PUGIXML_COMPACT
7156 // destroy hash table
7157 static_cast<impl::xml_document_struct*>(_root)->hash.clear();
7158 #endif
7159
7160 _root = 0;
7161 }
7162
7163#ifdef PUGIXML_HAS_MOVE
7165 {
7166 impl::xml_document_struct* doc = static_cast<impl::xml_document_struct*>(_root);
7167 impl::xml_document_struct* other = static_cast<impl::xml_document_struct*>(rhs._root);
7168
7169 // save first child pointer for later; this needs hash access
7170 xml_node_struct* other_first_child = other->first_child;
7171
7172 #ifdef PUGIXML_COMPACT
7173 // reserve space for the hash table up front; this is the only operation that can fail
7174 // if it does, we have no choice but to throw (if we have exceptions)
7175 if (other_first_child)
7176 {
7177 size_t other_children = 0;
7178 for (xml_node_struct* node = other_first_child; node; node = node->next_sibling)
7179 other_children++;
7180
7181 // in compact mode, each pointer assignment could result in a hash table request
7182 // during move, we have to relocate document first_child and parents of all children
7183 // normally there's just one child and its parent has a pointerless encoding but
7184 // we assume the worst here
7185 if (!other->_hash->reserve(other_children + 1))
7186 {
7187 #ifdef PUGIXML_NO_EXCEPTIONS
7188 return;
7189 #else
7190 throw std::bad_alloc();
7191 #endif
7192 }
7193 }
7194 #endif
7195
7196 // move allocation state
7197 doc->_root = other->_root;
7198 doc->_busy_size = other->_busy_size;
7199
7200 // move buffer state
7201 doc->buffer = other->buffer;
7202 doc->extra_buffers = other->extra_buffers;
7203 _buffer = rhs._buffer;
7204
7205 #ifdef PUGIXML_COMPACT
7206 // 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
7207 doc->hash = other->hash;
7208 doc->_hash = &doc->hash;
7209
7210 // make sure we don't access other hash up until the end when we reinitialize other document
7211 other->_hash = 0;
7212 #endif
7213
7214 // move page structure
7215 impl::xml_memory_page* doc_page = PUGI__GETPAGE(doc);
7216 assert(doc_page && !doc_page->prev && !doc_page->next);
7217
7218 impl::xml_memory_page* other_page = PUGI__GETPAGE(other);
7219 assert(other_page && !other_page->prev);
7220
7221 // relink pages since root page is embedded into xml_document
7222 if (impl::xml_memory_page* page = other_page->next)
7223 {
7224 assert(page->prev == other_page);
7225
7226 page->prev = doc_page;
7227
7228 doc_page->next = page;
7229 other_page->next = 0;
7230 }
7231
7232 // make sure pages point to the correct document state
7233 for (impl::xml_memory_page* page = doc_page->next; page; page = page->next)
7234 {
7235 assert(page->allocator == other);
7236
7237 page->allocator = doc;
7238
7239 #ifdef PUGIXML_COMPACT
7240 // this automatically migrates most children between documents and prevents ->parent assignment from allocating
7241 if (page->compact_shared_parent == other)
7242 page->compact_shared_parent = doc;
7243 #endif
7244 }
7245
7246 // move tree structure
7247 assert(!doc->first_child);
7248
7249 doc->first_child = other_first_child;
7250
7251 for (xml_node_struct* node = other_first_child; node; node = node->next_sibling)
7252 {
7253 #ifdef PUGIXML_COMPACT
7254 // most children will have migrated when we reassigned compact_shared_parent
7255 assert(node->parent == other || node->parent == doc);
7256
7257 node->parent = doc;
7258 #else
7259 assert(node->parent == other);
7260 node->parent = doc;
7261 #endif
7262 }
7263
7264 // reset other document
7265 new (other) impl::xml_document_struct(PUGI__GETPAGE(other));
7266 rhs._buffer = 0;
7267 }
7268#endif
7269
7270#ifndef PUGIXML_NO_STL
7271 PUGI__FN xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)
7272 {
7273 reset();
7274
7275 return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding, &_buffer);
7276 }
7277
7278 PUGI__FN xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)
7279 {
7280 reset();
7281
7282 return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding_wchar, &_buffer);
7283 }
7284#endif
7285
7286 PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options)
7287 {
7288 // Force native encoding (skip autodetection)
7289 #ifdef PUGIXML_WCHAR_MODE
7290 xml_encoding encoding = encoding_wchar;
7291 #else
7292 xml_encoding encoding = encoding_utf8;
7293 #endif
7294
7295 return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding);
7296 }
7297
7298 PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
7299 {
7300 return load_string(contents, options);
7301 }
7302
7303 PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding)
7304 {
7305 reset();
7306
7307 using impl::auto_deleter; // MSVC7 workaround
7308 auto_deleter<FILE> file(fopen(path_, "rb"), impl::close_file);
7309
7310 return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
7311 }
7312
7313 PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)
7314 {
7315 reset();
7316
7317 using impl::auto_deleter; // MSVC7 workaround
7318 auto_deleter<FILE> file(impl::open_file_wide(path_, L"rb"), impl::close_file);
7319
7320 return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
7321 }
7322
7323 PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
7324 {
7325 reset();
7326
7327 return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, const_cast<void*>(contents), size, options, encoding, false, false, &_buffer);
7328 }
7329
7330 PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding)
7331 {
7332 reset();
7333
7334 return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, false, &_buffer);
7335 }
7336
7337 PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding)
7338 {
7339 reset();
7340
7341 return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, true, &_buffer);
7342 }
7343
7344 PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const
7345 {
7346 impl::xml_buffered_writer buffered_writer(writer, encoding);
7347
7348 if ((flags & format_write_bom) && encoding != encoding_latin1)
7349 {
7350 // BOM always represents the codepoint U+FEFF, so just write it in native encoding
7351 #ifdef PUGIXML_WCHAR_MODE
7352 unsigned int bom = 0xfeff;
7353 buffered_writer.write(static_cast<wchar_t>(bom));
7354 #else
7355 buffered_writer.write('\xef', '\xbb', '\xbf');
7356 #endif
7357 }
7358
7359 if (!(flags & format_no_declaration) && !impl::has_declaration(_root))
7360 {
7361 buffered_writer.write_string(PUGIXML_TEXT("<?xml version=\"1.0\""));
7362 if (encoding == encoding_latin1) buffered_writer.write_string(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));
7363 buffered_writer.write('?', '>');
7364 if (!(flags & format_raw)) buffered_writer.write('\n');
7365 }
7366
7367 impl::node_output(buffered_writer, _root, indent, flags, 0);
7368
7369 buffered_writer.flush();
7370 }
7371
7372#ifndef PUGIXML_NO_STL
7373 PUGI__FN void xml_document::save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const
7374 {
7375 xml_writer_stream writer(stream);
7376
7377 save(writer, indent, flags, encoding);
7378 }
7379
7380 PUGI__FN void xml_document::save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags) const
7381 {
7382 xml_writer_stream writer(stream);
7383
7384 save(writer, indent, flags, encoding_wchar);
7385 }
7386#endif
7387
7388 PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
7389 {
7390 using impl::auto_deleter; // MSVC7 workaround
7391 auto_deleter<FILE> file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file);
7392
7393 return impl::save_file_impl(*this, file.data, indent, flags, encoding);
7394 }
7395
7396 PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
7397 {
7398 using impl::auto_deleter; // MSVC7 workaround
7399 auto_deleter<FILE> file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file);
7400
7401 return impl::save_file_impl(*this, file.data, indent, flags, encoding);
7402 }
7403
7405 {
7406 assert(_root);
7407
7408 for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
7409 if (PUGI__NODETYPE(i) == node_element)
7410 return xml_node(i);
7411
7412 return xml_node();
7413 }
7414
7415#ifndef PUGIXML_NO_STL
7416 PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str)
7417 {
7418 assert(str);
7419
7420 return impl::as_utf8_impl(str, impl::strlength_wide(str));
7421 }
7422
7423 PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t>& str)
7424 {
7425 return impl::as_utf8_impl(str.c_str(), str.size());
7426 }
7427
7428 PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str)
7429 {
7430 assert(str);
7431
7432 return impl::as_wide_impl(str, strlen(str));
7433 }
7434
7435 PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const std::string& str)
7436 {
7437 return impl::as_wide_impl(str.c_str(), str.size());
7438 }
7439#endif
7440
7442 {
7443 impl::xml_memory::allocate = allocate;
7444 impl::xml_memory::deallocate = deallocate;
7445 }
7446
7448 {
7449 return impl::xml_memory::allocate;
7450 }
7451
7453 {
7454 return impl::xml_memory::deallocate;
7455 }
7456}
7457
7458#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))
7459namespace std
7460{
7461 // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
7462 PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&)
7463 {
7464 return std::bidirectional_iterator_tag();
7465 }
7466
7467 PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&)
7468 {
7469 return std::bidirectional_iterator_tag();
7470 }
7471
7472 PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
7473 {
7474 return std::bidirectional_iterator_tag();
7475 }
7476}
7477#endif
7478
7479#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)
7480namespace std
7481{
7482 // Workarounds for (non-standard) iterator category detection
7483 PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&)
7484 {
7485 return std::bidirectional_iterator_tag();
7486 }
7487
7488 PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&)
7489 {
7490 return std::bidirectional_iterator_tag();
7491 }
7492
7493 PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
7494 {
7495 return std::bidirectional_iterator_tag();
7496 }
7497}
7498#endif
7499
7500#ifndef PUGIXML_NO_XPATH
7501// STL replacements
7504 {
7505 template <typename T> bool operator()(const T& lhs, const T& rhs) const
7506 {
7507 return lhs == rhs;
7508 }
7509 };
7510
7512 {
7513 template <typename T> bool operator()(const T& lhs, const T& rhs) const
7514 {
7515 return lhs != rhs;
7516 }
7517 };
7518
7519 struct less
7520 {
7521 template <typename T> bool operator()(const T& lhs, const T& rhs) const
7522 {
7523 return lhs < rhs;
7524 }
7525 };
7526
7528 {
7529 template <typename T> bool operator()(const T& lhs, const T& rhs) const
7530 {
7531 return lhs <= rhs;
7532 }
7533 };
7534
7535 template <typename T> void swap(T& lhs, T& rhs)
7536 {
7537 T temp = lhs;
7538 lhs = rhs;
7539 rhs = temp;
7540 }
7541
7542 template <typename I, typename Pred> I min_element(I begin, I end, const Pred& pred)
7543 {
7544 I result = begin;
7545
7546 for (I it = begin + 1; it != end; ++it)
7547 if (pred(*it, *result))
7548 result = it;
7549
7550 return result;
7551 }
7552
7553 template <typename I> void reverse(I begin, I end)
7554 {
7555 while (end - begin > 1) swap(*begin++, *--end);
7556 }
7557
7558 template <typename I> I unique(I begin, I end)
7559 {
7560 // fast skip head
7561 while (end - begin > 1 && *begin != *(begin + 1)) begin++;
7562
7563 if (begin == end) return begin;
7564
7565 // last written element
7566 I write = begin++;
7567
7568 // merge unique elements
7569 while (begin != end)
7570 {
7571 if (*begin != *write)
7572 *++write = *begin++;
7573 else
7574 begin++;
7575 }
7576
7577 // past-the-end (write points to live element)
7578 return write + 1;
7579 }
7580
7581 template <typename T, typename Pred> void insertion_sort(T* begin, T* end, const Pred& pred)
7582 {
7583 if (begin == end)
7584 return;
7585
7586 for (T* it = begin + 1; it != end; ++it)
7587 {
7588 T val = *it;
7589 T* hole = it;
7590
7591 // move hole backwards
7592 while (hole > begin && pred(val, *(hole - 1)))
7593 {
7594 *hole = *(hole - 1);
7595 hole--;
7596 }
7597
7598 // fill hole with element
7599 *hole = val;
7600 }
7601 }
7602
7603 template <typename I, typename Pred> I median3(I first, I middle, I last, const Pred& pred)
7604 {
7605 if (pred(*middle, *first)) swap(middle, first);
7606 if (pred(*last, *middle)) swap(last, middle);
7607 if (pred(*middle, *first)) swap(middle, first);
7608
7609 return middle;
7610 }
7611
7612 template <typename T, typename Pred> void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
7613 {
7614 // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups)
7615 T* eq = begin;
7616 T* lt = begin;
7617 T* gt = end;
7618
7619 while (lt < gt)
7620 {
7621 if (pred(*lt, pivot))
7622 lt++;
7623 else if (*lt == pivot)
7624 swap(*eq++, *lt++);
7625 else
7626 swap(*lt, *--gt);
7627 }
7628
7629 // we now have just 4 groups: = < >; move equal elements to the middle
7630 T* eqbeg = gt;
7631
7632 for (T* it = begin; it != eq; ++it)
7633 swap(*it, *--eqbeg);
7634
7635 *out_eqbeg = eqbeg;
7636 *out_eqend = gt;
7637 }
7638
7639 template <typename I, typename Pred> void sort(I begin, I end, const Pred& pred)
7640 {
7641 // sort large chunks
7642 while (end - begin > 16)
7643 {
7644 // find median element
7645 I middle = begin + (end - begin) / 2;
7646 I median = median3(begin, middle, end - 1, pred);
7647
7648 // partition in three chunks (< = >)
7649 I eqbeg, eqend;
7650 partition3(begin, end, *median, pred, &eqbeg, &eqend);
7651
7652 // loop on larger half
7653 if (eqbeg - begin > end - eqend)
7654 {
7655 sort(eqend, end, pred);
7656 end = eqbeg;
7657 }
7658 else
7659 {
7660 sort(begin, eqbeg, pred);
7661 begin = eqend;
7662 }
7663 }
7664
7665 // insertion sort small chunk
7666 insertion_sort(begin, end, pred);
7667 }
7669
7670// Allocator used for AST and evaluation stacks
7672 static const size_t xpath_memory_page_size =
7673 #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
7674 PUGIXML_MEMORY_XPATH_PAGE_SIZE
7675 #else
7676 4096
7677 #endif
7678 ;
7679
7680 static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*);
7681
7683 {
7685 size_t capacity;
7686
7687 union
7688 {
7691 };
7692 };
7693
7695 {
7698 bool* _error;
7699
7700 xpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error)
7701 {
7702 }
7703
7704 void* allocate(size_t size)
7705 {
7706 // round size up to block alignment boundary
7708
7709 if (_root_size + size <= _root->capacity)
7710 {
7711 void* buf = &_root->data[0] + _root_size;
7712 _root_size += size;
7713 return buf;
7714 }
7715 else
7716 {
7717 // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests
7718 size_t block_capacity_base = sizeof(_root->data);
7719 size_t block_capacity_req = size + block_capacity_base / 4;
7720 size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req;
7721
7722 size_t block_size = block_capacity + offsetof(xpath_memory_block, data);
7723
7724 xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size));
7725 if (!block)
7726 {
7727 if (_error) *_error = true;
7728 return 0;
7729 }
7730
7731 block->next = _root;
7732 block->capacity = block_capacity;
7733
7734 _root = block;
7735 _root_size = size;
7736
7737 return block->data;
7738 }
7739 }
7740
7741 void* reallocate(void* ptr, size_t old_size, size_t new_size)
7742 {
7743 // round size up to block alignment boundary
7744 old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
7745 new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
7746
7747 // we can only reallocate the last object
7748 assert(ptr == 0 || static_cast<char*>(ptr) + old_size == &_root->data[0] + _root_size);
7749
7750 // try to reallocate the object inplace
7751 if (ptr && _root_size - old_size + new_size <= _root->capacity)
7752 {
7753 _root_size = _root_size - old_size + new_size;
7754 return ptr;
7755 }
7756
7757 // allocate a new block
7758 void* result = allocate(new_size);
7759 if (!result) return 0;
7760
7761 // we have a new block
7762 if (ptr)
7763 {
7764 // copy old data (we only support growing)
7765 assert(new_size >= old_size);
7766 memcpy(result, ptr, old_size);
7767
7768 // free the previous page if it had no other objects
7769 assert(_root->data == result);
7770 assert(_root->next);
7771
7772 if (_root->next->data == ptr)
7773 {
7774 // deallocate the whole page, unless it was the first one
7776
7777 if (next)
7778 {
7780 _root->next = next;
7781 }
7782 }
7783 }
7784
7785 return result;
7786 }
7787
7788 void revert(const xpath_allocator& state)
7789 {
7790 // free all new pages
7792
7793 while (cur != state._root)
7794 {
7795 xpath_memory_block* next = cur->next;
7796
7798
7799 cur = next;
7800 }
7801
7802 // restore state
7803 _root = state._root;
7804 _root_size = state._root_size;
7805 }
7806
7807 void release()
7808 {
7810 assert(cur);
7811
7812 while (cur->next)
7813 {
7814 xpath_memory_block* next = cur->next;
7815
7817
7818 cur = next;
7819 }
7820 }
7821 };
7822
7837
7843
7845 {
7850 bool oom;
7851
7853 {
7854 blocks[0].next = blocks[1].next = 0;
7855 blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data);
7856
7857 stack.result = &result;
7858 stack.temp = &temp;
7859 }
7860
7862 {
7863 result.release();
7864 temp.release();
7865 }
7866 };
7868
7869// String class
7872 {
7873 const char_t* _buffer;
7876
7877 static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)
7878 {
7879 char_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t)));
7880 if (!result) return 0;
7881
7882 memcpy(result, string, length * sizeof(char_t));
7883 result[length] = 0;
7884
7885 return result;
7886 }
7887
7888 xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap)
7889 {
7890 }
7891
7892 public:
7893 static xpath_string from_const(const char_t* str)
7894 {
7895 return xpath_string(str, false, 0);
7896 }
7897
7898 static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end)
7899 {
7900 assert(begin <= end && *end == 0);
7901
7902 return xpath_string(begin, true, static_cast<size_t>(end - begin));
7903 }
7904
7905 static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc)
7906 {
7907 assert(begin <= end);
7908
7909 if (begin == end)
7910 return xpath_string();
7911
7912 size_t length = static_cast<size_t>(end - begin);
7913 const char_t* data = duplicate_string(begin, length, alloc);
7914
7915 return data ? xpath_string(data, true, length) : xpath_string();
7916 }
7917
7919 {
7920 }
7921
7922 void append(const xpath_string& o, xpath_allocator* alloc)
7923 {
7924 // skip empty sources
7925 if (!*o._buffer) return;
7926
7927 // fast append for constant empty target and constant source
7928 if (!*_buffer && !_uses_heap && !o._uses_heap)
7929 {
7930 _buffer = o._buffer;
7931 }
7932 else
7933 {
7934 // need to make heap copy
7935 size_t target_length = length();
7936 size_t source_length = o.length();
7937 size_t result_length = target_length + source_length;
7938
7939 // allocate new buffer
7940 char_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t)));
7941 if (!result) return;
7942
7943 // append first string to the new buffer in case there was no reallocation
7944 if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t));
7945
7946 // append second string to the new buffer
7947 memcpy(result + target_length, o._buffer, source_length * sizeof(char_t));
7948 result[result_length] = 0;
7949
7950 // finalize
7951 _buffer = result;
7952 _uses_heap = true;
7953 _length_heap = result_length;
7954 }
7955 }
7956
7957 const char_t* c_str() const
7958 {
7959 return _buffer;
7960 }
7961
7962 size_t length() const
7963 {
7965 }
7966
7967 char_t* data(xpath_allocator* alloc)
7968 {
7969 // make private heap copy
7970 if (!_uses_heap)
7971 {
7972 size_t length_ = strlength(_buffer);
7973 const char_t* data_ = duplicate_string(_buffer, length_, alloc);
7974
7975 if (!data_) return 0;
7976
7977 _buffer = data_;
7978 _uses_heap = true;
7979 _length_heap = length_;
7980 }
7981
7982 return const_cast<char_t*>(_buffer);
7983 }
7984
7985 bool empty() const
7986 {
7987 return *_buffer == 0;
7988 }
7989
7990 bool operator==(const xpath_string& o) const
7991 {
7992 return strequal(_buffer, o._buffer);
7993 }
7994
7995 bool operator!=(const xpath_string& o) const
7996 {
7997 return !strequal(_buffer, o._buffer);
7998 }
7999
8000 bool uses_heap() const
8001 {
8002 return _uses_heap;
8003 }
8004 };
8006
8008 PUGI__FN bool starts_with(const char_t* string, const char_t* pattern)
8009 {
8010 while (*pattern && *string == *pattern)
8011 {
8012 string++;
8013 pattern++;
8014 }
8015
8016 return *pattern == 0;
8017 }
8018
8019 PUGI__FN const char_t* find_char(const char_t* s, char_t c)
8020 {
8021 #ifdef PUGIXML_WCHAR_MODE
8022 return wcschr(s, c);
8023 #else
8024 return strchr(s, c);
8025 #endif
8026 }
8027
8028 PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p)
8029 {
8030 #ifdef PUGIXML_WCHAR_MODE
8031 // MSVC6 wcsstr bug workaround (if s is empty it always returns 0)
8032 return (*p == 0) ? s : wcsstr(s, p);
8033 #else
8034 return strstr(s, p);
8035 #endif
8036 }
8037
8038 // Converts symbol to lower case, if it is an ASCII one
8039 PUGI__FN char_t tolower_ascii(char_t ch)
8040 {
8041 return static_cast<unsigned int>(ch - 'A') < 26 ? static_cast<char_t>(ch | ' ') : ch;
8042 }
8043
8045 {
8046 if (na.attribute())
8047 return xpath_string::from_const(na.attribute().value());
8048 else
8049 {
8050 xml_node n = na.node();
8051
8052 switch (n.type())
8053 {
8054 case node_pcdata:
8055 case node_cdata:
8056 case node_comment:
8057 case node_pi:
8058 return xpath_string::from_const(n.value());
8059
8060 case node_document:
8061 case node_element:
8062 {
8063 xpath_string result;
8064
8065 // element nodes can have value if parse_embed_pcdata was used
8066 if (n.value()[0])
8067 result.append(xpath_string::from_const(n.value()), alloc);
8068
8069 xml_node cur = n.first_child();
8070
8071 while (cur && cur != n)
8072 {
8073 if (cur.type() == node_pcdata || cur.type() == node_cdata)
8074 result.append(xpath_string::from_const(cur.value()), alloc);
8075
8076 if (cur.first_child())
8077 cur = cur.first_child();
8078 else if (cur.next_sibling())
8079 cur = cur.next_sibling();
8080 else
8081 {
8082 while (!cur.next_sibling() && cur != n)
8083 cur = cur.parent();
8084
8085 if (cur != n) cur = cur.next_sibling();
8086 }
8087 }
8088
8089 return result;
8090 }
8091
8092 default:
8093 return xpath_string();
8094 }
8095 }
8096 }
8097
8099 {
8100 assert(ln->parent == rn->parent);
8101
8102 // there is no common ancestor (the shared parent is null), nodes are from different documents
8103 if (!ln->parent) return ln < rn;
8104
8105 // determine sibling order
8106 xml_node_struct* ls = ln;
8107 xml_node_struct* rs = rn;
8108
8109 while (ls && rs)
8110 {
8111 if (ls == rn) return true;
8112 if (rs == ln) return false;
8113
8114 ls = ls->next_sibling;
8115 rs = rs->next_sibling;
8116 }
8117
8118 // if rn sibling chain ended ln must be before rn
8119 return !rs;
8120 }
8121
8123 {
8124 // find common ancestor at the same depth, if any
8125 xml_node_struct* lp = ln;
8126 xml_node_struct* rp = rn;
8127
8128 while (lp && rp && lp->parent != rp->parent)
8129 {
8130 lp = lp->parent;
8131 rp = rp->parent;
8132 }
8133
8134 // parents are the same!
8135 if (lp && rp) return node_is_before_sibling(lp, rp);
8136
8137 // nodes are at different depths, need to normalize heights
8138 bool left_higher = !lp;
8139
8140 while (lp)
8141 {
8142 lp = lp->parent;
8143 ln = ln->parent;
8144 }
8145
8146 while (rp)
8147 {
8148 rp = rp->parent;
8149 rn = rn->parent;
8150 }
8151
8152 // one node is the ancestor of the other
8153 if (ln == rn) return left_higher;
8154
8155 // find common ancestor... again
8156 while (ln->parent != rn->parent)
8157 {
8158 ln = ln->parent;
8159 rn = rn->parent;
8160 }
8161
8162 return node_is_before_sibling(ln, rn);
8163 }
8164
8166 {
8167 while (node && node != parent) node = node->parent;
8168
8169 return parent && node == parent;
8170 }
8171
8172 PUGI__FN const void* document_buffer_order(const xpath_node& xnode)
8173 {
8174 xml_node_struct* node = xnode.node().internal_object();
8175
8176 if (node)
8177 {
8178 if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0)
8179 {
8180 if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name;
8181 if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value;
8182 }
8183
8184 return 0;
8185 }
8186
8187 xml_attribute_struct* attr = xnode.attribute().internal_object();
8188
8189 if (attr)
8190 {
8191 if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0)
8192 {
8193 if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name;
8194 if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value;
8195 }
8196
8197 return 0;
8198 }
8199
8200 return 0;
8201 }
8202
8204 {
8205 bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
8206 {
8207 // optimized document order based check
8208 const void* lo = document_buffer_order(lhs);
8209 const void* ro = document_buffer_order(rhs);
8210
8211 if (lo && ro) return lo < ro;
8212
8213 // slow comparison
8214 xml_node ln = lhs.node(), rn = rhs.node();
8215
8216 // compare attributes
8217 if (lhs.attribute() && rhs.attribute())
8218 {
8219 // shared parent
8220 if (lhs.parent() == rhs.parent())
8221 {
8222 // determine sibling order
8223 for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute())
8224 if (a == rhs.attribute())
8225 return true;
8226
8227 return false;
8228 }
8229
8230 // compare attribute parents
8231 ln = lhs.parent();
8232 rn = rhs.parent();
8233 }
8234 else if (lhs.attribute())
8235 {
8236 // attributes go after the parent element
8237 if (lhs.parent() == rhs.node()) return false;
8238
8239 ln = lhs.parent();
8240 }
8241 else if (rhs.attribute())
8242 {
8243 // attributes go after the parent element
8244 if (rhs.parent() == lhs.node()) return true;
8245
8246 rn = rhs.parent();
8247 }
8248
8249 if (ln == rn) return false;
8250
8251 if (!ln || !rn) return ln < rn;
8252
8253 return node_is_before(ln.internal_object(), rn.internal_object());
8254 }
8255 };
8256
8258 {
8259 bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
8260 {
8261 if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true;
8262 else return rhs.attribute() ? false : lhs.node() < rhs.node();
8263 }
8264 };
8265
8267 {
8268 #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))
8269 PUGI__STATIC_ASSERT(sizeof(float) == sizeof(uint32_t));
8270 typedef uint32_t UI; // BCC5 workaround
8271 union { float f; UI i; } u;
8272 u.i = 0x7fc00000;
8273 return u.f;
8274 #else
8275 // fallback
8276 const volatile double zero = 0.0;
8277 return zero / zero;
8278 #endif
8279 }
8280
8281 PUGI__FN bool is_nan(double value)
8282 {
8283 #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
8284 return !!_isnan(value);
8285 #elif defined(fpclassify) && defined(FP_NAN)
8286 return fpclassify(value) == FP_NAN;
8287 #else
8288 // fallback
8289 const volatile double v = value;
8290 return v != v;
8291 #endif
8292 }
8293
8294 PUGI__FN const char_t* convert_number_to_string_special(double value)
8295 {
8296 #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
8297 if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0;
8298 if (_isnan(value)) return PUGIXML_TEXT("NaN");
8299 return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
8300 #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO)
8301 switch (fpclassify(value))
8302 {
8303 case FP_NAN:
8304 return PUGIXML_TEXT("NaN");
8305
8306 case FP_INFINITE:
8307 return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
8308
8309 case FP_ZERO:
8310 return PUGIXML_TEXT("0");
8311
8312 default:
8313 return 0;
8314 }
8315 #else
8316 // fallback
8317 const volatile double v = value;
8318
8319 if (v == 0) return PUGIXML_TEXT("0");
8320 if (v != v) return PUGIXML_TEXT("NaN");
8321 if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
8322 return 0;
8323 #endif
8324 }
8325
8327 {
8328 return (value != 0 && !is_nan(value));
8329 }
8330
8332 {
8333 while (begin != end && end[-1] == '0') end--;
8334
8335 *end = 0;
8336 }
8337
8338 // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent
8339#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
8340 PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
8341 {
8342 // get base values
8343 int sign, exponent;
8344 _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign);
8345
8346 // truncate redundant zeros
8347 truncate_zeros(buffer, buffer + strlen(buffer));
8348
8349 // fill results
8350 *out_mantissa = buffer;
8351 *out_exponent = exponent;
8352 }
8353#else
8354 PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
8355 {
8356 // get a scientific notation value with IEEE DBL_DIG decimals
8357 PUGI__SNPRINTF(buffer, "%.*e", DBL_DIG, value);
8358
8359 // get the exponent (possibly negative)
8360 char* exponent_string = strchr(buffer, 'e');
8361 assert(exponent_string);
8362
8363 int exponent = atoi(exponent_string + 1);
8364
8365 // extract mantissa string: skip sign
8366 char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer;
8367 assert(mantissa[0] != '0' && mantissa[1] == '.');
8368
8369 // divide mantissa by 10 to eliminate integer part
8370 mantissa[1] = mantissa[0];
8371 mantissa++;
8372 exponent++;
8373
8374 // remove extra mantissa digits and zero-terminate mantissa
8375 truncate_zeros(mantissa, exponent_string);
8376
8377 // fill results
8378 *out_mantissa = mantissa;
8379 *out_exponent = exponent;
8380 }
8381#endif
8382
8384 {
8385 // try special number conversion
8386 const char_t* special = convert_number_to_string_special(value);
8387 if (special) return xpath_string::from_const(special);
8388
8389 // get mantissa + exponent form
8390 char mantissa_buffer[32];
8391
8392 char* mantissa;
8393 int exponent;
8394 convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent);
8395
8396 // allocate a buffer of suitable length for the number
8397 size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4;
8398 char_t* result = static_cast<char_t*>(alloc->allocate(sizeof(char_t) * result_size));
8399 if (!result) return xpath_string();
8400
8401 // make the number!
8402 char_t* s = result;
8403
8404 // sign
8405 if (value < 0) *s++ = '-';
8406
8407 // integer part
8408 if (exponent <= 0)
8409 {
8410 *s++ = '0';
8411 }
8412 else
8413 {
8414 while (exponent > 0)
8415 {
8416 assert(*mantissa == 0 || static_cast<unsigned int>(*mantissa - '0') <= 9);
8417 *s++ = *mantissa ? *mantissa++ : '0';
8418 exponent--;
8419 }
8420 }
8421
8422 // fractional part
8423 if (*mantissa)
8424 {
8425 // decimal point
8426 *s++ = '.';
8427
8428 // extra zeroes from negative exponent
8429 while (exponent < 0)
8430 {
8431 *s++ = '0';
8432 exponent++;
8433 }
8434
8435 // extra mantissa digits
8436 while (*mantissa)
8437 {
8438 assert(static_cast<unsigned int>(*mantissa - '0') <= 9);
8439 *s++ = *mantissa++;
8440 }
8441 }
8442
8443 // zero-terminate
8444 assert(s < result + result_size);
8445 *s = 0;
8446
8447 return xpath_string::from_heap_preallocated(result, s);
8448 }
8449
8450 PUGI__FN bool check_string_to_number_format(const char_t* string)
8451 {
8452 // parse leading whitespace
8453 while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;
8454
8455 // parse sign
8456 if (*string == '-') ++string;
8457
8458 if (!*string) return false;
8459
8460 // if there is no integer part, there should be a decimal part with at least one digit
8461 if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false;
8462
8463 // parse integer part
8464 while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;
8465
8466 // parse decimal part
8467 if (*string == '.')
8468 {
8469 ++string;
8470
8471 while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;
8472 }
8473
8474 // parse trailing whitespace
8475 while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;
8476
8477 return *string == 0;
8478 }
8479
8480 PUGI__FN double convert_string_to_number(const char_t* string)
8481 {
8482 // check string format
8483 if (!check_string_to_number_format(string)) return gen_nan();
8484
8485 // parse string
8486 #ifdef PUGIXML_WCHAR_MODE
8487 return wcstod(string, 0);
8488 #else
8489 return strtod(string, 0);
8490 #endif
8491 }
8492
8493 PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result)
8494 {
8495 size_t length = static_cast<size_t>(end - begin);
8496 char_t* scratch = buffer;
8497
8498 if (length >= sizeof(buffer) / sizeof(buffer[0]))
8499 {
8500 // need to make dummy on-heap copy
8501 scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
8502 if (!scratch) return false;
8503 }
8504
8505 // copy string to zero-terminated buffer and perform conversion
8506 memcpy(scratch, begin, length * sizeof(char_t));
8507 scratch[length] = 0;
8508
8509 *out_result = convert_string_to_number(scratch);
8510
8511 // free dummy buffer
8512 if (scratch != buffer) xml_memory::deallocate(scratch);
8513
8514 return true;
8515 }
8516
8517 PUGI__FN double round_nearest(double value)
8518 {
8519 return floor(value + 0.5);
8520 }
8521
8522 PUGI__FN double round_nearest_nzero(double value)
8523 {
8524 // same as round_nearest, but returns -0 for [-0.5, -0]
8525 // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0)
8526 return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5);
8527 }
8528
8529 PUGI__FN const char_t* qualified_name(const xpath_node& node)
8530 {
8531 return node.attribute() ? node.attribute().name() : node.node().name();
8532 }
8533
8534 PUGI__FN const char_t* local_name(const xpath_node& node)
8535 {
8536 const char_t* name = qualified_name(node);
8537 const char_t* p = find_char(name, ':');
8538
8539 return p ? p + 1 : name;
8540 }
8541
8543 {
8544 const char_t* prefix;
8546
8547 namespace_uri_predicate(const char_t* name)
8548 {
8549 const char_t* pos = find_char(name, ':');
8550
8551 prefix = pos ? name : 0;
8552 prefix_length = pos ? static_cast<size_t>(pos - name) : 0;
8553 }
8554
8555 bool operator()(xml_attribute a) const
8556 {
8557 const char_t* name = a.name();
8558
8559 if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false;
8560
8561 return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0;
8562 }
8563 };
8564
8565 PUGI__FN const char_t* namespace_uri(xml_node node)
8566 {
8567 namespace_uri_predicate pred = node.name();
8568
8569 xml_node p = node;
8570
8571 while (p)
8572 {
8573 xml_attribute a = p.find_attribute(pred);
8574
8575 if (a) return a.value();
8576
8577 p = p.parent();
8578 }
8579
8580 return PUGIXML_TEXT("");
8581 }
8582
8583 PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent)
8584 {
8585 namespace_uri_predicate pred = attr.name();
8586
8587 // Default namespace does not apply to attributes
8588 if (!pred.prefix) return PUGIXML_TEXT("");
8589
8590 xml_node p = parent;
8591
8592 while (p)
8593 {
8594 xml_attribute a = p.find_attribute(pred);
8595
8596 if (a) return a.value();
8597
8598 p = p.parent();
8599 }
8600
8601 return PUGIXML_TEXT("");
8602 }
8603
8604 PUGI__FN const char_t* namespace_uri(const xpath_node& node)
8605 {
8606 return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node());
8607 }
8608
8609 PUGI__FN char_t* normalize_space(char_t* buffer)
8610 {
8611 char_t* write = buffer;
8612
8613 for (char_t* it = buffer; *it; )
8614 {
8615 char_t ch = *it++;
8616
8617 if (PUGI__IS_CHARTYPE(ch, ct_space))
8618 {
8619 // replace whitespace sequence with single space
8620 while (PUGI__IS_CHARTYPE(*it, ct_space)) it++;
8621
8622 // avoid leading spaces
8623 if (write != buffer) *write++ = ' ';
8624 }
8625 else *write++ = ch;
8626 }
8627
8628 // remove trailing space
8629 if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--;
8630
8631 // zero-terminate
8632 *write = 0;
8633
8634 return write;
8635 }
8636
8637 PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)
8638 {
8639 char_t* write = buffer;
8640
8641 while (*buffer)
8642 {
8643 PUGI__DMC_VOLATILE char_t ch = *buffer++;
8644
8645 const char_t* pos = find_char(from, ch);
8646
8647 if (!pos)
8648 *write++ = ch; // do not process
8649 else if (static_cast<size_t>(pos - from) < to_length)
8650 *write++ = to[pos - from]; // replace
8651 }
8652
8653 // zero-terminate
8654 *write = 0;
8655
8656 return write;
8657 }
8658
8659 PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to)
8660 {
8661 unsigned char table[128] = {0};
8662
8663 while (*from)
8664 {
8665 unsigned int fc = static_cast<unsigned int>(*from);
8666 unsigned int tc = static_cast<unsigned int>(*to);
8667
8668 if (fc >= 128 || tc >= 128)
8669 return 0;
8670
8671 // code=128 means "skip character"
8672 if (!table[fc])
8673 table[fc] = static_cast<unsigned char>(tc ? tc : 128);
8674
8675 from++;
8676 if (tc) to++;
8677 }
8678
8679 for (int i = 0; i < 128; ++i)
8680 if (!table[i])
8681 table[i] = static_cast<unsigned char>(i);
8682
8683 void* result = alloc->allocate(sizeof(table));
8684 if (!result) return 0;
8685
8686 memcpy(result, table, sizeof(table));
8687
8688 return static_cast<unsigned char*>(result);
8689 }
8690
8691 PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table)
8692 {
8693 char_t* write = buffer;
8694
8695 while (*buffer)
8696 {
8697 char_t ch = *buffer++;
8698 unsigned int index = static_cast<unsigned int>(ch);
8699
8700 if (index < 128)
8701 {
8702 unsigned char code = table[index];
8703
8704 // code=128 means "skip character" (table size is 128 so 128 can be a special value)
8705 // this code skips these characters without extra branches
8706 *write = static_cast<char_t>(code);
8707 write += 1 - (code >> 7);
8708 }
8709 else
8710 {
8711 *write++ = ch;
8712 }
8713 }
8714
8715 // zero-terminate
8716 *write = 0;
8717
8718 return write;
8719 }
8720
8721 inline bool is_xpath_attribute(const char_t* name)
8722 {
8723 return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':'));
8724 }
8725
8727 {
8728 xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false)
8729 {
8730 }
8731
8732 bool value;
8733 char_t name[1];
8734 };
8735
8737 {
8739 {
8740 }
8741
8742 double value;
8743 char_t name[1];
8744 };
8745
8747 {
8749 {
8750 }
8751
8756
8757 char_t* value;
8758 char_t name[1];
8759 };
8760
8762 {
8764 {
8765 }
8766
8767 xpath_node_set value;
8768 char_t name[1];
8769 };
8770
8771 static const xpath_node_set dummy_node_set;
8772
8773 PUGI__FN PUGI__UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str)
8774 {
8775 // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)
8776 unsigned int result = 0;
8777
8778 while (*str)
8779 {
8780 result += static_cast<unsigned int>(*str++);
8781 result += result << 10;
8782 result ^= result >> 6;
8783 }
8784
8785 result += result << 3;
8786 result ^= result >> 11;
8787 result += result << 15;
8788
8789 return result;
8790 }
8791
8792 template <typename T> PUGI__FN T* new_xpath_variable(const char_t* name)
8793 {
8794 size_t length = strlength(name);
8795 if (length == 0) return 0; // empty variable names are invalid
8796
8797 // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters
8798 void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t));
8799 if (!memory) return 0;
8800
8801 T* result = new (memory) T();
8802
8803 memcpy(result->name, name, (length + 1) * sizeof(char_t));
8804
8805 return result;
8806 }
8807
8808 PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name)
8809 {
8810 switch (type)
8811 {
8812 case xpath_type_node_set:
8813 return new_xpath_variable<xpath_variable_node_set>(name);
8814
8815 case xpath_type_number:
8816 return new_xpath_variable<xpath_variable_number>(name);
8817
8818 case xpath_type_string:
8819 return new_xpath_variable<xpath_variable_string>(name);
8820
8821 case xpath_type_boolean:
8822 return new_xpath_variable<xpath_variable_boolean>(name);
8823
8824 default:
8825 return 0;
8826 }
8827 }
8828
8829 template <typename T> PUGI__FN void delete_xpath_variable(T* var)
8830 {
8831 var->~T();
8833 }
8834
8835 PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var)
8836 {
8837 switch (type)
8838 {
8839 case xpath_type_node_set:
8841 break;
8842
8843 case xpath_type_number:
8844 delete_xpath_variable(static_cast<xpath_variable_number*>(var));
8845 break;
8846
8847 case xpath_type_string:
8848 delete_xpath_variable(static_cast<xpath_variable_string*>(var));
8849 break;
8850
8851 case xpath_type_boolean:
8853 break;
8854
8855 default:
8856 assert(false && "Invalid variable type"); // unreachable
8857 }
8858 }
8859
8861 {
8862 switch (rhs->type())
8863 {
8864 case xpath_type_node_set:
8865 return lhs->set(static_cast<const xpath_variable_node_set*>(rhs)->value);
8866
8867 case xpath_type_number:
8868 return lhs->set(static_cast<const xpath_variable_number*>(rhs)->value);
8869
8870 case xpath_type_string:
8871 return lhs->set(static_cast<const xpath_variable_string*>(rhs)->value);
8872
8873 case xpath_type_boolean:
8874 return lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value);
8875
8876 default:
8877 assert(false && "Invalid variable type"); // unreachable
8878 return false;
8879 }
8880 }
8881
8882 PUGI__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)
8883 {
8884 size_t length = static_cast<size_t>(end - begin);
8885 char_t* scratch = buffer;
8886
8887 if (length >= sizeof(buffer) / sizeof(buffer[0]))
8888 {
8889 // need to make dummy on-heap copy
8890 scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
8891 if (!scratch) return false;
8892 }
8893
8894 // copy string to zero-terminated buffer and perform lookup
8895 memcpy(scratch, begin, length * sizeof(char_t));
8896 scratch[length] = 0;
8897
8898 *out_result = set->get(scratch);
8899
8900 // free dummy buffer
8901 if (scratch != buffer) xml_memory::deallocate(scratch);
8902
8903 return true;
8904 }
8906
8907// Internal node set class
8909 PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end)
8910 {
8911 if (end - begin < 2)
8912 return xpath_node_set::type_sorted;
8913
8915
8916 bool first = cmp(begin[0], begin[1]);
8917
8918 for (const xpath_node* it = begin + 1; it + 1 < end; ++it)
8919 if (cmp(it[0], it[1]) != first)
8920 return xpath_node_set::type_unsorted;
8921
8922 return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse;
8923 }
8924
8925 PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev)
8926 {
8927 xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;
8928
8929 if (type == xpath_node_set::type_unsorted)
8930 {
8931 xpath_node_set::type_t sorted = xpath_get_order(begin, end);
8932
8933 if (sorted == xpath_node_set::type_unsorted)
8934 {
8936
8937 type = xpath_node_set::type_sorted;
8938 }
8939 else
8940 type = sorted;
8941 }
8942
8943 if (type != order) reverse(begin, end);
8944
8945 return order;
8946 }
8947
8948 PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type)
8949 {
8950 if (begin == end) return xpath_node();
8951
8952 switch (type)
8953 {
8954 case xpath_node_set::type_sorted:
8955 return *begin;
8956
8957 case xpath_node_set::type_sorted_reverse:
8958 return *(end - 1);
8959
8960 case xpath_node_set::type_unsorted:
8962
8963 default:
8964 assert(false && "Invalid node set type"); // unreachable
8965 return xpath_node();
8966 }
8967 }
8968
8970 {
8971 xpath_node_set::type_t _type;
8972
8973 xpath_node* _begin;
8974 xpath_node* _end;
8975 xpath_node* _eos;
8976
8977 public:
8978 xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0)
8979 {
8980 }
8981
8982 xpath_node* begin() const
8983 {
8984 return _begin;
8985 }
8986
8987 xpath_node* end() const
8988 {
8989 return _end;
8990 }
8991
8992 bool empty() const
8993 {
8994 return _begin == _end;
8995 }
8996
8997 size_t size() const
8998 {
8999 return static_cast<size_t>(_end - _begin);
9000 }
9001
9002 xpath_node first() const
9003 {
9004 return xpath_first(_begin, _end, _type);
9005 }
9006
9007 void push_back_grow(const xpath_node& node, xpath_allocator* alloc);
9008
9009 void push_back(const xpath_node& node, xpath_allocator* alloc)
9010 {
9011 if (_end != _eos)
9012 *_end++ = node;
9013 else
9014 push_back_grow(node, alloc);
9015 }
9016
9017 void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc)
9018 {
9019 if (begin_ == end_) return;
9020
9021 size_t size_ = static_cast<size_t>(_end - _begin);
9022 size_t capacity = static_cast<size_t>(_eos - _begin);
9023 size_t count = static_cast<size_t>(end_ - begin_);
9024
9025 if (size_ + count > capacity)
9026 {
9027 // reallocate the old array or allocate a new one
9028 xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node)));
9029 if (!data) return;
9030
9031 // finalize
9032 _begin = data;
9033 _end = data + size_;
9034 _eos = data + size_ + count;
9035 }
9036
9037 memcpy(_end, begin_, count * sizeof(xpath_node));
9038 _end += count;
9039 }
9040
9041 void sort_do()
9042 {
9043 _type = xpath_sort(_begin, _end, _type, false);
9044 }
9045
9046 void truncate(xpath_node* pos)
9047 {
9048 assert(_begin <= pos && pos <= _end);
9049
9050 _end = pos;
9051 }
9052
9054 {
9055 if (_type == xpath_node_set::type_unsorted)
9057
9058 _end = unique(_begin, _end);
9059 }
9060
9061 xpath_node_set::type_t type() const
9062 {
9063 return _type;
9064 }
9065
9066 void set_type(xpath_node_set::type_t value)
9067 {
9068 _type = value;
9069 }
9070 };
9071
9073 {
9074 size_t capacity = static_cast<size_t>(_eos - _begin);
9075
9076 // get new capacity (1.5x rule)
9077 size_t new_capacity = capacity + capacity / 2 + 1;
9078
9079 // reallocate the old array or allocate a new one
9080 xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));
9081 if (!data) return;
9082
9083 // finalize
9084 _begin = data;
9085 _end = data + capacity;
9086 _eos = data + new_capacity;
9087
9088 // push
9089 *_end++ = node;
9090 }
9092
9095 {
9096 xpath_node n;
9098
9099 xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_)
9100 {
9101 }
9102 };
9103
9134
9136 {
9137 const char_t* begin;
9138 const char_t* end;
9139
9141 {
9142 }
9143
9144 bool operator==(const char_t* other) const
9145 {
9146 size_t length = static_cast<size_t>(end - begin);
9147
9148 return strequalrange(other, begin, length);
9149 }
9150 };
9151
9153 {
9154 const char_t* _cur;
9155 const char_t* _cur_lexeme_pos;
9157
9159
9160 public:
9161 explicit xpath_lexer(const char_t* query): _cur(query)
9162 {
9163 next();
9164 }
9165
9166 const char_t* state() const
9167 {
9168 return _cur;
9169 }
9170
9171 void next()
9172 {
9173 const char_t* cur = _cur;
9174
9175 while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur;
9176
9177 // save lexeme position for error reporting
9178 _cur_lexeme_pos = cur;
9179
9180 switch (*cur)
9181 {
9182 case 0:
9184 break;
9185
9186 case '>':
9187 if (*(cur+1) == '=')
9188 {
9189 cur += 2;
9191 }
9192 else
9193 {
9194 cur += 1;
9196 }
9197 break;
9198
9199 case '<':
9200 if (*(cur+1) == '=')
9201 {
9202 cur += 2;
9204 }
9205 else
9206 {
9207 cur += 1;
9209 }
9210 break;
9211
9212 case '!':
9213 if (*(cur+1) == '=')
9214 {
9215 cur += 2;
9217 }
9218 else
9219 {
9221 }
9222 break;
9223
9224 case '=':
9225 cur += 1;
9227
9228 break;
9229
9230 case '+':
9231 cur += 1;
9233
9234 break;
9235
9236 case '-':
9237 cur += 1;
9239
9240 break;
9241
9242 case '*':
9243 cur += 1;
9245
9246 break;
9247
9248 case '|':
9249 cur += 1;
9251
9252 break;
9253
9254 case '$':
9255 cur += 1;
9256
9258 {
9260
9261 while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
9262
9263 if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname
9264 {
9265 cur++; // :
9266
9267 while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
9268 }
9269
9271
9273 }
9274 else
9275 {
9277 }
9278
9279 break;
9280
9281 case '(':
9282 cur += 1;
9284
9285 break;
9286
9287 case ')':
9288 cur += 1;
9290
9291 break;
9292
9293 case '[':
9294 cur += 1;
9296
9297 break;
9298
9299 case ']':
9300 cur += 1;
9302
9303 break;
9304
9305 case ',':
9306 cur += 1;
9308
9309 break;
9310
9311 case '/':
9312 if (*(cur+1) == '/')
9313 {
9314 cur += 2;
9316 }
9317 else
9318 {
9319 cur += 1;
9321 }
9322 break;
9323
9324 case '.':
9325 if (*(cur+1) == '.')
9326 {
9327 cur += 2;
9329 }
9330 else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit))
9331 {
9332 _cur_lexeme_contents.begin = cur; // .
9333
9334 ++cur;
9335
9336 while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
9337
9339
9341 }
9342 else
9343 {
9344 cur += 1;
9346 }
9347 break;
9348
9349 case '@':
9350 cur += 1;
9352
9353 break;
9354
9355 case '"':
9356 case '\'':
9357 {
9358 char_t terminator = *cur;
9359
9360 ++cur;
9361
9363 while (*cur && *cur != terminator) cur++;
9365
9366 if (!*cur)
9368 else
9369 {
9370 cur += 1;
9372 }
9373
9374 break;
9375 }
9376
9377 case ':':
9378 if (*(cur+1) == ':')
9379 {
9380 cur += 2;
9382 }
9383 else
9384 {
9386 }
9387 break;
9388
9389 default:
9390 if (PUGI__IS_CHARTYPEX(*cur, ctx_digit))
9391 {
9393
9394 while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
9395
9396 if (*cur == '.')
9397 {
9398 cur++;
9399
9400 while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
9401 }
9402
9404
9406 }
9407 else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))
9408 {
9410
9411 while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
9412
9413 if (cur[0] == ':')
9414 {
9415 if (cur[1] == '*') // namespace test ncname:*
9416 {
9417 cur += 2; // :*
9418 }
9419 else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname
9420 {
9421 cur++; // :
9422
9423 while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
9424 }
9425 }
9426
9428
9430 }
9431 else
9432 {
9434 }
9435 }
9436
9437 _cur = cur;
9438 }
9439
9441 {
9442 return _cur_lexeme;
9443 }
9444
9445 const char_t* current_pos() const
9446 {
9447 return _cur_lexeme_pos;
9448 }
9449
9456 };
9457
9459 {
9461 ast_op_or, // left or right
9462 ast_op_and, // left and right
9463 ast_op_equal, // left = right
9464 ast_op_not_equal, // left != right
9465 ast_op_less, // left < right
9466 ast_op_greater, // left > right
9467 ast_op_less_or_equal, // left <= right
9468 ast_op_greater_or_equal, // left >= right
9469 ast_op_add, // left + right
9470 ast_op_subtract, // left - right
9471 ast_op_multiply, // left * right
9472 ast_op_divide, // left / right
9473 ast_op_mod, // left % right
9474 ast_op_negate, // left - right
9475 ast_op_union, // left | right
9476 ast_predicate, // apply predicate to set; next points to next predicate
9477 ast_filter, // select * from left where right
9478 ast_string_constant, // string constant
9479 ast_number_constant, // number constant
9480 ast_variable, // variable
9481 ast_func_last, // last()
9482 ast_func_position, // position()
9483 ast_func_count, // count(left)
9484 ast_func_id, // id(left)
9485 ast_func_local_name_0, // local-name()
9486 ast_func_local_name_1, // local-name(left)
9487 ast_func_namespace_uri_0, // namespace-uri()
9488 ast_func_namespace_uri_1, // namespace-uri(left)
9490 ast_func_name_1, // name(left)
9492 ast_func_string_1, // string(left)
9493 ast_func_concat, // concat(left, right, siblings)
9494 ast_func_starts_with, // starts_with(left, right)
9495 ast_func_contains, // contains(left, right)
9496 ast_func_substring_before, // substring-before(left, right)
9497 ast_func_substring_after, // substring-after(left, right)
9498 ast_func_substring_2, // substring(left, right)
9499 ast_func_substring_3, // substring(left, right, third)
9500 ast_func_string_length_0, // string-length()
9501 ast_func_string_length_1, // string-length(left)
9502 ast_func_normalize_space_0, // normalize-space()
9503 ast_func_normalize_space_1, // normalize-space(left)
9504 ast_func_translate, // translate(left, right, third)
9505 ast_func_boolean, // boolean(left)
9506 ast_func_not, // not(left)
9507 ast_func_true, // true()
9508 ast_func_false, // false()
9509 ast_func_lang, // lang(left)
9511 ast_func_number_1, // number(left)
9512 ast_func_sum, // sum(left)
9513 ast_func_floor, // floor(left)
9514 ast_func_ceiling, // ceiling(left)
9515 ast_func_round, // round(left)
9516 ast_step, // process set left with step
9517 ast_step_root, // select root node
9518
9519 ast_opt_translate_table, // translate(left, right, third) where right/third are constants
9520 ast_opt_compare_attribute // @name = 'string'
9522
9539
9552
9560
9567
9568 template <axis_t N> struct axis_to_type
9569 {
9570 static const axis_t axis;
9571 };
9572
9573 template <axis_t N> const axis_t axis_to_type<N>::axis = N;
9574
9576 {
9577 private:
9578 // node type
9579 char _type;
9581
9582 // for ast_step
9583 char _axis;
9584
9585 // for ast_step/ast_predicate/ast_filter
9586 char _test;
9587
9588 // tree node structure
9592
9593 union
9594 {
9595 // value for ast_string_constant
9596 const char_t* string;
9597 // value for ast_number_constant
9598 double number;
9599 // variable for ast_variable
9601 // node test for ast_step (node name/namespace/node type/pi target)
9602 const char_t* nodetest;
9603 // table for ast_opt_translate_table
9604 const unsigned char* table;
9606
9609
9610 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)
9611 {
9612 xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();
9613
9614 if (lt != xpath_type_node_set && rt != xpath_type_node_set)
9615 {
9616 if (lt == xpath_type_boolean || rt == xpath_type_boolean)
9617 return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));
9618 else if (lt == xpath_type_number || rt == xpath_type_number)
9619 return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));
9620 else if (lt == xpath_type_string || rt == xpath_type_string)
9621 {
9623
9624 xpath_string ls = lhs->eval_string(c, stack);
9625 xpath_string rs = rhs->eval_string(c, stack);
9626
9627 return comp(ls, rs);
9628 }
9629 }
9630 else if (lt == xpath_type_node_set && rt == xpath_type_node_set)
9631 {
9633
9636
9637 for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
9638 for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
9639 {
9641
9642 if (comp(string_value(*li, stack.result), string_value(*ri, stack.result)))
9643 return true;
9644 }
9645
9646 return false;
9647 }
9648 else
9649 {
9650 if (lt == xpath_type_node_set)
9651 {
9652 swap(lhs, rhs);
9653 swap(lt, rt);
9654 }
9655
9656 if (lt == xpath_type_boolean)
9657 return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));
9658 else if (lt == xpath_type_number)
9659 {
9661
9662 double l = lhs->eval_number(c, stack);
9664
9665 for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
9666 {
9668
9669 if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
9670 return true;
9671 }
9672
9673 return false;
9674 }
9675 else if (lt == xpath_type_string)
9676 {
9678
9679 xpath_string l = lhs->eval_string(c, stack);
9681
9682 for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
9683 {
9685
9686 if (comp(l, string_value(*ri, stack.result)))
9687 return true;
9688 }
9689
9690 return false;
9691 }
9692 }
9693
9694 assert(false && "Wrong types"); // unreachable
9695 return false;
9696 }
9697
9698 static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval)
9699 {
9700 return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any;
9701 }
9702
9703 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)
9704 {
9705 xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();
9706
9707 if (lt != xpath_type_node_set && rt != xpath_type_node_set)
9708 return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));
9709 else if (lt == xpath_type_node_set && rt == xpath_type_node_set)
9710 {
9712
9715
9716 for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
9717 {
9719
9720 double l = convert_string_to_number(string_value(*li, stack.result).c_str());
9721
9722 for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
9723 {
9724 xpath_allocator_capture crii(stack.result);
9725
9726 if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
9727 return true;
9728 }
9729 }
9730
9731 return false;
9732 }
9733 else if (lt != xpath_type_node_set && rt == xpath_type_node_set)
9734 {
9736
9737 double l = lhs->eval_number(c, stack);
9739
9740 for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
9741 {
9743
9744 if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
9745 return true;
9746 }
9747
9748 return false;
9749 }
9750 else if (lt == xpath_type_node_set && rt != xpath_type_node_set)
9751 {
9753
9755 double r = rhs->eval_number(c, stack);
9756
9757 for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
9758 {
9760
9761 if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r))
9762 return true;
9763 }
9764
9765 return false;
9766 }
9767 else
9768 {
9769 assert(false && "Wrong types"); // unreachable
9770 return false;
9771 }
9772 }
9773
9774 static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
9775 {
9776 assert(ns.size() >= first);
9777 assert(expr->rettype() != xpath_type_number);
9778
9779 size_t i = 1;
9780 size_t size = ns.size() - first;
9781
9782 xpath_node* last = ns.begin() + first;
9783
9784 // remove_if... or well, sort of
9785 for (xpath_node* it = last; it != ns.end(); ++it, ++i)
9786 {
9787 xpath_context c(*it, i, size);
9788
9789 if (expr->eval_boolean(c, stack))
9790 {
9791 *last++ = *it;
9792
9793 if (once) break;
9794 }
9795 }
9796
9797 ns.truncate(last);
9798 }
9799
9800 static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
9801 {
9802 assert(ns.size() >= first);
9803 assert(expr->rettype() == xpath_type_number);
9804
9805 size_t i = 1;
9806 size_t size = ns.size() - first;
9807
9808 xpath_node* last = ns.begin() + first;
9809
9810 // remove_if... or well, sort of
9811 for (xpath_node* it = last; it != ns.end(); ++it, ++i)
9812 {
9813 xpath_context c(*it, i, size);
9814
9815 if (expr->eval_number(c, stack) == i)
9816 {
9817 *last++ = *it;
9818
9819 if (once) break;
9820 }
9821 }
9822
9823 ns.truncate(last);
9824 }
9825
9826 static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
9827 {
9828 assert(ns.size() >= first);
9829 assert(expr->rettype() == xpath_type_number);
9830
9831 size_t size = ns.size() - first;
9832
9833 xpath_node* last = ns.begin() + first;
9834
9835 xpath_context c(xpath_node(), 1, size);
9836
9837 double er = expr->eval_number(c, stack);
9838
9839 if (er >= 1.0 && er <= size)
9840 {
9841 size_t eri = static_cast<size_t>(er);
9842
9843 if (er == eri)
9844 {
9845 xpath_node r = last[eri - 1];
9846
9847 *last++ = r;
9848 }
9849 }
9850
9851 ns.truncate(last);
9852 }
9853
9854 void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once)
9855 {
9856 if (ns.size() == first) return;
9857
9858 assert(_type == ast_filter || _type == ast_predicate);
9859
9861 apply_predicate_number_const(ns, first, _right, stack);
9862 else if (_right->rettype() == xpath_type_number)
9863 apply_predicate_number(ns, first, _right, stack, once);
9864 else
9865 apply_predicate_boolean(ns, first, _right, stack, once);
9866 }
9867
9868 void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval)
9869 {
9870 if (ns.size() == first) return;
9871
9872 bool last_once = eval_once(ns.type(), eval);
9873
9874 for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
9875 pred->apply_predicate(ns, first, stack, !pred->_next && last_once);
9876 }
9877
9878 bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc)
9879 {
9880 assert(a);
9881
9882 const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT("");
9883
9884 switch (_test)
9885 {
9886 case nodetest_name:
9887 if (strequal(name, _data.nodetest) && is_xpath_attribute(name))
9888 {
9889 ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
9890 return true;
9891 }
9892 break;
9893
9894 case nodetest_type_node:
9895 case nodetest_all:
9896 if (is_xpath_attribute(name))
9897 {
9898 ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
9899 return true;
9900 }
9901 break;
9902
9904 if (starts_with(name, _data.nodetest) && is_xpath_attribute(name))
9905 {
9906 ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
9907 return true;
9908 }
9909 break;
9910
9911 default:
9912 ;
9913 }
9914
9915 return false;
9916 }
9917
9919 {
9920 assert(n);
9921
9922 xml_node_type type = PUGI__NODETYPE(n);
9923
9924 switch (_test)
9925 {
9926 case nodetest_name:
9927 if (type == node_element && n->name && strequal(n->name, _data.nodetest))
9928 {
9929 ns.push_back(xml_node(n), alloc);
9930 return true;
9931 }
9932 break;
9933
9934 case nodetest_type_node:
9935 ns.push_back(xml_node(n), alloc);
9936 return true;
9937
9939 if (type == node_comment)
9940 {
9941 ns.push_back(xml_node(n), alloc);
9942 return true;
9943 }
9944 break;
9945
9946 case nodetest_type_text:
9947 if (type == node_pcdata || type == node_cdata)
9948 {
9949 ns.push_back(xml_node(n), alloc);
9950 return true;
9951 }
9952 break;
9953
9954 case nodetest_type_pi:
9955 if (type == node_pi)
9956 {
9957 ns.push_back(xml_node(n), alloc);
9958 return true;
9959 }
9960 break;
9961
9962 case nodetest_pi:
9963 if (type == node_pi && n->name && strequal(n->name, _data.nodetest))
9964 {
9965 ns.push_back(xml_node(n), alloc);
9966 return true;
9967 }
9968 break;
9969
9970 case nodetest_all:
9971 if (type == node_element)
9972 {
9973 ns.push_back(xml_node(n), alloc);
9974 return true;
9975 }
9976 break;
9977
9979 if (type == node_element && n->name && starts_with(n->name, _data.nodetest))
9980 {
9981 ns.push_back(xml_node(n), alloc);
9982 return true;
9983 }
9984 break;
9985
9986 default:
9987 assert(false && "Unknown axis"); // unreachable
9988 }
9989
9990 return false;
9991 }
9992
9993 template <class T> void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T)
9994 {
9995 const axis_t axis = T::axis;
9996
9997 switch (axis)
9998 {
9999 case axis_attribute:
10000 {
10001 for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute)
10002 if (step_push(ns, a, n, alloc) & once)
10003 return;
10004
10005 break;
10006 }
10007
10008 case axis_child:
10009 {
10010 for (xml_node_struct* c = n->first_child; c; c = c->next_sibling)
10011 if (step_push(ns, c, alloc) & once)
10012 return;
10013
10014 break;
10015 }
10016
10017 case axis_descendant:
10019 {
10020 if (axis == axis_descendant_or_self)
10021 if (step_push(ns, n, alloc) & once)
10022 return;
10023
10024 xml_node_struct* cur = n->first_child;
10025
10026 while (cur)
10027 {
10028 if (step_push(ns, cur, alloc) & once)
10029 return;
10030
10031 if (cur->first_child)
10032 cur = cur->first_child;
10033 else
10034 {
10035 while (!cur->next_sibling)
10036 {
10037 cur = cur->parent;
10038
10039 if (cur == n) return;
10040 }
10041
10042 cur = cur->next_sibling;
10043 }
10044 }
10045
10046 break;
10047 }
10048
10050 {
10051 for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling)
10052 if (step_push(ns, c, alloc) & once)
10053 return;
10054
10055 break;
10056 }
10057
10059 {
10060 for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c)
10061 if (step_push(ns, c, alloc) & once)
10062 return;
10063
10064 break;
10065 }
10066
10067 case axis_following:
10068 {
10069 xml_node_struct* cur = n;
10070
10071 // exit from this node so that we don't include descendants
10072 while (!cur->next_sibling)
10073 {
10074 cur = cur->parent;
10075
10076 if (!cur) return;
10077 }
10078
10079 cur = cur->next_sibling;
10080
10081 while (cur)
10082 {
10083 if (step_push(ns, cur, alloc) & once)
10084 return;
10085
10086 if (cur->first_child)
10087 cur = cur->first_child;
10088 else
10089 {
10090 while (!cur->next_sibling)
10091 {
10092 cur = cur->parent;
10093
10094 if (!cur) return;
10095 }
10096
10097 cur = cur->next_sibling;
10098 }
10099 }
10100
10101 break;
10102 }
10103
10104 case axis_preceding:
10105 {
10106 xml_node_struct* cur = n;
10107
10108 // exit from this node so that we don't include descendants
10109 while (!cur->prev_sibling_c->next_sibling)
10110 {
10111 cur = cur->parent;
10112
10113 if (!cur) return;
10114 }
10115
10116 cur = cur->prev_sibling_c;
10117
10118 while (cur)
10119 {
10120 if (cur->first_child)
10121 cur = cur->first_child->prev_sibling_c;
10122 else
10123 {
10124 // leaf node, can't be ancestor
10125 if (step_push(ns, cur, alloc) & once)
10126 return;
10127
10128 while (!cur->prev_sibling_c->next_sibling)
10129 {
10130 cur = cur->parent;
10131
10132 if (!cur) return;
10133
10134 if (!node_is_ancestor(cur, n))
10135 if (step_push(ns, cur, alloc) & once)
10136 return;
10137 }
10138
10139 cur = cur->prev_sibling_c;
10140 }
10141 }
10142
10143 break;
10144 }
10145
10146 case axis_ancestor:
10148 {
10149 if (axis == axis_ancestor_or_self)
10150 if (step_push(ns, n, alloc) & once)
10151 return;
10152
10153 xml_node_struct* cur = n->parent;
10154
10155 while (cur)
10156 {
10157 if (step_push(ns, cur, alloc) & once)
10158 return;
10159
10160 cur = cur->parent;
10161 }
10162
10163 break;
10164 }
10165
10166 case axis_self:
10167 {
10168 step_push(ns, n, alloc);
10169
10170 break;
10171 }
10172
10173 case axis_parent:
10174 {
10175 if (n->parent)
10176 step_push(ns, n->parent, alloc);
10177
10178 break;
10179 }
10180
10181 default:
10182 assert(false && "Unimplemented axis"); // unreachable
10183 }
10184 }
10185
10186 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)
10187 {
10188 const axis_t axis = T::axis;
10189
10190 switch (axis)
10191 {
10192 case axis_ancestor:
10194 {
10195 if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test
10196 if (step_push(ns, a, p, alloc) & once)
10197 return;
10198
10199 xml_node_struct* cur = p;
10200
10201 while (cur)
10202 {
10203 if (step_push(ns, cur, alloc) & once)
10204 return;
10205
10206 cur = cur->parent;
10207 }
10208
10209 break;
10210 }
10211
10213 case axis_self:
10214 {
10215 if (_test == nodetest_type_node) // reject attributes based on principal node type test
10216 step_push(ns, a, p, alloc);
10217
10218 break;
10219 }
10220
10221 case axis_following:
10222 {
10223 xml_node_struct* cur = p;
10224
10225 while (cur)
10226 {
10227 if (cur->first_child)
10228 cur = cur->first_child;
10229 else
10230 {
10231 while (!cur->next_sibling)
10232 {
10233 cur = cur->parent;
10234
10235 if (!cur) return;
10236 }
10237
10238 cur = cur->next_sibling;
10239 }
10240
10241 if (step_push(ns, cur, alloc) & once)
10242 return;
10243 }
10244
10245 break;
10246 }
10247
10248 case axis_parent:
10249 {
10250 step_push(ns, p, alloc);
10251
10252 break;
10253 }
10254
10255 case axis_preceding:
10256 {
10257 // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding
10258 step_fill(ns, p, alloc, once, v);
10259 break;
10260 }
10261
10262 default:
10263 assert(false && "Unimplemented axis"); // unreachable
10264 }
10265 }
10266
10267 template <class T> void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v)
10268 {
10269 const axis_t axis = T::axis;
10270 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);
10271
10272 if (xn.node())
10273 step_fill(ns, xn.node().internal_object(), alloc, once, v);
10274 else if (axis_has_attributes && xn.attribute() && xn.parent())
10275 step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v);
10276 }
10277
10278 template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v)
10279 {
10280 const axis_t axis = T::axis;
10281 const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);
10282 const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;
10283
10284 bool once =
10285 (axis == axis_attribute && _test == nodetest_name) ||
10286 (!_right && eval_once(axis_type, eval)) ||
10288
10290 ns.set_type(axis_type);
10291
10292 if (_left)
10293 {
10295
10296 // self axis preserves the original order
10297 if (axis == axis_self) ns.set_type(s.type());
10298
10299 for (const xpath_node* it = s.begin(); it != s.end(); ++it)
10300 {
10301 size_t size = ns.size();
10302
10303 // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes
10304 if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);
10305
10306 step_fill(ns, *it, stack.result, once, v);
10307 if (_right) apply_predicates(ns, size, stack, eval);
10308 }
10309 }
10310 else
10311 {
10312 step_fill(ns, c.n, stack.result, once, v);
10313 if (_right) apply_predicates(ns, 0, stack, eval);
10314 }
10315
10316 // child, attribute and self axes always generate unique set of nodes
10317 // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice
10318 if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted)
10319 ns.remove_duplicates();
10320
10321 return ns;
10322 }
10323
10324 public:
10325 xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value):
10326 _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
10327 {
10328 assert(type == ast_string_constant);
10329 _data.string = value;
10330 }
10331
10332 xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value):
10333 _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
10334 {
10335 assert(type == ast_number_constant);
10336 _data.number = value;
10337 }
10338
10339 xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value):
10340 _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
10341 {
10342 assert(type == ast_variable);
10343 _data.variable = value;
10344 }
10345
10346 xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0):
10347 _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0)
10348 {
10349 }
10350
10351 xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents):
10352 _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)
10353 {
10354 assert(type == ast_step);
10355 _data.nodetest = contents;
10356 }
10357
10359 _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast<char>(test)), _left(left), _right(right), _next(0)
10360 {
10361 assert(type == ast_filter || type == ast_predicate);
10362 }
10363
10365 {
10366 _next = value;
10367 }
10368
10370 {
10371 _right = value;
10372 }
10373
10374 bool eval_boolean(const xpath_context& c, const xpath_stack& stack)
10375 {
10376 switch (_type)
10377 {
10378 case ast_op_or:
10379 return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack);
10380
10381 case ast_op_and:
10382 return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack);
10383
10384 case ast_op_equal:
10385 return compare_eq(_left, _right, c, stack, equal_to());
10386
10387 case ast_op_not_equal:
10388 return compare_eq(_left, _right, c, stack, not_equal_to());
10389
10390 case ast_op_less:
10391 return compare_rel(_left, _right, c, stack, less());
10392
10393 case ast_op_greater:
10394 return compare_rel(_right, _left, c, stack, less());
10395
10397 return compare_rel(_left, _right, c, stack, less_equal());
10398
10400 return compare_rel(_right, _left, c, stack, less_equal());
10401
10403 {
10405
10406 xpath_string lr = _left->eval_string(c, stack);
10407 xpath_string rr = _right->eval_string(c, stack);
10408
10409 return starts_with(lr.c_str(), rr.c_str());
10410 }
10411
10412 case ast_func_contains:
10413 {
10415
10416 xpath_string lr = _left->eval_string(c, stack);
10417 xpath_string rr = _right->eval_string(c, stack);
10418
10419 return find_substring(lr.c_str(), rr.c_str()) != 0;
10420 }
10421
10422 case ast_func_boolean:
10423 return _left->eval_boolean(c, stack);
10424
10425 case ast_func_not:
10426 return !_left->eval_boolean(c, stack);
10427
10428 case ast_func_true:
10429 return true;
10430
10431 case ast_func_false:
10432 return false;
10433
10434 case ast_func_lang:
10435 {
10436 if (c.n.attribute()) return false;
10437
10439
10440 xpath_string lang = _left->eval_string(c, stack);
10441
10442 for (xml_node n = c.n.node(); n; n = n.parent())
10443 {
10444 xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang"));
10445
10446 if (a)
10447 {
10448 const char_t* value = a.value();
10449
10450 // strnicmp / strncasecmp is not portable
10451 for (const char_t* lit = lang.c_str(); *lit; ++lit)
10452 {
10453 if (tolower_ascii(*lit) != tolower_ascii(*value)) return false;
10454 ++value;
10455 }
10456
10457 return *value == 0 || *value == '-';
10458 }
10459 }
10460
10461 return false;
10462 }
10463
10465 {
10466 const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string();
10467
10468 xml_attribute attr = c.n.node().attribute(_left->_data.nodetest);
10469
10470 return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name());
10471 }
10472
10473 case ast_variable:
10474 {
10475 assert(_rettype == _data.variable->type());
10476
10477 if (_rettype == xpath_type_boolean)
10478 return _data.variable->get_boolean();
10479 }
10480
10481 // fallthrough
10482 default:
10483 {
10484 switch (_rettype)
10485 {
10486 case xpath_type_number:
10487 return convert_number_to_boolean(eval_number(c, stack));
10488
10489 case xpath_type_string:
10490 {
10492
10493 return !eval_string(c, stack).empty();
10494 }
10495
10496 case xpath_type_node_set:
10497 {
10499
10500 return !eval_node_set(c, stack, nodeset_eval_any).empty();
10501 }
10502
10503 default:
10504 assert(false && "Wrong expression for return type boolean"); // unreachable
10505 return false;
10506 }
10507 }
10508 }
10509 }
10510
10511 double eval_number(const xpath_context& c, const xpath_stack& stack)
10512 {
10513 switch (_type)
10514 {
10515 case ast_op_add:
10516 return _left->eval_number(c, stack) + _right->eval_number(c, stack);
10517
10518 case ast_op_subtract:
10519 return _left->eval_number(c, stack) - _right->eval_number(c, stack);
10520
10521 case ast_op_multiply:
10522 return _left->eval_number(c, stack) * _right->eval_number(c, stack);
10523
10524 case ast_op_divide:
10525 return _left->eval_number(c, stack) / _right->eval_number(c, stack);
10526
10527 case ast_op_mod:
10528 return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack));
10529
10530 case ast_op_negate:
10531 return -_left->eval_number(c, stack);
10532
10534 return _data.number;
10535
10536 case ast_func_last:
10537 return static_cast<double>(c.size);
10538
10539 case ast_func_position:
10540 return static_cast<double>(c.position);
10541
10542 case ast_func_count:
10543 {
10545
10546 return static_cast<double>(_left->eval_node_set(c, stack, nodeset_eval_all).size());
10547 }
10548
10550 {
10552
10553 return static_cast<double>(string_value(c.n, stack.result).length());
10554 }
10555
10557 {
10559
10560 return static_cast<double>(_left->eval_string(c, stack).length());
10561 }
10562
10563 case ast_func_number_0:
10564 {
10566
10568 }
10569
10570 case ast_func_number_1:
10571 return _left->eval_number(c, stack);
10572
10573 case ast_func_sum:
10574 {
10576
10577 double r = 0;
10578
10580
10581 for (const xpath_node* it = ns.begin(); it != ns.end(); ++it)
10582 {
10584
10586 }
10587
10588 return r;
10589 }
10590
10591 case ast_func_floor:
10592 {
10593 double r = _left->eval_number(c, stack);
10594
10595 return r == r ? floor(r) : r;
10596 }
10597
10598 case ast_func_ceiling:
10599 {
10600 double r = _left->eval_number(c, stack);
10601
10602 return r == r ? ceil(r) : r;
10603 }
10604
10605 case ast_func_round:
10606 return round_nearest_nzero(_left->eval_number(c, stack));
10607
10608 case ast_variable:
10609 {
10610 assert(_rettype == _data.variable->type());
10611
10612 if (_rettype == xpath_type_number)
10613 return _data.variable->get_number();
10614 }
10615
10616 // fallthrough
10617 default:
10618 {
10619 switch (_rettype)
10620 {
10621 case xpath_type_boolean:
10622 return eval_boolean(c, stack) ? 1 : 0;
10623
10624 case xpath_type_string:
10625 {
10627
10628 return convert_string_to_number(eval_string(c, stack).c_str());
10629 }
10630
10631 case xpath_type_node_set:
10632 {
10634
10635 return convert_string_to_number(eval_string(c, stack).c_str());
10636 }
10637
10638 default:
10639 assert(false && "Wrong expression for return type number"); // unreachable
10640 return 0;
10641 }
10642
10643 }
10644 }
10645 }
10646
10648 {
10649 assert(_type == ast_func_concat);
10650
10651 xpath_allocator_capture ct(stack.temp);
10652
10653 // count the string number
10654 size_t count = 1;
10655 for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++;
10656
10657 // allocate a buffer for temporary string objects
10658 xpath_string* buffer = static_cast<xpath_string*>(stack.temp->allocate(count * sizeof(xpath_string)));
10659 if (!buffer) return xpath_string();
10660
10661 // evaluate all strings to temporary stack
10662 xpath_stack swapped_stack = {stack.temp, stack.result};
10663
10664 buffer[0] = _left->eval_string(c, swapped_stack);
10665
10666 size_t pos = 1;
10667 for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack);
10668 assert(pos == count);
10669
10670 // get total length
10671 size_t length = 0;
10672 for (size_t i = 0; i < count; ++i) length += buffer[i].length();
10673
10674 // create final string
10675 char_t* result = static_cast<char_t*>(stack.result->allocate((length + 1) * sizeof(char_t)));
10676 if (!result) return xpath_string();
10677
10678 char_t* ri = result;
10679
10680 for (size_t j = 0; j < count; ++j)
10681 for (const char_t* bi = buffer[j].c_str(); *bi; ++bi)
10682 *ri++ = *bi;
10683
10684 *ri = 0;
10685
10686 return xpath_string::from_heap_preallocated(result, ri);
10687 }
10688
10690 {
10691 switch (_type)
10692 {
10694 return xpath_string::from_const(_data.string);
10695
10697 {
10698 xpath_node na = c.n;
10699
10701 }
10702
10704 {
10706
10708 xpath_node na = ns.first();
10709
10711 }
10712
10713 case ast_func_name_0:
10714 {
10715 xpath_node na = c.n;
10716
10718 }
10719
10720 case ast_func_name_1:
10721 {
10723
10725 xpath_node na = ns.first();
10726
10728 }
10729
10731 {
10732 xpath_node na = c.n;
10733
10735 }
10736
10738 {
10740
10742 xpath_node na = ns.first();
10743
10745 }
10746
10747 case ast_func_string_0:
10748 return string_value(c.n, stack.result);
10749
10750 case ast_func_string_1:
10751 return _left->eval_string(c, stack);
10752
10753 case ast_func_concat:
10754 return eval_string_concat(c, stack);
10755
10757 {
10759
10760 xpath_stack swapped_stack = {stack.temp, stack.result};
10761
10762 xpath_string s = _left->eval_string(c, swapped_stack);
10763 xpath_string p = _right->eval_string(c, swapped_stack);
10764
10765 const char_t* pos = find_substring(s.c_str(), p.c_str());
10766
10767 return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string();
10768 }
10769
10771 {
10773
10774 xpath_stack swapped_stack = {stack.temp, stack.result};
10775
10776 xpath_string s = _left->eval_string(c, swapped_stack);
10777 xpath_string p = _right->eval_string(c, swapped_stack);
10778
10779 const char_t* pos = find_substring(s.c_str(), p.c_str());
10780 if (!pos) return xpath_string();
10781
10782 const char_t* rbegin = pos + p.length();
10783 const char_t* rend = s.c_str() + s.length();
10784
10785 return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);
10786 }
10787
10789 {
10791
10792 xpath_stack swapped_stack = {stack.temp, stack.result};
10793
10794 xpath_string s = _left->eval_string(c, swapped_stack);
10795 size_t s_length = s.length();
10796
10797 double first = round_nearest(_right->eval_number(c, stack));
10798
10799 if (is_nan(first)) return xpath_string(); // NaN
10800 else if (first >= s_length + 1) return xpath_string();
10801
10802 size_t pos = first < 1 ? 1 : static_cast<size_t>(first);
10803 assert(1 <= pos && pos <= s_length + 1);
10804
10805 const char_t* rbegin = s.c_str() + (pos - 1);
10806 const char_t* rend = s.c_str() + s.length();
10807
10808 return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);
10809 }
10810
10812 {
10814
10815 xpath_stack swapped_stack = {stack.temp, stack.result};
10816
10817 xpath_string s = _left->eval_string(c, swapped_stack);
10818 size_t s_length = s.length();
10819
10820 double first = round_nearest(_right->eval_number(c, stack));
10821 double last = first + round_nearest(_right->_next->eval_number(c, stack));
10822
10823 if (is_nan(first) || is_nan(last)) return xpath_string();
10824 else if (first >= s_length + 1) return xpath_string();
10825 else if (first >= last) return xpath_string();
10826 else if (last < 1) return xpath_string();
10827
10828 size_t pos = first < 1 ? 1 : static_cast<size_t>(first);
10829 size_t end = last >= s_length + 1 ? s_length + 1 : static_cast<size_t>(last);
10830
10831 assert(1 <= pos && pos <= end && end <= s_length + 1);
10832 const char_t* rbegin = s.c_str() + (pos - 1);
10833 const char_t* rend = s.c_str() + (end - 1);
10834
10835 return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result);
10836 }
10837
10839 {
10840 xpath_string s = string_value(c.n, stack.result);
10841
10842 char_t* begin = s.data(stack.result);
10843 if (!begin) return xpath_string();
10844
10845 char_t* end = normalize_space(begin);
10846
10848 }
10849
10851 {
10852 xpath_string s = _left->eval_string(c, stack);
10853
10854 char_t* begin = s.data(stack.result);
10855 if (!begin) return xpath_string();
10856
10857 char_t* end = normalize_space(begin);
10858
10860 }
10861
10862 case ast_func_translate:
10863 {
10865
10866 xpath_stack swapped_stack = {stack.temp, stack.result};
10867
10868 xpath_string s = _left->eval_string(c, stack);
10869 xpath_string from = _right->eval_string(c, swapped_stack);
10870 xpath_string to = _right->_next->eval_string(c, swapped_stack);
10871
10872 char_t* begin = s.data(stack.result);
10873 if (!begin) return xpath_string();
10874
10875 char_t* end = translate(begin, from.c_str(), to.c_str(), to.length());
10876
10878 }
10879
10881 {
10882 xpath_string s = _left->eval_string(c, stack);
10883
10884 char_t* begin = s.data(stack.result);
10885 if (!begin) return xpath_string();
10886
10887 char_t* end = translate_table(begin, _data.table);
10888
10890 }
10891
10892 case ast_variable:
10893 {
10894 assert(_rettype == _data.variable->type());
10895
10896 if (_rettype == xpath_type_string)
10897 return xpath_string::from_const(_data.variable->get_string());
10898 }
10899
10900 // fallthrough
10901 default:
10902 {
10903 switch (_rettype)
10904 {
10905 case xpath_type_boolean:
10906 return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
10907
10908 case xpath_type_number:
10909 return convert_number_to_string(eval_number(c, stack), stack.result);
10910
10911 case xpath_type_node_set:
10912 {
10914
10915 xpath_stack swapped_stack = {stack.temp, stack.result};
10916
10917 xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first);
10918 return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);
10919 }
10920
10921 default:
10922 assert(false && "Wrong expression for return type string"); // unreachable
10923 return xpath_string();
10924 }
10925 }
10926 }
10927 }
10928
10930 {
10931 switch (_type)
10932 {
10933 case ast_op_union:
10934 {
10936
10937 xpath_stack swapped_stack = {stack.temp, stack.result};
10938
10939 xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval);
10940 xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval);
10941
10942 // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother
10943 rs.set_type(xpath_node_set::type_unsorted);
10944
10945 rs.append(ls.begin(), ls.end(), stack.result);
10946 rs.remove_duplicates();
10947
10948 return rs;
10949 }
10950
10951 case ast_filter:
10952 {
10954
10955 // either expression is a number or it contains position() call; sort by document order
10956 if (_test != predicate_posinv) set.sort_do();
10957
10958 bool once = eval_once(set.type(), eval);
10959
10960 apply_predicate(set, 0, stack, once);
10961
10962 return set;
10963 }
10964
10965 case ast_func_id:
10966 return xpath_node_set_raw();
10967
10968 case ast_step:
10969 {
10970 switch (_axis)
10971 {
10972 case axis_ancestor:
10973 return step_do(c, stack, eval, axis_to_type<axis_ancestor>());
10974
10976 return step_do(c, stack, eval, axis_to_type<axis_ancestor_or_self>());
10977
10978 case axis_attribute:
10979 return step_do(c, stack, eval, axis_to_type<axis_attribute>());
10980
10981 case axis_child:
10982 return step_do(c, stack, eval, axis_to_type<axis_child>());
10983
10984 case axis_descendant:
10985 return step_do(c, stack, eval, axis_to_type<axis_descendant>());
10986
10988 return step_do(c, stack, eval, axis_to_type<axis_descendant_or_self>());
10989
10990 case axis_following:
10991 return step_do(c, stack, eval, axis_to_type<axis_following>());
10992
10994 return step_do(c, stack, eval, axis_to_type<axis_following_sibling>());
10995
10996 case axis_namespace:
10997 // namespaced axis is not supported
10998 return xpath_node_set_raw();
10999
11000 case axis_parent:
11001 return step_do(c, stack, eval, axis_to_type<axis_parent>());
11002
11003 case axis_preceding:
11004 return step_do(c, stack, eval, axis_to_type<axis_preceding>());
11005
11007 return step_do(c, stack, eval, axis_to_type<axis_preceding_sibling>());
11008
11009 case axis_self:
11010 return step_do(c, stack, eval, axis_to_type<axis_self>());
11011
11012 default:
11013 assert(false && "Unknown axis"); // unreachable
11014 return xpath_node_set_raw();
11015 }
11016 }
11017
11018 case ast_step_root:
11019 {
11020 assert(!_right); // root step can't have any predicates
11021
11023
11024 ns.set_type(xpath_node_set::type_sorted);
11025
11026 if (c.n.node()) ns.push_back(c.n.node().root(), stack.result);
11027 else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result);
11028
11029 return ns;
11030 }
11031
11032 case ast_variable:
11033 {
11034 assert(_rettype == _data.variable->type());
11035
11036 if (_rettype == xpath_type_node_set)
11037 {
11038 const xpath_node_set& s = _data.variable->get_node_set();
11039
11041
11042 ns.set_type(s.type());
11043 ns.append(s.begin(), s.end(), stack.result);
11044
11045 return ns;
11046 }
11047 }
11048
11049 // fallthrough
11050 default:
11051 assert(false && "Wrong expression for return type node set"); // unreachable
11052 return xpath_node_set_raw();
11053 }
11054 }
11055
11057 {
11058 if (_left)
11059 _left->optimize(alloc);
11060
11061 if (_right)
11062 _right->optimize(alloc);
11063
11064 if (_next)
11065 _next->optimize(alloc);
11066
11067 optimize_self(alloc);
11068 }
11069
11071 {
11072 // Rewrite [position()=expr] with [expr]
11073 // Note that this step has to go before classification to recognize [position()=1]
11074 if ((_type == ast_filter || _type == ast_predicate) &&
11075 _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number)
11076 {
11077 _right = _right->_right;
11078 }
11079
11080 // Classify filter/predicate ops to perform various optimizations during evaluation
11081 if (_type == ast_filter || _type == ast_predicate)
11082 {
11083 assert(_test == predicate_default);
11084
11087 else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last))
11089 else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr())
11091 }
11092
11093 // Rewrite descendant-or-self::node()/child::foo with descendant::foo
11094 // The former is a full form of //foo, the latter is much faster since it executes the node test immediately
11095 // Do a similar kind of rewrite for self/descendant/descendant-or-self axes
11096 // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1])
11100 {
11103 else
11105
11106 _left = _left->_left;
11107 }
11108
11109 // Use optimized lookup table implementation for translate() with constant arguments
11111 {
11113
11114 if (table)
11115 {
11117 _data.table = table;
11118 }
11119 }
11120
11121 // Use optimized path for @attr = 'value' or @attr = $value
11122 if (_type == ast_op_equal &&
11124 (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string)))
11125 {
11127 }
11128 }
11129
11130 bool is_posinv_expr() const
11131 {
11132 switch (_type)
11133 {
11134 case ast_func_position:
11135 case ast_func_last:
11136 return false;
11137
11140 case ast_variable:
11141 return true;
11142
11143 case ast_step:
11144 case ast_step_root:
11145 return true;
11146
11147 case ast_predicate:
11148 case ast_filter:
11149 return true;
11150
11151 default:
11152 if (_left && !_left->is_posinv_expr()) return false;
11153
11154 for (xpath_ast_node* n = _right; n; n = n->_next)
11155 if (!n->is_posinv_expr()) return false;
11156
11157 return true;
11158 }
11159 }
11160
11161 bool is_posinv_step() const
11162 {
11163 assert(_type == ast_step);
11164
11165 for (xpath_ast_node* n = _right; n; n = n->_next)
11166 {
11167 assert(n->_type == ast_predicate);
11168
11169 if (n->_test != predicate_posinv)
11170 return false;
11171 }
11172
11173 return true;
11174 }
11175
11176 xpath_value_type rettype() const
11177 {
11178 return static_cast<xpath_value_type>(_rettype);
11179 }
11180 };
11181
11183 {
11186
11187 const char_t* _query;
11188 xpath_variable_set* _variables;
11189
11190 xpath_parse_result* _result;
11191
11192 char_t _scratch[32];
11193
11194 xpath_ast_node* error(const char* message)
11195 {
11196 _result->error = message;
11197 _result->offset = _lexer.current_pos() - _query;
11198
11199 return 0;
11200 }
11201
11203 {
11204 assert(_alloc->_error);
11205 *_alloc->_error = true;
11206
11207 return 0;
11208 }
11209
11211 {
11212 return _alloc->allocate(sizeof(xpath_ast_node));
11213 }
11214
11215 xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value)
11216 {
11217 void* memory = alloc_node();
11218 return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
11219 }
11220
11221 xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value)
11222 {
11223 void* memory = alloc_node();
11224 return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
11225 }
11226
11227 xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value)
11228 {
11229 void* memory = alloc_node();
11230 return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
11231 }
11232
11233 xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0)
11234 {
11235 void* memory = alloc_node();
11236 return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0;
11237 }
11238
11239 xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents)
11240 {
11241 void* memory = alloc_node();
11242 return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0;
11243 }
11244
11246 {
11247 void* memory = alloc_node();
11248 return memory ? new (memory) xpath_ast_node(type, left, right, test) : 0;
11249 }
11250
11251 const char_t* alloc_string(const xpath_lexer_string& value)
11252 {
11253 if (!value.begin)
11254 return PUGIXML_TEXT("");
11255
11256 size_t length = static_cast<size_t>(value.end - value.begin);
11257
11258 char_t* c = static_cast<char_t*>(_alloc->allocate((length + 1) * sizeof(char_t)));
11259 if (!c) return 0;
11260
11261 memcpy(c, value.begin, length * sizeof(char_t));
11262 c[length] = 0;
11263
11264 return c;
11265 }
11266
11268 {
11269 switch (name.begin[0])
11270 {
11271 case 'b':
11272 if (name == PUGIXML_TEXT("boolean") && argc == 1)
11273 return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]);
11274
11275 break;
11276
11277 case 'c':
11278 if (name == PUGIXML_TEXT("count") && argc == 1)
11279 {
11280 if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
11281 return alloc_node(ast_func_count, xpath_type_number, args[0]);
11282 }
11283 else if (name == PUGIXML_TEXT("contains") && argc == 2)
11284 return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]);
11285 else if (name == PUGIXML_TEXT("concat") && argc >= 2)
11286 return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]);
11287 else if (name == PUGIXML_TEXT("ceiling") && argc == 1)
11288 return alloc_node(ast_func_ceiling, xpath_type_number, args[0]);
11289
11290 break;
11291
11292 case 'f':
11293 if (name == PUGIXML_TEXT("false") && argc == 0)
11294 return alloc_node(ast_func_false, xpath_type_boolean);
11295 else if (name == PUGIXML_TEXT("floor") && argc == 1)
11296 return alloc_node(ast_func_floor, xpath_type_number, args[0]);
11297
11298 break;
11299
11300 case 'i':
11301 if (name == PUGIXML_TEXT("id") && argc == 1)
11302 return alloc_node(ast_func_id, xpath_type_node_set, args[0]);
11303
11304 break;
11305
11306 case 'l':
11307 if (name == PUGIXML_TEXT("last") && argc == 0)
11308 return alloc_node(ast_func_last, xpath_type_number);
11309 else if (name == PUGIXML_TEXT("lang") && argc == 1)
11310 return alloc_node(ast_func_lang, xpath_type_boolean, args[0]);
11311 else if (name == PUGIXML_TEXT("local-name") && argc <= 1)
11312 {
11313 if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
11314 return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]);
11315 }
11316
11317 break;
11318
11319 case 'n':
11320 if (name == PUGIXML_TEXT("name") && argc <= 1)
11321 {
11322 if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
11323 return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]);
11324 }
11325 else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1)
11326 {
11327 if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
11328 return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]);
11329 }
11330 else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1)
11331 return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]);
11332 else if (name == PUGIXML_TEXT("not") && argc == 1)
11333 return alloc_node(ast_func_not, xpath_type_boolean, args[0]);
11334 else if (name == PUGIXML_TEXT("number") && argc <= 1)
11335 return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]);
11336
11337 break;
11338
11339 case 'p':
11340 if (name == PUGIXML_TEXT("position") && argc == 0)
11341 return alloc_node(ast_func_position, xpath_type_number);
11342
11343 break;
11344
11345 case 'r':
11346 if (name == PUGIXML_TEXT("round") && argc == 1)
11347 return alloc_node(ast_func_round, xpath_type_number, args[0]);
11348
11349 break;
11350
11351 case 's':
11352 if (name == PUGIXML_TEXT("string") && argc <= 1)
11353 return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);
11354 else if (name == PUGIXML_TEXT("string-length") && argc <= 1)
11355 return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]);
11356 else if (name == PUGIXML_TEXT("starts-with") && argc == 2)
11357 return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);
11358 else if (name == PUGIXML_TEXT("substring-before") && argc == 2)
11359 return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]);
11360 else if (name == PUGIXML_TEXT("substring-after") && argc == 2)
11361 return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]);
11362 else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3))
11363 return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]);
11364 else if (name == PUGIXML_TEXT("sum") && argc == 1)
11365 {
11366 if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
11367 return alloc_node(ast_func_sum, xpath_type_number, args[0]);
11368 }
11369
11370 break;
11371
11372 case 't':
11373 if (name == PUGIXML_TEXT("translate") && argc == 3)
11374 return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]);
11375 else if (name == PUGIXML_TEXT("true") && argc == 0)
11376 return alloc_node(ast_func_true, xpath_type_boolean);
11377
11378 break;
11379
11380 default:
11381 break;
11382 }
11383
11384 return error("Unrecognized function or wrong parameter count");
11385 }
11386
11387 axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified)
11388 {
11389 specified = true;
11390
11391 switch (name.begin[0])
11392 {
11393 case 'a':
11394 if (name == PUGIXML_TEXT("ancestor"))
11395 return axis_ancestor;
11396 else if (name == PUGIXML_TEXT("ancestor-or-self"))
11397 return axis_ancestor_or_self;
11398 else if (name == PUGIXML_TEXT("attribute"))
11399 return axis_attribute;
11400
11401 break;
11402
11403 case 'c':
11404 if (name == PUGIXML_TEXT("child"))
11405 return axis_child;
11406
11407 break;
11408
11409 case 'd':
11410 if (name == PUGIXML_TEXT("descendant"))
11411 return axis_descendant;
11412 else if (name == PUGIXML_TEXT("descendant-or-self"))
11414
11415 break;
11416
11417 case 'f':
11418 if (name == PUGIXML_TEXT("following"))
11419 return axis_following;
11420 else if (name == PUGIXML_TEXT("following-sibling"))
11422
11423 break;
11424
11425 case 'n':
11426 if (name == PUGIXML_TEXT("namespace"))
11427 return axis_namespace;
11428
11429 break;
11430
11431 case 'p':
11432 if (name == PUGIXML_TEXT("parent"))
11433 return axis_parent;
11434 else if (name == PUGIXML_TEXT("preceding"))
11435 return axis_preceding;
11436 else if (name == PUGIXML_TEXT("preceding-sibling"))
11438
11439 break;
11440
11441 case 's':
11442 if (name == PUGIXML_TEXT("self"))
11443 return axis_self;
11444
11445 break;
11446
11447 default:
11448 break;
11449 }
11450
11451 specified = false;
11452 return axis_child;
11453 }
11454
11456 {
11457 switch (name.begin[0])
11458 {
11459 case 'c':
11460 if (name == PUGIXML_TEXT("comment"))
11461 return nodetest_type_comment;
11462
11463 break;
11464
11465 case 'n':
11466 if (name == PUGIXML_TEXT("node"))
11467 return nodetest_type_node;
11468
11469 break;
11470
11471 case 'p':
11472 if (name == PUGIXML_TEXT("processing-instruction"))
11473 return nodetest_type_pi;
11474
11475 break;
11476
11477 case 't':
11478 if (name == PUGIXML_TEXT("text"))
11479 return nodetest_type_text;
11480
11481 break;
11482
11483 default:
11484 break;
11485 }
11486
11487 return nodetest_none;
11488 }
11489
11490 // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
11492 {
11493 switch (_lexer.current())
11494 {
11495 case lex_var_ref:
11496 {
11498
11499 if (!_variables)
11500 return error("Unknown variable: variable set is not provided");
11501
11502 xpath_variable* var = 0;
11503 if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var))
11504 return error_oom();
11505
11506 if (!var)
11507 return error("Unknown variable: variable set does not contain the given name");
11508
11509 _lexer.next();
11510
11511 return alloc_node(ast_variable, var->type(), var);
11512 }
11513
11514 case lex_open_brace:
11515 {
11516 _lexer.next();
11517
11519 if (!n) return 0;
11520
11522 return error("Expected ')' to match an opening '('");
11523
11524 _lexer.next();
11525
11526 return n;
11527 }
11528
11529 case lex_quoted_string:
11530 {
11531 const char_t* value = alloc_string(_lexer.contents());
11532 if (!value) return 0;
11533
11534 _lexer.next();
11535
11536 return alloc_node(ast_string_constant, xpath_type_string, value);
11537 }
11538
11539 case lex_number:
11540 {
11541 double value = 0;
11542
11544 return error_oom();
11545
11546 _lexer.next();
11547
11548 return alloc_node(ast_number_constant, xpath_type_number, value);
11549 }
11550
11551 case lex_string:
11552 {
11553 xpath_ast_node* args[2] = {0};
11554 size_t argc = 0;
11555
11556 xpath_lexer_string function = _lexer.contents();
11557 _lexer.next();
11558
11559 xpath_ast_node* last_arg = 0;
11560
11561 if (_lexer.current() != lex_open_brace)
11562 return error("Unrecognized function call");
11563 _lexer.next();
11564
11565 while (_lexer.current() != lex_close_brace)
11566 {
11567 if (argc > 0)
11568 {
11569 if (_lexer.current() != lex_comma)
11570 return error("No comma between function arguments");
11571 _lexer.next();
11572 }
11573
11575 if (!n) return 0;
11576
11577 if (argc < 2) args[argc] = n;
11578 else last_arg->set_next(n);
11579
11580 argc++;
11581 last_arg = n;
11582 }
11583
11584 _lexer.next();
11585
11586 return parse_function(function, argc, args);
11587 }
11588
11589 default:
11590 return error("Unrecognizable primary expression");
11591 }
11592 }
11593
11594 // FilterExpr ::= PrimaryExpr | FilterExpr Predicate
11595 // Predicate ::= '[' PredicateExpr ']'
11596 // PredicateExpr ::= Expr
11598 {
11600 if (!n) return 0;
11601
11603 {
11604 _lexer.next();
11605
11606 if (n->rettype() != xpath_type_node_set)
11607 return error("Predicate has to be applied to node set");
11608
11610 if (!expr) return 0;
11611
11613 if (!n) return 0;
11614
11616 return error("Expected ']' to match an opening '['");
11617
11618 _lexer.next();
11619 }
11620
11621 return n;
11622 }
11623
11624 // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
11625 // AxisSpecifier ::= AxisName '::' | '@'?
11626 // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')'
11627 // NameTest ::= '*' | NCName ':' '*' | QName
11628 // AbbreviatedStep ::= '.' | '..'
11630 {
11631 if (set && set->rettype() != xpath_type_node_set)
11632 return error("Step has to be applied to node set");
11633
11634 bool axis_specified = false;
11635 axis_t axis = axis_child; // implied child axis
11636
11638 {
11639 axis = axis_attribute;
11640 axis_specified = true;
11641
11642 _lexer.next();
11643 }
11644 else if (_lexer.current() == lex_dot)
11645 {
11646 _lexer.next();
11647
11649 return error("Predicates are not allowed after an abbreviated step");
11650
11652 }
11653 else if (_lexer.current() == lex_double_dot)
11654 {
11655 _lexer.next();
11656
11658 return error("Predicates are not allowed after an abbreviated step");
11659
11661 }
11662
11663 nodetest_t nt_type = nodetest_none;
11664 xpath_lexer_string nt_name;
11665
11666 if (_lexer.current() == lex_string)
11667 {
11668 // node name test
11669 nt_name = _lexer.contents();
11670 _lexer.next();
11671
11672 // was it an axis name?
11674 {
11675 // parse axis name
11676 if (axis_specified)
11677 return error("Two axis specifiers in one step");
11678
11679 axis = parse_axis_name(nt_name, axis_specified);
11680
11681 if (!axis_specified)
11682 return error("Unknown axis");
11683
11684 // read actual node test
11685 _lexer.next();
11686
11687 if (_lexer.current() == lex_multiply)
11688 {
11689 nt_type = nodetest_all;
11690 nt_name = xpath_lexer_string();
11691 _lexer.next();
11692 }
11693 else if (_lexer.current() == lex_string)
11694 {
11695 nt_name = _lexer.contents();
11696 _lexer.next();
11697 }
11698 else
11699 {
11700 return error("Unrecognized node test");
11701 }
11702 }
11703
11704 if (nt_type == nodetest_none)
11705 {
11706 // node type test or processing-instruction
11707 if (_lexer.current() == lex_open_brace)
11708 {
11709 _lexer.next();
11710
11712 {
11713 _lexer.next();
11714
11715 nt_type = parse_node_test_type(nt_name);
11716
11717 if (nt_type == nodetest_none)
11718 return error("Unrecognized node type");
11719
11720 nt_name = xpath_lexer_string();
11721 }
11722 else if (nt_name == PUGIXML_TEXT("processing-instruction"))
11723 {
11725 return error("Only literals are allowed as arguments to processing-instruction()");
11726
11727 nt_type = nodetest_pi;
11728 nt_name = _lexer.contents();
11729 _lexer.next();
11730
11732 return error("Unmatched brace near processing-instruction()");
11733 _lexer.next();
11734 }
11735 else
11736 {
11737 return error("Unmatched brace near node type test");
11738 }
11739 }
11740 // QName or NCName:*
11741 else
11742 {
11743 if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:*
11744 {
11745 nt_name.end--; // erase *
11746
11747 nt_type = nodetest_all_in_namespace;
11748 }
11749 else
11750 {
11751 nt_type = nodetest_name;
11752 }
11753 }
11754 }
11755 }
11756 else if (_lexer.current() == lex_multiply)
11757 {
11758 nt_type = nodetest_all;
11759 _lexer.next();
11760 }
11761 else
11762 {
11763 return error("Unrecognized node test");
11764 }
11765
11766 const char_t* nt_name_copy = alloc_string(nt_name);
11767 if (!nt_name_copy) return 0;
11768
11769 xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy);
11770 if (!n) return 0;
11771
11772 xpath_ast_node* last = 0;
11773
11775 {
11776 _lexer.next();
11777
11779 if (!expr) return 0;
11780
11782 if (!pred) return 0;
11783
11785 return error("Expected ']' to match an opening '['");
11786 _lexer.next();
11787
11788 if (last) last->set_next(pred);
11789 else n->set_right(pred);
11790
11791 last = pred;
11792 }
11793
11794 return n;
11795 }
11796
11797 // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
11799 {
11800 xpath_ast_node* n = parse_step(set);
11801 if (!n) return 0;
11802
11804 {
11805 lexeme_t l = _lexer.current();
11806 _lexer.next();
11807
11808 if (l == lex_double_slash)
11809 {
11811 if (!n) return 0;
11812 }
11813
11814 n = parse_step(n);
11815 if (!n) return 0;
11816 }
11817
11818 return n;
11819 }
11820
11821 // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
11822 // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
11824 {
11825 if (_lexer.current() == lex_slash)
11826 {
11827 _lexer.next();
11828
11829 xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set);
11830 if (!n) return 0;
11831
11832 // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path
11833 lexeme_t l = _lexer.current();
11834
11835 if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply)
11837 else
11838 return n;
11839 }
11840 else if (_lexer.current() == lex_double_slash)
11841 {
11842 _lexer.next();
11843
11844 xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set);
11845 if (!n) return 0;
11846
11848 if (!n) return 0;
11849
11851 }
11852
11853 // 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
11855 }
11856
11857 // PathExpr ::= LocationPath
11858 // | FilterExpr
11859 // | FilterExpr '/' RelativeLocationPath
11860 // | FilterExpr '//' RelativeLocationPath
11861 // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
11862 // UnaryExpr ::= UnionExpr | '-' UnaryExpr
11864 {
11865 // Clarification.
11866 // PathExpr begins with either LocationPath or FilterExpr.
11867 // FilterExpr begins with PrimaryExpr
11868 // PrimaryExpr begins with '$' in case of it being a variable reference,
11869 // '(' in case of it being an expression, string literal, number constant or
11870 // function call.
11874 {
11875 if (_lexer.current() == lex_string)
11876 {
11877 // This is either a function call, or not - if not, we shall proceed with location path
11878 const char_t* state = _lexer.state();
11879
11880 while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state;
11881
11882 if (*state != '(')
11883 return parse_location_path();
11884
11885 // This looks like a function call; however this still can be a node-test. Check it.
11887 return parse_location_path();
11888 }
11889
11891 if (!n) return 0;
11892
11894 {
11895 lexeme_t l = _lexer.current();
11896 _lexer.next();
11897
11898 if (l == lex_double_slash)
11899 {
11900 if (n->rettype() != xpath_type_node_set)
11901 return error("Step has to be applied to node set");
11902
11904 if (!n) return 0;
11905 }
11906
11907 // select from location path
11909 }
11910
11911 return n;
11912 }
11913 else if (_lexer.current() == lex_minus)
11914 {
11915 _lexer.next();
11916
11917 // precedence 7+ - only parses union expressions
11919 if (!n) return 0;
11920
11921 return alloc_node(ast_op_negate, xpath_type_number, n);
11922 }
11923 else
11924 {
11925 return parse_location_path();
11926 }
11927 }
11928
11930 {
11932 xpath_value_type rettype;
11934
11936 {
11937 }
11938
11939 binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_)
11940 {
11941 }
11942
11944 {
11945 switch (lexer.current())
11946 {
11947 case lex_string:
11948 if (lexer.contents() == PUGIXML_TEXT("or"))
11949 return binary_op_t(ast_op_or, xpath_type_boolean, 1);
11950 else if (lexer.contents() == PUGIXML_TEXT("and"))
11951 return binary_op_t(ast_op_and, xpath_type_boolean, 2);
11952 else if (lexer.contents() == PUGIXML_TEXT("div"))
11953 return binary_op_t(ast_op_divide, xpath_type_number, 6);
11954 else if (lexer.contents() == PUGIXML_TEXT("mod"))
11955 return binary_op_t(ast_op_mod, xpath_type_number, 6);
11956 else
11957 return binary_op_t();
11958
11959 case lex_equal:
11960 return binary_op_t(ast_op_equal, xpath_type_boolean, 3);
11961
11962 case lex_not_equal:
11963 return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3);
11964
11965 case lex_less:
11966 return binary_op_t(ast_op_less, xpath_type_boolean, 4);
11967
11968 case lex_greater:
11969 return binary_op_t(ast_op_greater, xpath_type_boolean, 4);
11970
11971 case lex_less_or_equal:
11972 return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4);
11973
11975 return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4);
11976
11977 case lex_plus:
11978 return binary_op_t(ast_op_add, xpath_type_number, 5);
11979
11980 case lex_minus:
11981 return binary_op_t(ast_op_subtract, xpath_type_number, 5);
11982
11983 case lex_multiply:
11984 return binary_op_t(ast_op_multiply, xpath_type_number, 6);
11985
11986 case lex_union:
11987 return binary_op_t(ast_op_union, xpath_type_node_set, 7);
11988
11989 default:
11990 return binary_op_t();
11991 }
11992 }
11993 };
11994
11996 {
11998
11999 while (op.asttype != ast_unknown && op.precedence >= limit)
12000 {
12001 _lexer.next();
12002
12004 if (!rhs) return 0;
12005
12007
12008 while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence)
12009 {
12010 rhs = parse_expression_rec(rhs, nextop.precedence);
12011 if (!rhs) return 0;
12012
12013 nextop = binary_op_t::parse(_lexer);
12014 }
12015
12016 if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set))
12017 return error("Union operator has to be applied to node sets");
12018
12019 lhs = alloc_node(op.asttype, op.rettype, lhs, rhs);
12020 if (!lhs) return 0;
12021
12023 }
12024
12025 return lhs;
12026 }
12027
12028 // Expr ::= OrExpr
12029 // OrExpr ::= AndExpr | OrExpr 'or' AndExpr
12030 // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
12031 // EqualityExpr ::= RelationalExpr
12032 // | EqualityExpr '=' RelationalExpr
12033 // | EqualityExpr '!=' RelationalExpr
12034 // RelationalExpr ::= AdditiveExpr
12035 // | RelationalExpr '<' AdditiveExpr
12036 // | RelationalExpr '>' AdditiveExpr
12037 // | RelationalExpr '<=' AdditiveExpr
12038 // | RelationalExpr '>=' AdditiveExpr
12039 // AdditiveExpr ::= MultiplicativeExpr
12040 // | AdditiveExpr '+' MultiplicativeExpr
12041 // | AdditiveExpr '-' MultiplicativeExpr
12042 // MultiplicativeExpr ::= UnaryExpr
12043 // | MultiplicativeExpr '*' UnaryExpr
12044 // | MultiplicativeExpr 'div' UnaryExpr
12045 // | MultiplicativeExpr 'mod' UnaryExpr
12047 {
12049 if (!n) return 0;
12050
12051 return parse_expression_rec(n, limit);
12052 }
12053
12054 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)
12055 {
12056 }
12057
12059 {
12061 if (!n) return 0;
12062
12063 // check if there are unparsed tokens left
12064 if (_lexer.current() != lex_eof)
12065 return error("Incorrect query");
12066
12067 return n;
12068 }
12069
12070 static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result)
12071 {
12072 xpath_parser parser(query, variables, alloc, result);
12073
12074 return parser.parse();
12075 }
12076 };
12077
12079 {
12081 {
12082 void* memory = xml_memory::allocate(sizeof(xpath_query_impl));
12083 if (!memory) return 0;
12084
12085 return new (memory) xpath_query_impl();
12086 }
12087
12088 static void destroy(xpath_query_impl* impl)
12089 {
12090 // free all allocated pages
12091 impl->alloc.release();
12092
12093 // free allocator memory (with the first page)
12095 }
12096
12098 {
12099 block.next = 0;
12100 block.capacity = sizeof(block.data);
12101 }
12102
12106 bool oom;
12107 };
12108
12110 {
12111 if (!impl) return 0;
12112
12113 if (impl->root->rettype() != xpath_type_node_set)
12114 {
12115 #ifdef PUGIXML_NO_EXCEPTIONS
12116 return 0;
12117 #else
12118 xpath_parse_result res;
12119 res.error = "Expression does not evaluate to node set";
12120
12121 throw xpath_exception(res);
12122 #endif
12123 }
12124
12125 return impl->root;
12126 }
12128
12129namespace pugi
12130{
12131#ifndef PUGIXML_NO_EXCEPTIONS
12133 {
12134 assert(_result.error);
12135 }
12136
12137 PUGI__FN const char* xpath_exception::what() const noexcept
12138 {
12139 return _result.error;
12140 }
12141
12143 {
12144 return _result;
12145 }
12146#endif
12147
12151
12152 PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_)
12153 {
12154 }
12155
12156 PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_)
12157 {
12158 }
12159
12161 {
12162 return _attribute ? xml_node() : _node;
12163 }
12164
12166 {
12167 return _attribute;
12168 }
12169
12171 {
12172 return _attribute ? _node : _node.parent();
12173 }
12174
12176 {
12177 }
12178
12179 PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const
12180 {
12182 }
12183
12185 {
12186 return !(_node || _attribute);
12187 }
12188
12190 {
12191 return _node == n._node && _attribute == n._attribute;
12192 }
12193
12195 {
12196 return _node != n._node || _attribute != n._attribute;
12197 }
12198
12199#ifdef __BORLANDC__
12200 PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs)
12201 {
12202 return (bool)lhs && rhs;
12203 }
12204
12205 PUGI__FN bool operator||(const xpath_node& lhs, bool rhs)
12206 {
12207 return (bool)lhs || rhs;
12208 }
12209#endif
12210
12212 {
12213 assert(begin_ <= end_);
12214
12215 size_t size_ = static_cast<size_t>(end_ - begin_);
12216
12217 if (size_ <= 1)
12218 {
12219 // deallocate old buffer
12220 if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
12221
12222 // use internal buffer
12223 if (begin_ != end_) _storage = *begin_;
12224
12225 _begin = &_storage;
12226 _end = &_storage + size_;
12227 _type = type_;
12228 }
12229 else
12230 {
12231 // make heap copy
12232 xpath_node* storage = static_cast<xpath_node*>(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));
12233
12234 if (!storage)
12235 {
12236 #ifdef PUGIXML_NO_EXCEPTIONS
12237 return;
12238 #else
12239 throw std::bad_alloc();
12240 #endif
12241 }
12242
12243 memcpy(storage, begin_, size_ * sizeof(xpath_node));
12244
12245 // deallocate old buffer
12246 if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
12247
12248 // finalize
12249 _begin = storage;
12250 _end = storage + size_;
12251 _type = type_;
12252 }
12253 }
12254
12255#ifdef PUGIXML_HAS_MOVE
12257 {
12258 _type = rhs._type;
12259 _storage = rhs._storage;
12260 _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin;
12261 _end = _begin + (rhs._end - rhs._begin);
12262
12263 rhs._type = type_unsorted;
12264 rhs._begin = &rhs._storage;
12265 rhs._end = rhs._begin;
12266 }
12267#endif
12268
12269 PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage)
12270 {
12271 }
12272
12273 PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage)
12274 {
12275 _assign(begin_, end_, type_);
12276 }
12277
12279 {
12280 if (_begin != &_storage)
12281 impl::xml_memory::deallocate(_begin);
12282 }
12283
12284 PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage)
12285 {
12286 _assign(ns._begin, ns._end, ns._type);
12287 }
12288
12290 {
12291 if (this == &ns) return *this;
12292
12293 _assign(ns._begin, ns._end, ns._type);
12294
12295 return *this;
12296 }
12297
12298#ifdef PUGIXML_HAS_MOVE
12299 PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(&_storage), _end(&_storage)
12300 {
12301 _move(rhs);
12302 }
12303
12304 PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT
12305 {
12306 if (this == &rhs) return *this;
12307
12308 if (_begin != &_storage)
12309 impl::xml_memory::deallocate(_begin);
12310
12311 _move(rhs);
12312
12313 return *this;
12314 }
12315#endif
12316
12318 {
12319 return _type;
12320 }
12321
12323 {
12324 return _end - _begin;
12325 }
12326
12328 {
12329 return _begin == _end;
12330 }
12331
12333 {
12334 assert(index < size());
12335 return _begin[index];
12336 }
12337
12342
12344 {
12345 return _end;
12346 }
12347
12349 {
12350 _type = impl::xpath_sort(_begin, _end, _type, reverse);
12351 }
12352
12354 {
12355 return impl::xpath_first(_begin, _end, _type);
12356 }
12357
12358 PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0)
12359 {
12360 }
12361
12362 PUGI__FN xpath_parse_result::operator bool() const
12363 {
12364 return error == 0;
12365 }
12366
12368 {
12369 return error ? error : "No error";
12370 }
12371
12373 {
12374 }
12375
12377 {
12378 switch (_type)
12379 {
12381 return static_cast<const impl::xpath_variable_node_set*>(this)->name;
12382
12383 case xpath_type_number:
12384 return static_cast<const impl::xpath_variable_number*>(this)->name;
12385
12386 case xpath_type_string:
12387 return static_cast<const impl::xpath_variable_string*>(this)->name;
12388
12389 case xpath_type_boolean:
12390 return static_cast<const impl::xpath_variable_boolean*>(this)->name;
12391
12392 default:
12393 assert(false && "Invalid variable type"); // unreachable
12394 return 0;
12395 }
12396 }
12397
12399 {
12400 return _type;
12401 }
12402
12404 {
12405 return (_type == xpath_type_boolean) ? static_cast<const impl::xpath_variable_boolean*>(this)->value : false;
12406 }
12407
12409 {
12410 return (_type == xpath_type_number) ? static_cast<const impl::xpath_variable_number*>(this)->value : impl::gen_nan();
12411 }
12412
12414 {
12415 const char_t* value = (_type == xpath_type_string) ? static_cast<const impl::xpath_variable_string*>(this)->value : 0;
12416 return value ? value : PUGIXML_TEXT("");
12417 }
12418
12420 {
12421 return (_type == xpath_type_node_set) ? static_cast<const impl::xpath_variable_node_set*>(this)->value : impl::dummy_node_set;
12422 }
12423
12425 {
12426 if (_type != xpath_type_boolean) return false;
12427
12428 static_cast<impl::xpath_variable_boolean*>(this)->value = value;
12429 return true;
12430 }
12431
12433 {
12434 if (_type != xpath_type_number) return false;
12435
12436 static_cast<impl::xpath_variable_number*>(this)->value = value;
12437 return true;
12438 }
12439
12441 {
12442 if (_type != xpath_type_string) return false;
12443
12444 impl::xpath_variable_string* var = static_cast<impl::xpath_variable_string*>(this);
12445
12446 // duplicate string
12447 size_t size = (impl::strlength(value) + 1) * sizeof(char_t);
12448
12449 char_t* copy = static_cast<char_t*>(impl::xml_memory::allocate(size));
12450 if (!copy) return false;
12451
12452 memcpy(copy, value, size);
12453
12454 // replace old string
12455 if (var->value) impl::xml_memory::deallocate(var->value);
12456 var->value = copy;
12457
12458 return true;
12459 }
12460
12462 {
12463 if (_type != xpath_type_node_set) return false;
12464
12465 static_cast<impl::xpath_variable_node_set*>(this)->value = value;
12466 return true;
12467 }
12468
12470 {
12471 for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
12472 _data[i] = 0;
12473 }
12474
12476 {
12477 for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
12478 _destroy(_data[i]);
12479 }
12480
12482 {
12483 for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
12484 _data[i] = 0;
12485
12486 _assign(rhs);
12487 }
12488
12490 {
12491 if (this == &rhs) return *this;
12492
12493 _assign(rhs);
12494
12495 return *this;
12496 }
12497
12498#ifdef PUGIXML_HAS_MOVE
12500 {
12501 for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
12502 {
12503 _data[i] = rhs._data[i];
12504 rhs._data[i] = 0;
12505 }
12506 }
12507
12508 PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
12509 {
12510 for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
12511 {
12512 _destroy(_data[i]);
12513
12514 _data[i] = rhs._data[i];
12515 rhs._data[i] = 0;
12516 }
12517
12518 return *this;
12519 }
12520#endif
12521
12523 {
12524 xpath_variable_set temp;
12525
12526 for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
12527 if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i]))
12528 return;
12529
12530 _swap(temp);
12531 }
12532
12534 {
12535 for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
12536 {
12537 xpath_variable* chain = _data[i];
12538
12539 _data[i] = rhs._data[i];
12540 rhs._data[i] = chain;
12541 }
12542 }
12543
12545 {
12546 const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
12547 size_t hash = impl::hash_string(name) % hash_size;
12548
12549 // look for existing variable
12550 for (xpath_variable* var = _data[hash]; var; var = var->_next)
12551 if (impl::strequal(var->name(), name))
12552 return var;
12553
12554 return 0;
12555 }
12556
12558 {
12559 xpath_variable* last = 0;
12560
12561 while (var)
12562 {
12563 // allocate storage for new variable
12564 xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name());
12565 if (!nvar) return false;
12566
12567 // link the variable to the result immediately to handle failures gracefully
12568 if (last)
12569 last->_next = nvar;
12570 else
12571 *out_result = nvar;
12572
12573 last = nvar;
12574
12575 // copy the value; this can fail due to out-of-memory conditions
12576 if (!impl::copy_xpath_variable(nvar, var)) return false;
12577
12578 var = var->_next;
12579 }
12580
12581 return true;
12582 }
12583
12585 {
12586 while (var)
12587 {
12588 xpath_variable* next = var->_next;
12589
12590 impl::delete_xpath_variable(var->_type, var);
12591
12592 var = next;
12593 }
12594 }
12595
12597 {
12598 const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
12599 size_t hash = impl::hash_string(name) % hash_size;
12600
12601 // look for existing variable
12602 for (xpath_variable* var = _data[hash]; var; var = var->_next)
12603 if (impl::strequal(var->name(), name))
12604 return var->type() == type ? var : 0;
12605
12606 // add new variable
12607 xpath_variable* result = impl::new_xpath_variable(type, name);
12608
12609 if (result)
12610 {
12611 result->_next = _data[hash];
12612
12613 _data[hash] = result;
12614 }
12615
12616 return result;
12617 }
12618
12619 PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value)
12620 {
12622 return var ? var->set(value) : false;
12623 }
12624
12625 PUGI__FN bool xpath_variable_set::set(const char_t* name, double value)
12626 {
12627 xpath_variable* var = add(name, xpath_type_number);
12628 return var ? var->set(value) : false;
12629 }
12630
12631 PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value)
12632 {
12633 xpath_variable* var = add(name, xpath_type_string);
12634 return var ? var->set(value) : false;
12635 }
12636
12638 {
12640 return var ? var->set(value) : false;
12641 }
12642
12644 {
12645 return _find(name);
12646 }
12647
12649 {
12650 return _find(name);
12651 }
12652
12654 {
12655 impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create();
12656
12657 if (!qimpl)
12658 {
12659 #ifdef PUGIXML_NO_EXCEPTIONS
12660 _result.error = "Out of memory";
12661 #else
12662 throw std::bad_alloc();
12663 #endif
12664 }
12665 else
12666 {
12667 using impl::auto_deleter; // MSVC7 workaround
12668 auto_deleter<impl::xpath_query_impl> impl(qimpl, impl::xpath_query_impl::destroy);
12669
12670 qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);
12671
12672 if (qimpl->root)
12673 {
12674 qimpl->root->optimize(&qimpl->alloc);
12675
12676 _impl = impl.release();
12677 _result.error = 0;
12678 }
12679 else
12680 {
12681 #ifdef PUGIXML_NO_EXCEPTIONS
12682 if (qimpl->oom) _result.error = "Out of memory";
12683 #else
12684 if (qimpl->oom) throw std::bad_alloc();
12685 throw xpath_exception(_result);
12686 #endif
12687 }
12688 }
12689 }
12690
12692 {
12693 }
12694
12696 {
12697 if (_impl)
12698 impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
12699 }
12700
12701#ifdef PUGIXML_HAS_MOVE
12703 {
12704 _impl = rhs._impl;
12705 _result = rhs._result;
12706 rhs._impl = 0;
12707 rhs._result = xpath_parse_result();
12708 }
12709
12710 PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT
12711 {
12712 if (this == &rhs) return *this;
12713
12714 if (_impl)
12715 impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
12716
12717 _impl = rhs._impl;
12718 _result = rhs._result;
12719 rhs._impl = 0;
12720 rhs._result = xpath_parse_result();
12721
12722 return *this;
12723 }
12724#endif
12725
12727 {
12728 if (!_impl) return xpath_type_none;
12729
12730 return static_cast<impl::xpath_query_impl*>(_impl)->root->rettype();
12731 }
12732
12734 {
12735 if (!_impl) return false;
12736
12737 impl::xpath_context c(n, 1, 1);
12738 impl::xpath_stack_data sd;
12739
12740 bool r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack);
12741
12742 if (sd.oom)
12743 {
12744 #ifdef PUGIXML_NO_EXCEPTIONS
12745 return false;
12746 #else
12747 throw std::bad_alloc();
12748 #endif
12749 }
12750
12751 return r;
12752 }
12753
12755 {
12756 if (!_impl) return impl::gen_nan();
12757
12758 impl::xpath_context c(n, 1, 1);
12759 impl::xpath_stack_data sd;
12760
12761 double r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack);
12762
12763 if (sd.oom)
12764 {
12765 #ifdef PUGIXML_NO_EXCEPTIONS
12766 return impl::gen_nan();
12767 #else
12768 throw std::bad_alloc();
12769 #endif
12770 }
12771
12772 return r;
12773 }
12774
12775#ifndef PUGIXML_NO_STL
12777 {
12778 if (!_impl) return string_t();
12779
12780 impl::xpath_context c(n, 1, 1);
12781 impl::xpath_stack_data sd;
12782
12783 impl::xpath_string r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack);
12784
12785 if (sd.oom)
12786 {
12787 #ifdef PUGIXML_NO_EXCEPTIONS
12788 return string_t();
12789 #else
12790 throw std::bad_alloc();
12791 #endif
12792 }
12793
12794 return string_t(r.c_str(), r.length());
12795 }
12796#endif
12797
12798 PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const
12799 {
12800 impl::xpath_context c(n, 1, 1);
12801 impl::xpath_stack_data sd;
12802
12803 impl::xpath_string r = _impl ? static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string();
12804
12805 if (sd.oom)
12806 {
12807 #ifdef PUGIXML_NO_EXCEPTIONS
12808 r = impl::xpath_string();
12809 #else
12810 throw std::bad_alloc();
12811 #endif
12812 }
12813
12814 size_t full_size = r.length() + 1;
12815
12816 if (capacity > 0)
12817 {
12818 size_t size = (full_size < capacity) ? full_size : capacity;
12819 assert(size > 0);
12820
12821 memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t));
12822 buffer[size - 1] = 0;
12823 }
12824
12825 return full_size;
12826 }
12827
12829 {
12830 impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
12831 if (!root) return xpath_node_set();
12832
12833 impl::xpath_context c(n, 1, 1);
12834 impl::xpath_stack_data sd;
12835
12836 impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all);
12837
12838 if (sd.oom)
12839 {
12840 #ifdef PUGIXML_NO_EXCEPTIONS
12841 return xpath_node_set();
12842 #else
12843 throw std::bad_alloc();
12844 #endif
12845 }
12846
12847 return xpath_node_set(r.begin(), r.end(), r.type());
12848 }
12849
12851 {
12852 impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
12853 if (!root) return xpath_node();
12854
12855 impl::xpath_context c(n, 1, 1);
12856 impl::xpath_stack_data sd;
12857
12858 impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first);
12859
12860 if (sd.oom)
12861 {
12862 #ifdef PUGIXML_NO_EXCEPTIONS
12863 return xpath_node();
12864 #else
12865 throw std::bad_alloc();
12866 #endif
12867 }
12868
12869 return r.first();
12870 }
12871
12873 {
12874 return _result;
12875 }
12876
12878 {
12879 }
12880
12881 PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const
12882 {
12884 }
12885
12887 {
12888 return !_impl;
12889 }
12890
12892 {
12893 xpath_query q(query, variables);
12894 return q.evaluate_node(*this);
12895 }
12896
12898 {
12899 return query.evaluate_node(*this);
12900 }
12901
12903 {
12904 xpath_query q(query, variables);
12905 return q.evaluate_node_set(*this);
12906 }
12907
12909 {
12910 return query.evaluate_node_set(*this);
12911 }
12912
12914 {
12915 xpath_query q(query, variables);
12916 return q.evaluate_node(*this);
12917 }
12918
12920 {
12921 return query.evaluate_node(*this);
12922 }
12923}
12924
12925#endif
12926
12927#ifdef __BORLANDC__
12928# pragma option pop
12929#endif
12930
12931// Intel C++ does not properly keep warning state for function templates,
12932// so popping warning state at the end of translation unit leads to warnings in the middle.
12933#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
12934# pragma warning(pop)
12935#endif
12936
12937#if defined(_MSC_VER) && defined(__c2__)
12938# pragma clang diagnostic pop
12939#endif
12940
12941#if defined(__clang__)
12942# pragma clang diagnostic pop
12943#endif
12944
12945// Undefine all local macros (makes sure we're not leaking macros in header-only mode)
12946#undef PUGI__NO_INLINE
12947#undef PUGI__UNLIKELY
12948#undef PUGI__STATIC_ASSERT
12949#undef PUGI__DMC_VOLATILE
12950#undef PUGI__UNSIGNED_OVERFLOW
12951#undef PUGI__MSVC_CRT_VERSION
12952#undef PUGI__SNPRINTF
12953#undef PUGI__NS_BEGIN
12954#undef PUGI__NS_END
12955#undef PUGI__FN
12956#undef PUGI__FN_NO_INLINE
12957#undef PUGI__GETHEADER_IMPL
12958#undef PUGI__GETPAGE_IMPL
12959#undef PUGI__GETPAGE
12960#undef PUGI__NODETYPE
12961#undef PUGI__IS_CHARTYPE_IMPL
12962#undef PUGI__IS_CHARTYPE
12963#undef PUGI__IS_CHARTYPEX
12964#undef PUGI__ENDSWITH
12965#undef PUGI__SKIPWS
12966#undef PUGI__OPTSET
12967#undef PUGI__PUSHNODE
12968#undef PUGI__POPNODE
12969#undef PUGI__SCANFOR
12970#undef PUGI__SCANWHILE
12971#undef PUGI__SCANWHILE_UNROLL
12972#undef PUGI__ENDSEG
12973#undef PUGI__THROW_ERROR
12974#undef PUGI__CHECK_ERROR
12975
12976#endif
12977
12978/**
12979 * Copyright (c) 2006-2018 Arseny Kapoulkine
12980 *
12981 * Permission is hereby granted, free of charge, to any person
12982 * obtaining a copy of this software and associated documentation
12983 * files (the "Software"), to deal in the Software without
12984 * restriction, including without limitation the rights to use,
12985 * copy, modify, merge, publish, distribute, sublicense, and/or sell
12986 * copies of the Software, and to permit persons to whom the
12987 * Software is furnished to do so, subject to the following
12988 * conditions:
12989 *
12990 * The above copyright notice and this permission notice shall be
12991 * included in all copies or substantial portions of the Software.
12992 *
12993 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12994 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
12995 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
12996 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
12997 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
12998 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
12999 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
13000 * OTHER DEALINGS IN THE SOFTWARE.
13001 */
auto end() const noexcept
auto begin() const noexcept
名称范围迭代支持
ConcurrentQueue & operator=(ConcurrentQueue const &) MOODYCAMEL_DELETE_FUNCTION
bool operator!=(const xml_attribute_iterator &rhs) const
Definition pugixml.cpp:6884
const xml_attribute_iterator & operator--()
Definition pugixml.cpp:6915
xml_attribute * operator->() const
Definition pugixml.cpp:6895
xml_attribute & operator*() const
Definition pugixml.cpp:6889
bool operator==(const xml_attribute_iterator &rhs) const
Definition pugixml.cpp:6879
const xml_attribute_iterator & operator++()
Definition pugixml.cpp:6901
const char_t * as_string(const char_t *def=PUGIXML_TEXT("")) const
Definition pugixml.cpp:5358
bool operator>=(const xml_attribute &r) const
Definition pugixml.cpp:5343
bool as_bool(bool def=false) const
Definition pugixml.cpp:5383
bool operator!=(const xml_attribute &r) const
Definition pugixml.cpp:5323
bool operator<(const xml_attribute &r) const
Definition pugixml.cpp:5328
int as_int(int def=0) const
Definition pugixml.cpp:5363
float as_float(float def=0) const
Definition pugixml.cpp:5378
bool operator==(const xml_attribute &r) const
Definition pugixml.cpp:5318
bool empty() const
Definition pugixml.cpp:5400
size_t hash_value() const
Definition pugixml.cpp:5415
const char_t * name() const
Definition pugixml.cpp:5405
xml_attribute & operator=(const char_t *rhs)
Definition pugixml.cpp:5425
xml_attribute previous_attribute() const
Definition pugixml.cpp:5353
xml_attribute_struct * _attr
Definition pugixml.hpp:353
xml_attribute next_attribute() const
Definition pugixml.cpp:5348
bool operator!() const
Definition pugixml.cpp:5313
bool operator<=(const xml_attribute &r) const
Definition pugixml.cpp:5338
double as_double(double def=0) const
Definition pugixml.cpp:5373
void(* unspecified_bool_type)(xml_attribute ***)
Definition pugixml.hpp:355
bool set_name(const char_t *rhs)
Definition pugixml.cpp:5487
bool set_value(const char_t *rhs)
Definition pugixml.cpp:5494
unsigned int as_uint(unsigned int def=0) const
Definition pugixml.cpp:5368
xml_attribute_struct * internal_object() const
Definition pugixml.cpp:5420
bool operator>(const xml_attribute &r) const
Definition pugixml.cpp:5333
const char_t * value() const
Definition pugixml.cpp:5410
xml_parse_result load_buffer_inplace(void *contents, size_t size, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition pugixml.cpp:7330
xml_node document_element() const
Definition pugixml.cpp:7404
xml_parse_result load_string(const char_t *contents, unsigned int options=parse_default)
Definition pugixml.cpp:7286
bool save_file(const char *path, const char_t *indent=PUGIXML_TEXT("\t"), unsigned int flags=format_default, xml_encoding encoding=encoding_auto) const
Definition pugixml.cpp:7388
xml_parse_result load_buffer_inplace_own(void *contents, size_t size, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition pugixml.cpp:7337
xml_parse_result load_file(const char *path, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition pugixml.cpp:7303
xml_parse_result load_buffer(const void *contents, size_t size, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition pugixml.cpp:7323
xml_document & operator=(const xml_document &)
xml_parse_result load(std::basic_istream< char, std::char_traits< char > > &stream, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition pugixml.cpp:7271
void save(xml_writer &writer, const char_t *indent=PUGIXML_TEXT("\t"), unsigned int flags=format_default, xml_encoding encoding=encoding_auto) const
Definition pugixml.cpp:7344
void _move(xml_document &rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
bool operator!=(const xml_named_node_iterator &rhs) const
Definition pugixml.cpp:6945
xml_node * operator->() const
Definition pugixml.cpp:6956
const xml_named_node_iterator & operator--()
Definition pugixml.cpp:6976
bool operator==(const xml_named_node_iterator &rhs) const
Definition pugixml.cpp:6940
xml_node & operator*() const
Definition pugixml.cpp:6950
const xml_named_node_iterator & operator++()
Definition pugixml.cpp:6962
bool operator==(const xml_node_iterator &rhs) const
Definition pugixml.cpp:6818
const xml_node_iterator & operator--()
Definition pugixml.cpp:6854
xml_node * operator->() const
Definition pugixml.cpp:6834
bool operator!=(const xml_node_iterator &rhs) const
Definition pugixml.cpp:6823
const xml_node_iterator & operator++()
Definition pugixml.cpp:6840
xml_node & operator*() const
Definition pugixml.cpp:6828
string_t path(char_t delimiter='/') const
Definition pugixml.cpp:6331
size_t hash_value() const
Definition pugixml.cpp:6460
PUGIXML_DEPRECATED xpath_node select_single_node(const char_t *query, xpath_variable_set *variables=0) const
bool set_value(const char_t *rhs)
Definition pugixml.cpp:5837
xml_node_type type() const
Definition pugixml.cpp:5675
xml_node append_child(xml_node_type type=node_element)
Definition pugixml.cpp:5987
xml_node child(const char_t *name) const
Definition pugixml.cpp:5685
friend class xml_named_node_iterator
Definition pugixml.hpp:460
xml_node insert_move_after(const xml_node &moved, const xml_node &node)
Definition pugixml.cpp:6195
xml_node last_child() const
Definition pugixml.cpp:5822
xml_node append_move(const xml_node &moved)
Definition pugixml.cpp:6163
xml_node first_child() const
Definition pugixml.cpp:5817
bool operator>(const xml_node &r) const
Definition pugixml.cpp:5650
xml_object_range< xml_node_iterator > children() const
Definition pugixml.cpp:5620
xml_node prepend_move(const xml_node &moved)
Definition pugixml.cpp:6179
xml_attribute append_attribute(const char_t *name)
Definition pugixml.cpp:5847
xml_node next_sibling() const
Definition pugixml.cpp:5716
xml_node_struct * internal_object() const
Definition pugixml.cpp:6465
xml_node_struct * _root
Definition pugixml.hpp:463
xml_object_range< xml_attribute_iterator > attributes() const
Definition pugixml.cpp:5630
xml_node parent() const
Definition pugixml.cpp:5772
bool remove_child(const xml_node &n)
Definition pugixml.cpp:6255
xml_attribute append_copy(const xml_attribute &proto)
Definition pugixml.cpp:5917
xml_node insert_child_after(xml_node_type type, const xml_node &node)
Definition pugixml.cpp:6039
xml_attribute last_attribute() const
Definition pugixml.cpp:5812
attribute_iterator attributes_end() const
Definition pugixml.cpp:5615
xml_node previous_sibling() const
Definition pugixml.cpp:5764
xpath_node select_node(const char_t *query, xpath_variable_set *variables=0) const
ptrdiff_t offset_debug() const
Definition pugixml.cpp:6497
iterator end() const
Definition pugixml.cpp:5605
xml_text text() const
Definition pugixml.cpp:5782
xml_attribute insert_attribute_before(const char_t *name, const xml_attribute &attr)
Definition pugixml.cpp:5899
xml_attribute insert_attribute_after(const char_t *name, const xml_attribute &attr)
Definition pugixml.cpp:5881
bool operator!=(const xml_node &r) const
Definition pugixml.cpp:5640
bool operator<(const xml_node &r) const
Definition pugixml.cpp:5645
xml_attribute prepend_attribute(const char_t *name)
Definition pugixml.cpp:5864
xml_parse_result append_buffer(const void *contents, size_t size, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition pugixml.cpp:6268
const char_t * value() const
Definition pugixml.cpp:5680
void(* unspecified_bool_type)(xml_node ***)
Definition pugixml.hpp:465
xml_attribute_iterator attribute_iterator
Definition pugixml.hpp:685
bool traverse(xml_tree_walker &walker)
Definition pugixml.cpp:6413
xml_node find_child_by_attribute(const char_t *name, const char_t *attr_name, const char_t *attr_value) const
Definition pugixml.cpp:6303
const char_t * child_value() const
Definition pugixml.cpp:5787
bool set_name(const char_t *rhs)
Definition pugixml.cpp:5827
xml_node prepend_child(xml_node_type type=node_element)
Definition pugixml.cpp:6004
xml_attribute first_attribute() const
Definition pugixml.cpp:5807
bool operator!() const
Definition pugixml.cpp:5595
bool operator>=(const xml_node &r) const
Definition pugixml.cpp:5660
xml_attribute insert_copy_after(const xml_attribute &proto, const xml_attribute &attr)
Definition pugixml.cpp:5951
xml_attribute prepend_copy(const xml_attribute &proto)
Definition pugixml.cpp:5934
xml_node insert_move_before(const xml_node &moved, const xml_node &node)
Definition pugixml.cpp:6213
xml_node first_element_by_path(const char_t *path, char_t delimiter='/') const
Definition pugixml.cpp:6366
xml_attribute attribute(const char_t *name) const
Definition pugixml.cpp:5695
const char_t * name() const
Definition pugixml.cpp:5670
xml_attribute insert_copy_before(const xml_attribute &proto, const xml_attribute &attr)
Definition pugixml.cpp:5969
attribute_iterator attributes_begin() const
Definition pugixml.cpp:5610
xpath_node_set select_nodes(const char_t *query, xpath_variable_set *variables=0) const
xml_node_iterator iterator
Definition pugixml.hpp:679
bool operator<=(const xml_node &r) const
Definition pugixml.cpp:5655
iterator begin() const
Definition pugixml.cpp:5600
bool remove_attribute(const xml_attribute &a)
Definition pugixml.cpp:6236
xml_node root() const
Definition pugixml.cpp:5777
bool empty() const
Definition pugixml.cpp:5665
void print(xml_writer &writer, const char_t *indent=PUGIXML_TEXT("\t"), unsigned int flags=format_default, xml_encoding encoding=encoding_auto, unsigned int depth=0) const
Definition pugixml.cpp:6470
xml_node insert_child_before(xml_node_type type, const xml_node &node)
Definition pugixml.cpp:6021
bool operator==(const xml_node &r) const
Definition pugixml.cpp:5635
const char_t * as_string(const char_t *def=PUGIXML_TEXT("")) const
Definition pugixml.cpp:6597
double as_double(double def=0) const
Definition pugixml.cpp:6618
xml_text & operator=(const char_t *rhs)
Definition pugixml.cpp:6727
bool operator!() const
Definition pugixml.cpp:6580
friend class xml_node
Definition pugixml.hpp:714
float as_float(float def=0) const
Definition pugixml.cpp:6625
xml_node_struct * _data_new()
Definition pugixml.cpp:6559
xml_node_struct * _root
Definition pugixml.hpp:716
bool as_bool(bool def=false) const
Definition pugixml.cpp:6632
xml_node data() const
Definition pugixml.cpp:6789
bool empty() const
Definition pugixml.cpp:6585
bool set(const char_t *rhs)
Definition pugixml.cpp:6655
int as_int(int def=0) const
Definition pugixml.cpp:6604
unsigned int as_uint(unsigned int def=0) const
Definition pugixml.cpp:6611
void(* unspecified_bool_type)(xml_text ***)
Definition pugixml.hpp:718
const char_t * get() const
Definition pugixml.cpp:6590
xml_node_struct * _data() const
Definition pugixml.cpp:6544
virtual bool end(xml_node &node)
Definition pugixml.cpp:5291
virtual bool for_each(xml_node &node)=0
virtual bool begin(xml_node &node)
Definition pugixml.cpp:5286
virtual ~xml_tree_walker()
Definition pugixml.cpp:5277
xml_writer_file(void *file)
Definition pugixml.cpp:5237
virtual void write(const void *data, size_t size) PUGIXML_OVERRIDE
Definition pugixml.cpp:5241
xml_writer_stream(std::basic_ostream< char, std::char_traits< char > > &stream)
Definition pugixml.cpp:5248
std::basic_ostream< wchar_t, std::char_traits< wchar_t > > * wide_stream
Definition pugixml.hpp:342
virtual void write(const void *data, size_t size) PUGIXML_OVERRIDE
Definition pugixml.cpp:5256
std::basic_ostream< char, std::char_traits< char > > * narrow_stream
Definition pugixml.hpp:341
const xpath_parse_result & result() const
xpath_exception(const xpath_parse_result &result)
virtual const char * what() const noexcept PUGIXML_OVERRIDE
xpath_parse_result _result
Definition pugixml.hpp:1267
type_t type() const
xpath_node_set & operator=(const xpath_node_set &ns)
void _assign(const_iterator begin, const_iterator end, type_t type)
const_iterator end() const
void sort(bool reverse=false)
const_iterator begin() const
xpath_node first() const
const xpath_node & operator[](size_t index) const
size_t size() const
xpath_node * _begin
Definition pugixml.hpp:1386
xpath_node * _end
Definition pugixml.hpp:1387
void _move(xpath_node_set &rhs) PUGIXML_NOEXCEPT
void(* unspecified_bool_type)(xpath_node ***)
Definition pugixml.hpp:1288
bool operator!() const
xml_node parent() const
xml_attribute attribute() const
xml_node node() const
bool operator!=(const xpath_node &n) const
xml_attribute _attribute
Definition pugixml.hpp:1286
bool operator==(const xpath_node &n) const
string_t evaluate_string(const xpath_node &n) const
void(* unspecified_bool_type)(xpath_query ***)
Definition pugixml.hpp:1195
bool operator!() const
double evaluate_number(const xpath_node &n) const
xpath_node_set evaluate_node_set(const xpath_node &n) const
xpath_value_type return_type() const
xpath_parse_result _result
Definition pugixml.hpp:1193
xpath_query & operator=(const xpath_query &)
const xpath_parse_result & result() const
bool evaluate_boolean(const xpath_node &n) const
xpath_node evaluate_node(const xpath_node &n) const
xpath_variable * add(const char_t *name, xpath_value_type type)
void _swap(xpath_variable_set &rhs)
bool set(const char_t *name, bool value)
void _assign(const xpath_variable_set &rhs)
static void _destroy(xpath_variable *var)
static bool _clone(xpath_variable *var, xpath_variable **out_result)
xpath_variable * get(const char_t *name)
xpath_variable * _data[64]
Definition pugixml.hpp:1149
xpath_variable * _find(const char_t *name) const
xpath_variable_set & operator=(const xpath_variable_set &rhs)
xpath_variable * _next
Definition pugixml.hpp:1117
bool set(bool value)
xpath_variable(xpath_value_type type)
double get_number() const
const char_t * get_string() const
bool get_boolean() const
const xpath_node_set & get_node_set() const
xpath_value_type type() const
const char_t * name() const
xpath_value_type _type
Definition pugixml.hpp:1116
void write_direct(const char_t *data, size_t length)
Definition pugixml.cpp:3929
void write_string(const char_t *data)
Definition pugixml.cpp:3983
void write(char_t d0)
Definition pugixml.cpp:4008
union xml_buffered_writer::@16 scratch
xml_writer & writer
Definition pugixml.cpp:4102
xml_buffered_writer(xml_writer &writer_, xml_encoding user_encoding)
Definition pugixml.cpp:3899
void write(char_t d0, char_t d1)
Definition pugixml.cpp:4017
xml_buffered_writer(const xml_buffered_writer &)
char_t data_char[bufcapacity]
Definition pugixml.cpp:4099
xml_buffered_writer & operator=(const xml_buffered_writer &)
char_t buffer[bufcapacity]
Definition pugixml.cpp:4092
void flush(const char_t *data, size_t size)
Definition pugixml.cpp:3911
uint16_t data_u16[2 *bufcapacity]
Definition pugixml.cpp:4097
void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)
Definition pugixml.cpp:4050
xml_encoding encoding
Definition pugixml.cpp:4104
uint8_t data_u8[4 *bufcapacity]
Definition pugixml.cpp:4096
void write_buffer(const char_t *data, size_t length)
Definition pugixml.cpp:3968
void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)
Definition pugixml.cpp:4063
void write(char_t d0, char_t d1, char_t d2, char_t d3)
Definition pugixml.cpp:4038
void write(char_t d0, char_t d1, char_t d2)
Definition pugixml.cpp:4027
uint32_t data_u32[bufcapacity]
Definition pugixml.cpp:4098
const unsigned char * table
Definition pugixml.cpp:9604
xpath_variable * variable
Definition pugixml.cpp:9600
xpath_ast_node * _next
Definition pugixml.cpp:9591
const char_t * string
Definition pugixml.cpp:9596
void set_next(xpath_ast_node *value)
bool step_push(xpath_node_set_raw &ns, xml_node_struct *n, xpath_allocator *alloc)
Definition pugixml.cpp:9918
xpath_value_type rettype() const
xpath_node_set_raw step_do(const xpath_context &c, const xpath_stack &stack, nodeset_eval_t eval, T v)
static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval)
Definition pugixml.cpp:9698
void optimize_self(xpath_allocator *alloc)
bool is_posinv_expr() const
bool step_push(xpath_node_set_raw &ns, xml_attribute_struct *a, xml_node_struct *parent, xpath_allocator *alloc)
Definition pugixml.cpp:9878
const char_t * nodetest
Definition pugixml.cpp:9602
xpath_node_set_raw eval_node_set(const xpath_context &c, const xpath_stack &stack, nodeset_eval_t eval)
xpath_string eval_string(const xpath_context &c, const xpath_stack &stack)
union xpath_ast_node::@19 _data
xpath_ast_node(ast_type_t type, xpath_ast_node *left, axis_t axis, nodetest_t test, const char_t *contents)
void apply_predicate(xpath_node_set_raw &ns, size_t first, const xpath_stack &stack, bool once)
Definition pugixml.cpp:9854
static void apply_predicate_number_const(xpath_node_set_raw &ns, size_t first, xpath_ast_node *expr, const xpath_stack &stack)
Definition pugixml.cpp:9826
xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable *value)
double eval_number(const xpath_context &c, const xpath_stack &stack)
void optimize(xpath_allocator *alloc)
static bool compare_eq(xpath_ast_node *lhs, xpath_ast_node *rhs, const xpath_context &c, const xpath_stack &stack, const Comp &comp)
Definition pugixml.cpp:9610
bool is_posinv_step() const
xpath_string eval_string_concat(const xpath_context &c, const xpath_stack &stack)
xpath_ast_node * _right
Definition pugixml.cpp:9590
static void apply_predicate_boolean(xpath_node_set_raw &ns, size_t first, xpath_ast_node *expr, const xpath_stack &stack, bool once)
Definition pugixml.cpp:9774
bool eval_boolean(const xpath_context &c, const xpath_stack &stack)
static bool compare_rel(xpath_ast_node *lhs, xpath_ast_node *rhs, const xpath_context &c, const xpath_stack &stack, const Comp &comp)
Definition pugixml.cpp:9703
static void apply_predicate_number(xpath_node_set_raw &ns, size_t first, xpath_ast_node *expr, const xpath_stack &stack, bool once)
Definition pugixml.cpp:9800
xpath_ast_node & operator=(const xpath_ast_node &)
void step_fill(xpath_node_set_raw &ns, xml_attribute_struct *a, xml_node_struct *p, xpath_allocator *alloc, bool once, T v)
void step_fill(xpath_node_set_raw &ns, xml_node_struct *n, xpath_allocator *alloc, bool once, T)
Definition pugixml.cpp:9993
void step_fill(xpath_node_set_raw &ns, const xpath_node &xn, xpath_allocator *alloc, bool once, T v)
xpath_ast_node * _left
Definition pugixml.cpp:9589
xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value)
xpath_ast_node(const xpath_ast_node &)
xpath_ast_node(ast_type_t type, xpath_ast_node *left, xpath_ast_node *right, predicate_t test)
void apply_predicates(xpath_node_set_raw &ns, size_t first, const xpath_stack &stack, nodeset_eval_t eval)
Definition pugixml.cpp:9868
xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t *value)
xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node *left=0, xpath_ast_node *right=0)
void set_right(xpath_ast_node *value)
const char_t * _cur_lexeme_pos
Definition pugixml.cpp:9155
const char_t * current_pos() const
Definition pugixml.cpp:9445
lexeme_t _cur_lexeme
Definition pugixml.cpp:9158
void next()
Definition pugixml.cpp:9171
lexeme_t current() const
Definition pugixml.cpp:9440
xpath_lexer_string _cur_lexeme_contents
Definition pugixml.cpp:9156
const xpath_lexer_string & contents() const
Definition pugixml.cpp:9450
xpath_lexer(const char_t *query)
Definition pugixml.cpp:9161
const char_t * state() const
Definition pugixml.cpp:9166
const char_t * _cur
Definition pugixml.cpp:9154
xpath_node_set::type_t _type
Definition pugixml.cpp:8971
void append(const xpath_node *begin_, const xpath_node *end_, xpath_allocator *alloc)
Definition pugixml.cpp:9017
size_t size() const
Definition pugixml.cpp:8997
xpath_node_set::type_t type() const
Definition pugixml.cpp:9061
xpath_node * end() const
Definition pugixml.cpp:8987
void push_back(const xpath_node &node, xpath_allocator *alloc)
Definition pugixml.cpp:9009
xpath_node * _eos
Definition pugixml.cpp:8975
xpath_node first() const
Definition pugixml.cpp:9002
xpath_node * _begin
Definition pugixml.cpp:8973
bool empty() const
Definition pugixml.cpp:8992
xpath_node * begin() const
Definition pugixml.cpp:8982
void truncate(xpath_node *pos)
Definition pugixml.cpp:9046
void push_back_grow(const xpath_node &node, xpath_allocator *alloc)
Definition pugixml.cpp:9072
void set_type(xpath_node_set::type_t value)
Definition pugixml.cpp:9066
xpath_node * _end
Definition pugixml.cpp:8974
size_t _length_heap
Definition pugixml.cpp:7875
static xpath_string from_heap_preallocated(const char_t *begin, const char_t *end)
Definition pugixml.cpp:7898
bool empty() const
Definition pugixml.cpp:7985
bool uses_heap() const
Definition pugixml.cpp:8000
static xpath_string from_const(const char_t *str)
Definition pugixml.cpp:7893
size_t length() const
Definition pugixml.cpp:7962
bool operator!=(const xpath_string &o) const
Definition pugixml.cpp:7995
xpath_string(const char_t *buffer, bool uses_heap_, size_t length_heap)
Definition pugixml.cpp:7888
void append(const xpath_string &o, xpath_allocator *alloc)
Definition pugixml.cpp:7922
static xpath_string from_heap(const char_t *begin, const char_t *end, xpath_allocator *alloc)
Definition pugixml.cpp:7905
char_t * data(xpath_allocator *alloc)
Definition pugixml.cpp:7967
bool operator==(const xpath_string &o) const
Definition pugixml.cpp:7990
const char_t * _buffer
Definition pugixml.cpp:7873
static char_t * duplicate_string(const char_t *string, size_t length, xpath_allocator *alloc)
Definition pugixml.cpp:7877
const char_t * c_str() const
Definition pugixml.cpp:7957
Carlaе·عܵռ
pugi XMLռ
xml_encoding
Definition pugixml.hpp:218
@ encoding_utf16_le
Definition pugixml.hpp:221
@ encoding_utf32_be
Definition pugixml.hpp:225
@ encoding_utf16_be
Definition pugixml.hpp:222
@ encoding_utf8
Definition pugixml.hpp:220
@ encoding_latin1
Definition pugixml.hpp:228
@ encoding_utf32_le
Definition pugixml.hpp:224
@ encoding_auto
Definition pugixml.hpp:219
@ encoding_wchar
Definition pugixml.hpp:227
std::basic_string< PUGIXML_CHAR, std::char_traits< PUGIXML_CHAR >, std::allocator< PUGIXML_CHAR > > string_t
Definition pugixml.hpp:132
PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function()
Definition pugixml.cpp:7452
PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function()
Definition pugixml.cpp:7447
const unsigned int format_no_declaration
Definition pugixml.hpp:243
xml_node_type
Definition pugixml.hpp:141
@ node_comment
Definition pugixml.hpp:147
@ node_pcdata
Definition pugixml.hpp:145
@ node_element
Definition pugixml.hpp:144
@ node_doctype
Definition pugixml.hpp:150
@ node_document
Definition pugixml.hpp:143
@ node_declaration
Definition pugixml.hpp:149
@ node_pi
Definition pugixml.hpp:148
@ node_null
Definition pugixml.hpp:142
@ node_cdata
Definition pugixml.hpp:146
const unsigned int format_raw
Definition pugixml.hpp:240
void(* deallocation_function)(void *ptr)
Definition pugixml.hpp:1408
static PUGI__FN void unspecified_bool_xpath_query(xpath_query ***)
static PUGI__FN void unspecified_bool_xml_node(xml_node ***)
Definition pugixml.cpp:5586
void *(* allocation_function)(size_t size)
Definition pugixml.hpp:1405
PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t *str)
Definition pugixml.cpp:7416
static PUGI__FN void unspecified_bool_xml_attribute(xml_attribute ***)
Definition pugixml.cpp:5304
@ status_append_invalid_root
Definition pugixml.hpp:974
@ status_end_element_mismatch
Definition pugixml.hpp:973
@ status_bad_end_element
Definition pugixml.hpp:972
@ status_io_error
Definition pugixml.hpp:959
@ status_bad_attribute
Definition pugixml.hpp:971
@ status_file_not_found
Definition pugixml.hpp:958
@ status_internal_error
Definition pugixml.hpp:961
@ status_bad_start_element
Definition pugixml.hpp:970
@ status_ok
Definition pugixml.hpp:956
@ status_bad_comment
Definition pugixml.hpp:965
@ status_bad_doctype
Definition pugixml.hpp:968
@ status_out_of_memory
Definition pugixml.hpp:960
@ status_unrecognized_tag
Definition pugixml.hpp:962
@ status_bad_cdata
Definition pugixml.hpp:967
@ status_bad_pi
Definition pugixml.hpp:964
@ status_bad_pcdata
Definition pugixml.hpp:969
@ status_no_document_element
Definition pugixml.hpp:976
const unsigned int format_save_file_text
Definition pugixml.hpp:249
const unsigned int format_write_bom
Definition pugixml.hpp:237
PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate)
Definition pugixml.cpp:7441
static PUGI__FN void unspecified_bool_xpath_node(xpath_node ***)
static PUGI__FN void unspecified_bool_xml_text(xml_text ***)
Definition pugixml.cpp:6571
PUGI__FN std::basic_string< wchar_t > PUGIXML_FUNCTION as_wide(const char *str)
Definition pugixml.cpp:7428
xpath_value_type
Definition pugixml.hpp:1083
@ xpath_type_number
Definition pugixml.hpp:1086
@ xpath_type_boolean
Definition pugixml.hpp:1088
@ xpath_type_none
Definition pugixml.hpp:1084
@ xpath_type_string
Definition pugixml.hpp:1087
@ xpath_type_node_set
Definition pugixml.hpp:1085
PUGIXML_CHAR char_t
Definition pugixml.hpp:128
static double sn[6]
Definition odrSpiral.cpp:47
static double sd[6]
Definition odrSpiral.cpp:56
void destroy_node(xml_node_struct *n, xml_allocator &alloc)
Definition pugixml.cpp:1397
#define PUGI__CHECK_ERROR(err, m)
Definition pugixml.cpp:2798
PUGI__FN void default_deallocate(void *ptr)
Definition pugixml.cpp:225
PUGI__FN size_t strlength_wide(const wchar_t *s)
Definition pugixml.cpp:290
void insertion_sort(T *begin, T *end, const Pred &pred)
Definition pugixml.cpp:7581
static const size_t xml_memory_page_size
Definition pugixml.cpp:589
PUGI__FN void node_copy_string(String &dest, Header &header, uintptr_t header_mask, char_t *source, Header &source_header, xml_allocator *alloc)
Definition pugixml.cpp:4579
#define PUGI__OPTSET(OPT)
Definition pugixml.cpp:2790
PUGI__FN double round_nearest_nzero(double value)
Definition pugixml.cpp:8522
PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t *data, size_t size)
Definition pugixml.cpp:2400
PUGI__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)
Definition pugixml.cpp:4880
#define PUGI__NODETYPE(n)
Definition pugixml.cpp:546
ast_type_t
Definition pugixml.cpp:9459
@ ast_op_and
Definition pugixml.cpp:9462
@ ast_number_constant
Definition pugixml.cpp:9479
@ ast_filter
Definition pugixml.cpp:9477
@ ast_func_substring_3
Definition pugixml.cpp:9499
@ ast_func_sum
Definition pugixml.cpp:9512
@ ast_func_name_1
Definition pugixml.cpp:9490
@ ast_func_floor
Definition pugixml.cpp:9513
@ ast_op_divide
Definition pugixml.cpp:9472
@ ast_opt_translate_table
Definition pugixml.cpp:9519
@ ast_func_concat
Definition pugixml.cpp:9493
@ ast_op_equal
Definition pugixml.cpp:9463
@ ast_unknown
Definition pugixml.cpp:9460
@ ast_func_name_0
Definition pugixml.cpp:9489
@ ast_predicate
Definition pugixml.cpp:9476
@ ast_func_not
Definition pugixml.cpp:9506
@ ast_variable
Definition pugixml.cpp:9480
@ ast_func_string_1
Definition pugixml.cpp:9492
@ ast_func_string_0
Definition pugixml.cpp:9491
@ ast_opt_compare_attribute
Definition pugixml.cpp:9520
@ ast_func_number_0
Definition pugixml.cpp:9510
@ ast_op_union
Definition pugixml.cpp:9475
@ ast_func_substring_before
Definition pugixml.cpp:9496
@ ast_func_string_length_0
Definition pugixml.cpp:9500
@ ast_func_local_name_1
Definition pugixml.cpp:9486
@ ast_func_namespace_uri_0
Definition pugixml.cpp:9487
@ ast_func_lang
Definition pugixml.cpp:9509
@ ast_func_true
Definition pugixml.cpp:9507
@ ast_func_normalize_space_1
Definition pugixml.cpp:9503
@ ast_op_not_equal
Definition pugixml.cpp:9464
@ ast_func_contains
Definition pugixml.cpp:9495
@ ast_op_greater
Definition pugixml.cpp:9466
@ ast_op_negate
Definition pugixml.cpp:9474
@ ast_func_substring_2
Definition pugixml.cpp:9498
@ ast_func_position
Definition pugixml.cpp:9482
@ ast_string_constant
Definition pugixml.cpp:9478
@ ast_func_ceiling
Definition pugixml.cpp:9514
@ ast_op_subtract
Definition pugixml.cpp:9470
@ ast_func_last
Definition pugixml.cpp:9481
@ ast_func_normalize_space_0
Definition pugixml.cpp:9502
@ ast_func_boolean
Definition pugixml.cpp:9505
@ ast_op_less_or_equal
Definition pugixml.cpp:9467
@ ast_step
Definition pugixml.cpp:9516
@ ast_op_multiply
Definition pugixml.cpp:9471
@ ast_func_count
Definition pugixml.cpp:9483
@ ast_func_substring_after
Definition pugixml.cpp:9497
@ ast_func_namespace_uri_1
Definition pugixml.cpp:9488
@ ast_step_root
Definition pugixml.cpp:9517
@ ast_func_translate
Definition pugixml.cpp:9504
@ ast_func_round
Definition pugixml.cpp:9515
@ ast_func_number_1
Definition pugixml.cpp:9511
@ ast_func_string_length_1
Definition pugixml.cpp:9501
@ ast_func_starts_with
Definition pugixml.cpp:9494
@ ast_op_add
Definition pugixml.cpp:9469
@ ast_op_or
Definition pugixml.cpp:9461
@ ast_op_greater_or_equal
Definition pugixml.cpp:9468
@ ast_func_false
Definition pugixml.cpp:9508
@ ast_func_local_name_0
Definition pugixml.cpp:9485
@ ast_op_mod
Definition pugixml.cpp:9473
@ ast_func_id
Definition pugixml.cpp:9484
@ ast_op_less
Definition pugixml.cpp:9465
I median3(I first, I middle, I last, const Pred &pred)
Definition pugixml.cpp:7603
PUGI__FN xml_encoding get_write_native_encoding()
Definition pugixml.cpp:3755
PUGI__FN const char_t * qualified_name(const xpath_node &node)
Definition pugixml.cpp:8529
PUGI__FN bool convert_number_to_boolean(double value)
Definition pugixml.cpp:8326
predicate_t
Definition pugixml.cpp:9554
@ predicate_constant
Definition pugixml.cpp:9557
@ predicate_constant_one
Definition pugixml.cpp:9558
@ predicate_default
Definition pugixml.cpp:9555
@ predicate_posinv
Definition pugixml.cpp:9556
PUGI__FN bool allow_insert_attribute(xml_node_type parent)
Definition pugixml.cpp:4540
char_t *(* strconv_attribute_t)(char_t *, char_t)
Definition pugixml.cpp:2927
#define PUGI__SCANWHILE_UNROLL(X)
Definition pugixml.cpp:2795
PUGI__FN const void * document_buffer_order(const xpath_node &xnode)
Definition pugixml.cpp:8172
PUGI__FN void as_utf8_end(char *buffer, size_t size, const wchar_t *str, size_t length)
Definition pugixml.cpp:2487
wchar_selector< sizeof(wchar_t)>::writer wchar_writer
Definition pugixml.cpp:2005
PUGI__FN xml_encoding get_wchar_encoding()
Definition pugixml.cpp:2109
PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t *value, U minv, U maxv)
Definition pugixml.cpp:4678
PUGI__FN void node_copy_attribute(xml_attribute_struct *da, xml_attribute_struct *sa)
Definition pugixml.cpp:4661
PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child)
Definition pugixml.cpp:4545
lexeme_t
Definition pugixml.cpp:9105
@ lex_multiply
Definition pugixml.cpp:9115
@ lex_double_slash
Definition pugixml.cpp:9123
@ lex_quoted_string
Definition pugixml.cpp:9120
@ lex_not_equal
Definition pugixml.cpp:9108
@ lex_axis_attribute
Definition pugixml.cpp:9128
@ lex_less
Definition pugixml.cpp:9109
@ lex_equal
Definition pugixml.cpp:9107
@ lex_greater_or_equal
Definition pugixml.cpp:9112
@ lex_none
Definition pugixml.cpp:9106
@ lex_union
Definition pugixml.cpp:9116
@ lex_comma
Definition pugixml.cpp:9127
@ lex_close_brace
Definition pugixml.cpp:9119
@ lex_slash
Definition pugixml.cpp:9122
@ lex_var_ref
Definition pugixml.cpp:9117
@ lex_dot
Definition pugixml.cpp:9129
@ lex_minus
Definition pugixml.cpp:9114
@ lex_string
Definition pugixml.cpp:9126
@ lex_eof
Definition pugixml.cpp:9132
@ lex_plus
Definition pugixml.cpp:9113
@ lex_number
Definition pugixml.cpp:9121
@ lex_greater
Definition pugixml.cpp:9110
@ lex_double_dot
Definition pugixml.cpp:9130
@ lex_less_or_equal
Definition pugixml.cpp:9111
@ lex_close_square_brace
Definition pugixml.cpp:9125
@ lex_open_square_brace
Definition pugixml.cpp:9124
@ lex_open_brace
Definition pugixml.cpp:9118
@ lex_double_colon
Definition pugixml.cpp:9131
PUGI__FN FILE * open_file_wide(const wchar_t *path, const wchar_t *mode)
Definition pugixml.cpp:5188
PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t *data, size_t length, D, T)
Definition pugixml.cpp:3782
PUGI__FN void text_output_cdata(xml_buffered_writer &writer, const char_t *s)
Definition pugixml.cpp:4156
PUGI__FN PUGI__UNSIGNED_OVERFLOW unsigned int hash_string(const char_t *str)
Definition pugixml.cpp:8773
PUGI__FN bool get_mutable_buffer(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, bool is_mutable)
Definition pugixml.cpp:2241
PUGI__FN bool set_value_integer(String &dest, Header &header, uintptr_t header_mask, U value, bool negative)
Definition pugixml.cpp:4847
PUGI__FN bool convert_buffer_generic(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, D)
Definition pugixml.cpp:2375
static const uintptr_t xml_memory_page_type_mask
Definition pugixml.cpp:525
#define PUGI__UNSIGNED_OVERFLOW
Definition pugixml.cpp:151
PUGI__FN void node_copy_tree(xml_node_struct *dn, xml_node_struct *sn)
Definition pugixml.cpp:4615
PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator *alloc)
Definition pugixml.cpp:8383
PUGI__FN bool is_little_endian()
Definition pugixml.cpp:2102
PUGI__FN void node_output(xml_buffered_writer &writer, xml_node_struct *root, const char_t *indent, unsigned int flags, unsigned int depth)
Definition pugixml.cpp:4425
PUGI__FN size_t zero_terminate_buffer(void *buffer, size_t size, xml_encoding encoding)
Definition pugixml.cpp:4953
PUGI__FN void node_output_simple(xml_buffered_writer &writer, xml_node_struct *node, unsigned int flags)
Definition pugixml.cpp:4363
PUGI__FN float get_value_float(const char_t *value)
Definition pugixml.cpp:4779
PUGI__FN char_t * strconv_comment(char_t *s, char_t endch)
Definition pugixml.cpp:2800
#define PUGI__IS_CHARTYPE(c, ct)
Definition pugixml.cpp:2099
PUGI__FN void node_copy_contents(xml_node_struct *dn, xml_node_struct *sn, xml_allocator *shared_alloc)
Definition pugixml.cpp:4598
PUGI__FN double gen_nan()
Definition pugixml.cpp:8266
PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)
Definition pugixml.cpp:3078
PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)
Definition pugixml.cpp:2909
PUGI__FN bool convert_buffer(char_t *&out_buffer, size_t &out_length, xml_encoding encoding, const void *contents, size_t size, bool is_mutable)
Definition pugixml.cpp:2446
PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream< T > &stream, void **out_buffer, size_t *out_size)
Definition pugixml.cpp:5099
PUGI__FN bool set_value_bool(String &dest, Header &header, uintptr_t header_mask, bool value)
Definition pugixml.cpp:4875
I min_element(I begin, I end, const Pred &pred)
Definition pugixml.cpp:7542
#define PUGI__ENDSEG()
Definition pugixml.cpp:2796
indent_flags_t
Definition pugixml.cpp:4420
@ indent_indent
Definition pugixml.cpp:4422
@ indent_newline
Definition pugixml.cpp:4421
xml_document_struct & get_document(const Object *object)
Definition pugixml.cpp:1358
PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t *data, size_t size)
Definition pugixml.cpp:2173
nodeset_eval_t
Definition pugixml.cpp:9562
@ nodeset_eval_first
Definition pugixml.cpp:9565
@ nodeset_eval_any
Definition pugixml.cpp:9564
@ nodeset_eval_all
Definition pugixml.cpp:9563
PUGI__FN char_t * strconv_escape(char_t *s, gap &g)
Definition pugixml.cpp:2647
PUGI__FN void node_output_comment(xml_buffered_writer &writer, const char_t *s)
Definition pugixml.cpp:4218
PUGI__FN bool node_is_ancestor(xml_node_struct *parent, xml_node_struct *node)
Definition pugixml.cpp:8165
void sort(I begin, I end, const Pred &pred)
Definition pugixml.cpp:7639
void destroy_attribute(xml_attribute_struct *a, xml_allocator &alloc)
Definition pugixml.cpp:1386
PUGI__FN void node_output_pi_value(xml_buffered_writer &writer, const char_t *s)
Definition pugixml.cpp:4243
wchar_selector< sizeof(wchar_t)>::counter wchar_counter
Definition pugixml.cpp:2004
PUGI__NS_END PUGI__NS_BEGIN PUGI__FN bool starts_with(const char_t *string, const char_t *pattern)
Definition pugixml.cpp:8008
static const uintptr_t xml_memory_page_name_allocated_or_shared_mask
Definition pugixml.cpp:528
#define PUGI__IS_CHARTYPEX(c, ct)
Definition pugixml.cpp:2100
void remove_attribute(xml_attribute_struct *attr, xml_node_struct *node)
Definition pugixml.cpp:1577
PUGI__NS_END PUGI__NS_BEGIN PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node *begin, const xpath_node *end)
Definition pugixml.cpp:8909
PUGI__FN int get_value_int(const char_t *value)
Definition pugixml.cpp:4760
I unique(I begin, I end)
Definition pugixml.cpp:7558
PUGI__FN bool node_is_before_sibling(xml_node_struct *ln, xml_node_struct *rn)
Definition pugixml.cpp:8098
static const unsigned char chartypex_table[256]
Definition pugixml.cpp:2071
PUGI__FN bool check_string_to_number_format(const char_t *string)
Definition pugixml.cpp:8450
static const uintptr_t xml_memory_page_contents_shared_mask
Definition pugixml.cpp:522
void prepend_node(xml_node_struct *child, xml_node_struct *node)
Definition pugixml.cpp:1447
PUGI__NS_END static PUGI__NS_BEGIN const uintptr_t xml_memory_block_alignment
Definition pugixml.cpp:518
PUGI__FN_NO_INLINE xml_attribute_struct * append_new_attribute(xml_node_struct *node, xml_allocator &alloc)
Definition pugixml.cpp:1605
PUGI__FN T * new_xpath_variable(const char_t *name)
Definition pugixml.cpp:8792
PUGI__FN const char_t * find_char(const char_t *s, char_t c)
Definition pugixml.cpp:8019
PUGI__FN size_t get_valid_length(const char_t *data, size_t length)
Definition pugixml.cpp:3853
void insert_node_before(xml_node_struct *child, xml_node_struct *node)
Definition pugixml.cpp:1482
PUGI__FN std::string as_utf8_impl(const wchar_t *str, size_t length)
Definition pugixml.cpp:2499
xml_memory_management_function_storage< int > xml_memory
Definition pugixml.cpp:244
PUGI__FN bool node_is_before(xml_node_struct *ln, xml_node_struct *rn)
Definition pugixml.cpp:8122
PUGI__FN xml_parse_status get_file_size(FILE *file, size_t &out_result)
Definition pugixml.cpp:4913
bool is_xpath_attribute(const char_t *name)
Definition pugixml.cpp:8721
#define PUGI__SCANCHARTYPE(ct)
char_t *(* strconv_pcdata_t)(char_t *)
Definition pugixml.cpp:2856
static const uintptr_t xpath_memory_block_alignment
Definition pugixml.cpp:7680
void remove_node(xml_node_struct *node)
Definition pugixml.cpp:1499
#define PUGI__GETPAGE_IMPL(header)
Definition pugixml.cpp:540
static const unsigned char chartype_table[256]
Definition pugixml.cpp:2041
#define PUGI__ENDSWITH(c, e)
Definition pugixml.cpp:2788
PUGI__NS_END PUGI__NS_BEGIN uint16_t endian_swap(uint16_t value)
Definition pugixml.cpp:1633
PUGI__FN impl::xpath_ast_node * evaluate_node_set_prepare(xpath_query_impl *impl)
PUGI__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)
Definition pugixml.cpp:8882
PUGI__FN char_t * normalize_space(char_t *buffer)
Definition pugixml.cpp:8609
bool is_text_node(xml_node_struct *node)
Definition pugixml.cpp:4670
PUGI__FN xpath_string string_value(const xpath_node &na, xpath_allocator *alloc)
Definition pugixml.cpp:8044
PUGI__FN bool parse_declaration_encoding(const uint8_t *data, size_t size, const uint8_t *&out_encoding, size_t &out_length)
Definition pugixml.cpp:2119
void reverse(I begin, I end)
Definition pugixml.cpp:7553
#define PUGI__FN_NO_INLINE
Definition pugixml.cpp:197
#define PUGI__STATIC_ASSERT(cond)
Definition pugixml.cpp:134
#define PUGI__NS_BEGIN
Definition pugixml.cpp:193
PUGI__FN void text_output_indent(xml_buffered_writer &writer, const char_t *indent, size_t indent_length, unsigned int depth)
Definition pugixml.cpp:4178
PUGI__FN void node_output_end(xml_buffered_writer &writer, xml_node_struct *node)
Definition pugixml.cpp:4353
PUGI__FN double get_value_double(const char_t *value)
Definition pugixml.cpp:4770
PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream< T > &stream, void **out_buffer, size_t *out_size)
Definition pugixml.cpp:5045
#define PUGI__FN
Definition pugixml.cpp:196
PUGI__FN void close_file(FILE *file)
Definition pugixml.cpp:5006
PUGI__FN_NO_INLINE xml_node_struct * append_new_node(xml_node_struct *node, xml_allocator &alloc, xml_node_type type=node_element)
Definition pugixml.cpp:1593
#define PUGI__SKIPWS()
Definition pugixml.cpp:2789
PUGI__FN PUGI__UNSIGNED_OVERFLOW char_t * integer_to_string(char_t *begin, char_t *end, U value, bool negative)
Definition pugixml.cpp:4809
#define PUGI__DMC_VOLATILE
Definition pugixml.cpp:140
PUGI__NS_END PUGI__NS_BEGIN xml_attribute_struct * allocate_attribute(xml_allocator &alloc)
Definition pugixml.cpp:1368
#define PUGI__UNLIKELY(cond)
Definition pugixml.cpp:130
#define PUGI__THROW_ERROR(err, m)
Definition pugixml.cpp:2797
PUGI__FN const char_t * namespace_uri(xml_node node)
Definition pugixml.cpp:8565
xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset=0)
Definition pugixml.cpp:3104
xml_allocator & get_allocator(const Object *object)
Definition pugixml.cpp:1350
PUGI__FN bool set_value_convert(String &dest, Header &header, uintptr_t header_mask, float value)
Definition pugixml.cpp:4857
PUGI__FN bool allow_move(xml_node parent, xml_node child)
Definition pugixml.cpp:4554
PUGI__FN size_t convert_buffer_output(char_t *, uint8_t *r_u8, uint16_t *r_u16, uint32_t *r_u32, const char_t *data, size_t length, xml_encoding encoding)
Definition pugixml.cpp:3869
PUGI__FN char_t * translate_table(char_t *buffer, const unsigned char *table)
Definition pugixml.cpp:8691
#define PUGI__SCANFOR(X)
Definition pugixml.cpp:2793
PUGI__FN const char_t * convert_number_to_string_special(double value)
Definition pugixml.cpp:8294
PUGI__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)
Definition pugixml.cpp:5135
PUGI__FN bool is_attribute_of(xml_attribute_struct *attr, xml_node_struct *node)
Definition pugixml.cpp:4531
static const uintptr_t xml_memory_page_name_allocated_mask
Definition pugixml.cpp:523
PUGI__FN bool convert_string_to_number_scratch(char_t(&buffer)[32], const char_t *begin, const char_t *end, double *out_result)
Definition pugixml.cpp:8493
PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node *begin, xpath_node *end, xpath_node_set::type_t type, bool rev)
Definition pugixml.cpp:8925
#define PUGI__NS_END
Definition pugixml.cpp:194
PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding)
Definition pugixml.cpp:3764
PUGI__FN void delete_xpath_variable(T *var)
Definition pugixml.cpp:8829
PUGI__FN bool convert_buffer_latin1(char_t *&out_buffer, size_t &out_length, const void *contents, size_t size, bool is_mutable)
Definition pugixml.cpp:2409
void swap(T &lhs, T &rhs)
Definition pugixml.cpp:7535
PUGI__NS_END PUGI__NS_BEGIN PUGI__FN size_t strlength(const char_t *s)
Definition pugixml.cpp:252
PUGI__FN char * convert_path_heap(const wchar_t *str)
Definition pugixml.cpp:5167
PUGI__FN double round_nearest(double value)
Definition pugixml.cpp:8517
PUGI__FN char_t * translate(char_t *buffer, const char_t *from, const char_t *to, size_t to_length)
Definition pugixml.cpp:8637
void insert_node_after(xml_node_struct *child, xml_node_struct *node)
Definition pugixml.cpp:1465
nodetest_t
Definition pugixml.cpp:9541
@ nodetest_all
Definition pugixml.cpp:9549
@ nodetest_name
Definition pugixml.cpp:9543
@ nodetest_none
Definition pugixml.cpp:9542
@ nodetest_type_text
Definition pugixml.cpp:9547
@ nodetest_all_in_namespace
Definition pugixml.cpp:9550
@ nodetest_pi
Definition pugixml.cpp:9548
@ nodetest_type_comment
Definition pugixml.cpp:9545
@ nodetest_type_node
Definition pugixml.cpp:9544
@ nodetest_type_pi
Definition pugixml.cpp:9546
#define PUGI__POPNODE()
Definition pugixml.cpp:2792
#define PUGI__SNPRINTF
Definition pugixml.cpp:179
void insert_attribute_before(xml_attribute_struct *attr, xml_attribute_struct *place, xml_node_struct *node)
Definition pugixml.cpp:1565
PUGI__FN const char_t * find_substring(const char_t *s, const char_t *p)
Definition pugixml.cpp:8028
PUGI__FN bool strequalrange(const char_t *lhs, const char_t *rhs, size_t count)
Definition pugixml.cpp:278
static const uintptr_t xml_memory_page_value_allocated_or_shared_mask
Definition pugixml.cpp:529
#define PUGI__SCANCHAR(ch)
PUGI__FN bool save_file_impl(const xml_document &doc, FILE *file, const char_t *indent, unsigned int flags, xml_encoding encoding)
Definition pugixml.cpp:5208
PUGI__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)
Definition pugixml.cpp:4291
PUGI__FN bool has_declaration(xml_node_struct *node)
Definition pugixml.cpp:4518
PUGI__FN double convert_string_to_number(const char_t *string)
Definition pugixml.cpp:8480
PUGI__FN bool is_nan(double value)
Definition pugixml.cpp:8281
PUGI__FN bool set_value_ascii(String &dest, Header &header, uintptr_t header_mask, char *buf)
Definition pugixml.cpp:4831
static const uintptr_t xml_memory_page_value_allocated_mask
Definition pugixml.cpp:524
PUGI__FN xpath_node xpath_first(const xpath_node *begin, const xpath_node *end, xpath_node_set::type_t type)
Definition pugixml.cpp:8948
#define PUGI__PUSHNODE(TYPE)
Definition pugixml.cpp:2791
static const xpath_node_set dummy_node_set
Definition pugixml.cpp:8771
PUGI__FN size_t as_utf8_begin(const wchar_t *str, size_t length)
Definition pugixml.cpp:2481
PUGI__FN unsigned char * translate_table_generate(xpath_allocator *alloc, const char_t *from, const char_t *to)
Definition pugixml.cpp:8659
PUGI__FN void text_output(xml_buffered_writer &writer, const char_t *s, chartypex_t type, unsigned int flags)
Definition pugixml.cpp:4148
void append_attribute(xml_attribute_struct *attr, xml_node_struct *node)
Definition pugixml.cpp:1518
void prepend_attribute(xml_attribute_struct *attr, xml_node_struct *node)
Definition pugixml.cpp:1537
xml_node_struct * allocate_node(xml_allocator &alloc, xml_node_type type)
Definition pugixml.cpp:1377
#define PUGI__GETHEADER_IMPL(object, page, flags)
Definition pugixml.cpp:537
#define PUGI__GETPAGE(n)
Definition pugixml.cpp:544
PUGI__FN bool strcpy_insitu(String &dest, Header &header, uintptr_t header_mask, const char_t *source, size_t source_length)
Definition pugixml.cpp:2557
#define PUGI__SCANWHILE(X)
Definition pugixml.cpp:2794
PUGI__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)
Definition pugixml.cpp:4264
PUGI__FN void truncate_zeros(char *begin, char *end)
Definition pugixml.cpp:8331
axis_t
Definition pugixml.cpp:9524
@ axis_preceding
Definition pugixml.cpp:9535
@ axis_ancestor_or_self
Definition pugixml.cpp:9526
@ axis_attribute
Definition pugixml.cpp:9527
@ axis_following_sibling
Definition pugixml.cpp:9532
@ axis_child
Definition pugixml.cpp:9528
@ axis_descendant
Definition pugixml.cpp:9529
@ axis_descendant_or_self
Definition pugixml.cpp:9530
@ axis_self
Definition pugixml.cpp:9537
@ axis_following
Definition pugixml.cpp:9531
@ axis_ancestor
Definition pugixml.cpp:9525
@ axis_preceding_sibling
Definition pugixml.cpp:9536
@ axis_namespace
Definition pugixml.cpp:9533
@ axis_parent
Definition pugixml.cpp:9534
chartype_t
Definition pugixml.cpp:2030
@ ct_parse_comment
Definition pugixml.cpp:2036
@ ct_start_symbol
Definition pugixml.cpp:2038
@ ct_parse_attr
Definition pugixml.cpp:2032
@ ct_parse_attr_ws
Definition pugixml.cpp:2033
@ ct_parse_cdata
Definition pugixml.cpp:2035
@ ct_parse_pcdata
Definition pugixml.cpp:2031
@ ct_space
Definition pugixml.cpp:2034
@ ct_symbol
Definition pugixml.cpp:2037
PUGI__FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void *contents, size_t size)
Definition pugixml.cpp:2221
PUGI__FN xml_parse_result load_file_impl(xml_document_struct *doc, FILE *file, unsigned int options, xml_encoding encoding, char_t **out_buffer)
Definition pugixml.cpp:4977
void partition3(T *begin, T *end, T pivot, const Pred &pred, T **out_eqbeg, T **out_eqend)
Definition pugixml.cpp:7612
PUGI__FN bool copy_xpath_variable(xpath_variable *lhs, const xpath_variable *rhs)
Definition pugixml.cpp:8860
PUGI__FN const char_t * local_name(const xpath_node &node)
Definition pugixml.cpp:8534
void append_node(xml_node_struct *child, xml_node_struct *node)
Definition pugixml.cpp:1426
PUGI__FN unsigned int get_value_uint(const char_t *value)
Definition pugixml.cpp:4765
PUGI__FN void convert_number_to_mantissa_exponent(double value, char(&buffer)[32], char **out_mantissa, int *out_exponent)
Definition pugixml.cpp:8354
PUGI__FN bool get_value_bool(const char_t *value)
Definition pugixml.cpp:4788
PUGI__NS_END static PUGI__NS_BEGIN const size_t xpath_memory_page_size
Definition pugixml.cpp:7672
PUGI__FN bool strequal(const char_t *src, const char_t *dst)
Definition pugixml.cpp:265
PUGI__FN std::basic_string< wchar_t > as_wide_impl(const char *str, size_t size)
Definition pugixml.cpp:2514
chartypex_t
Definition pugixml.cpp:2063
@ ctx_digit
Definition pugixml.cpp:2067
@ ctx_special_attr
Definition pugixml.cpp:2065
@ ctx_symbol
Definition pugixml.cpp:2068
@ ctx_start_symbol
Definition pugixml.cpp:2066
@ ctx_special_pcdata
Definition pugixml.cpp:2064
PUGI__FN char_t * strconv_cdata(char_t *s, char_t endch)
Definition pugixml.cpp:2828
PUGI__NS_BEGIN PUGI__FN void * default_allocate(size_t size)
Definition pugixml.cpp:220
bool strcpy_insitu_allow(size_t length, const Header &header, uintptr_t header_mask, char_t *target)
Definition pugixml.cpp:2540
PUGI__FN char_t tolower_ascii(char_t ch)
Definition pugixml.cpp:8039
PUGI__FN void text_output_escaped(xml_buffered_writer &writer, const char_t *s, chartypex_t type)
Definition pugixml.cpp:4107
void insert_attribute_after(xml_attribute_struct *attr, xml_attribute_struct *place, xml_node_struct *node)
Definition pugixml.cpp:1553
#define PUGIXML_NOEXCEPT_IF_NOT_COMPACT
Definition pugixml.hpp:102
#define PUGIXML_FUNCTION
Definition pugixml.hpp:63
#define PUGIXML_NOEXCEPT
Definition pugixml.hpp:85
#define PUGIXML_TEXT(t)
Definition pugixml.hpp:121
auto_deleter(T *data_, D deleter_)
Definition pugixml.cpp:320
T * release()
Definition pugixml.cpp:332
void(* D)(T *)
Definition pugixml.cpp:313
static const axis_t axis
Definition pugixml.cpp:9570
bool operator()(const xpath_node &lhs, const xpath_node &rhs) const
Definition pugixml.cpp:8205
bool operator()(const xpath_node &lhs, const xpath_node &rhs) const
Definition pugixml.cpp:8259
bool operator()(const T &lhs, const T &rhs) const
Definition pugixml.cpp:7505
char_t * end
Definition pugixml.cpp:2607
gap()
Definition pugixml.cpp:2610
char_t * flush(char_t *s)
Definition pugixml.cpp:2633
void push(char_t *&s, size_t count)
Definition pugixml.cpp:2616
size_t size
Definition pugixml.cpp:2608
static Traits::value_type process(const uint8_t *data, size_t size, typename Traits::value_type result, Traits)
Definition pugixml.cpp:1973
static value_type high(value_type result, uint32_t ch)
Definition pugixml.cpp:1804
static value_type low(value_type result, uint32_t ch)
Definition pugixml.cpp:1797
uint8_t * value_type
Definition pugixml.cpp:1795
bool operator()(const T &lhs, const T &rhs) const
Definition pugixml.cpp:7529
bool operator()(const T &lhs, const T &rhs) const
Definition pugixml.cpp:7521
xml_node_struct * node
Definition pugixml.cpp:5220
name_null_sentry(xml_node_struct *node_)
Definition pugixml.cpp:5223
namespace_uri_predicate(const char_t *name)
Definition pugixml.cpp:8547
const char_t * prefix
Definition pugixml.cpp:8544
bool operator()(xml_attribute a) const
Definition pugixml.cpp:8555
bool operator()(const T &lhs, const T &rhs) const
Definition pugixml.cpp:7513
xml_attribute_struct * prev_attribute_c
Definition pugixml.cpp:1297
xml_attribute_struct(impl::xml_memory_page *page)
Definition pugixml.cpp:1287
xml_attribute_struct * next_attribute
Definition pugixml.cpp:1298
xml_attribute_struct * first_attribute
Definition pugixml.cpp:1320
xml_node_struct * prev_sibling_c
Definition pugixml.cpp:1317
xml_node_struct * next_sibling
Definition pugixml.cpp:1318
xml_node_struct * parent
Definition pugixml.cpp:1313
xml_node_struct * first_child
Definition pugixml.cpp:1315
xml_node_struct(impl::xml_memory_page *page, xml_node_type type)
Definition pugixml.cpp:1303
const char * description() const
Definition pugixml.cpp:7007
xml_parse_status status
Definition pugixml.hpp:983
const char * description() const
static char_t * parse_eol(char_t *s, char_t end_quote)
Definition pugixml.cpp:3019
static char_t * parse_simple(char_t *s, char_t end_quote)
Definition pugixml.cpp:3051
static char_t * parse_wnorm(char_t *s, char_t end_quote)
Definition pugixml.cpp:2931
static char_t * parse_wconv(char_t *s, char_t end_quote)
Definition pugixml.cpp:2983
static char_t * parse(char_t *s)
Definition pugixml.cpp:2860
size_t value_type
Definition pugixml.cpp:1711
static value_type low(value_type result, uint32_t)
Definition pugixml.cpp:1713
static value_type high(value_type result, uint32_t)
Definition pugixml.cpp:1718
uint16_t type
Definition pugixml.cpp:1884
static Traits::value_type process(const uint16_t *data, size_t size, typename Traits::value_type result, Traits)
Definition pugixml.cpp:1886
static value_type high(value_type result, uint32_t ch)
Definition pugixml.cpp:1735
uint16_t * value_type
Definition pugixml.cpp:1726
static value_type low(value_type result, uint32_t ch)
Definition pugixml.cpp:1728
static value_type any(value_type result, uint32_t ch)
Definition pugixml.cpp:1746
static value_type low(value_type result, uint32_t)
Definition pugixml.cpp:1756
size_t value_type
Definition pugixml.cpp:1754
static value_type high(value_type result, uint32_t)
Definition pugixml.cpp:1761
uint32_t type
Definition pugixml.cpp:1936
static Traits::value_type process(const uint32_t *data, size_t size, typename Traits::value_type result, Traits)
Definition pugixml.cpp:1938
static value_type low(value_type result, uint32_t ch)
Definition pugixml.cpp:1771
uint32_t * value_type
Definition pugixml.cpp:1769
static value_type high(value_type result, uint32_t ch)
Definition pugixml.cpp:1778
static value_type any(value_type result, uint32_t ch)
Definition pugixml.cpp:1785
static value_type low(value_type result, uint32_t ch)
Definition pugixml.cpp:1647
static value_type high(value_type result, uint32_t)
Definition pugixml.cpp:1657
size_t value_type
Definition pugixml.cpp:1645
static Traits::value_type process(const uint8_t *data, size_t size, typename Traits::value_type result, Traits)
Definition pugixml.cpp:1818
uint8_t type
Definition pugixml.cpp:1816
static value_type any(value_type result, uint32_t ch)
Definition pugixml.cpp:1703
static value_type high(value_type result, uint32_t ch)
Definition pugixml.cpp:1693
static value_type low(value_type result, uint32_t ch)
Definition pugixml.cpp:1668
uint8_t * value_type
Definition pugixml.cpp:1666
static Traits::value_type process(const wchar_t *data, size_t size, typename Traits::value_type result, Traits traits)
Definition pugixml.cpp:2011
utf16_counter counter
Definition pugixml.cpp:1991
utf16_decoder< opt_false > decoder
Definition pugixml.cpp:1993
utf16_writer writer
Definition pugixml.cpp:1992
utf32_writer writer
Definition pugixml.cpp:2000
utf32_counter counter
Definition pugixml.cpp:1999
utf32_decoder< opt_false > decoder
Definition pugixml.cpp:2001
void * allocate_memory(size_t size, xml_memory_page *&out_page)
Definition pugixml.cpp:646
void * allocate_object(size_t size, xml_memory_page *&out_page)
Definition pugixml.cpp:704
char_t * allocate_string(size_t length)
Definition pugixml.cpp:762
static void deallocate_page(xml_memory_page *page)
Definition pugixml.cpp:636
void * allocate_memory_oob(size_t size, xml_memory_page *&out_page)
Definition pugixml.cpp:839
xml_memory_page * _root
Definition pugixml.cpp:830
size_t _busy_size
Definition pugixml.cpp:831
void deallocate_memory(void *ptr, size_t size, xml_memory_page *page)
Definition pugixml.cpp:711
bool reserve()
Definition pugixml.cpp:821
xml_memory_page * allocate_page(size_t data_size)
Definition pugixml.cpp:616
xml_allocator(xml_memory_page *root)
Definition pugixml.cpp:608
void deallocate_string(char_t *string)
Definition pugixml.cpp:796
const char_t * buffer
Definition pugixml.cpp:1340
xml_document_struct(xml_memory_page *page)
Definition pugixml.cpp:1336
xml_extra_buffer * extra_buffers
Definition pugixml.cpp:1342
xml_extra_buffer * next
Definition pugixml.cpp:1330
static deallocation_function deallocate
Definition pugixml.cpp:234
static allocation_function allocate
Definition pugixml.cpp:233
xml_memory_page * prev
Definition pugixml.cpp:575
static xml_memory_page * construct(void *memory)
Definition pugixml.cpp:553
xml_memory_page * next
Definition pugixml.cpp:576
size_t freed_size
Definition pugixml.cpp:579
xml_allocator * allocator
Definition pugixml.cpp:573
static char_t * parse_skip_bom(char_t *s)
Definition pugixml.cpp:3688
xml_parse_status error_status
Definition pugixml.cpp:3117
xml_parser(xml_allocator *alloc_)
Definition pugixml.cpp:3119
char_t * parse_doctype_primitive(char_t *s)
Definition pugixml.cpp:3130
char_t * error_offset
Definition pugixml.cpp:3116
char_t * parse_tree(char_t *s, xml_node_struct *root, unsigned int optmsk, char_t endch)
Definition pugixml.cpp:3443
xml_allocator * alloc
Definition pugixml.cpp:3115
static xml_parse_result parse(char_t *buffer, size_t length, xml_document_struct *xmldoc, xml_node_struct *root, unsigned int optmsk)
Definition pugixml.cpp:3706
static bool has_element_node_siblings(xml_node_struct *node)
Definition pugixml.cpp:3694
char_t * parse_doctype_group(char_t *s, char_t endch)
Definition pugixml.cpp:3194
char_t * parse_question(char_t *s, xml_node_struct *&ref_cursor, unsigned int optmsk, char_t endch)
Definition pugixml.cpp:3349
char_t * parse_doctype_ignore(char_t *s)
Definition pugixml.cpp:3163
char_t * parse_exclamation(char_t *s, xml_node_struct *cursor, unsigned int optmsk, char_t endch)
Definition pugixml.cpp:3240
static xml_stream_chunk * create()
Definition pugixml.cpp:5014
T data[xml_memory_page_size/sizeof(T)]
Definition pugixml.cpp:5042
static void destroy(xml_stream_chunk *chunk)
Definition pugixml.cpp:5022
xml_stream_chunk * next
Definition pugixml.cpp:5039
xpath_allocator _state
Definition pugixml.cpp:7835
xpath_allocator * _target
Definition pugixml.cpp:7834
xpath_allocator_capture(xpath_allocator *alloc)
Definition pugixml.cpp:7825
void * reallocate(void *ptr, size_t old_size, size_t new_size)
Definition pugixml.cpp:7741
void * allocate(size_t size)
Definition pugixml.cpp:7704
xpath_memory_block * _root
Definition pugixml.cpp:7696
void revert(const xpath_allocator &state)
Definition pugixml.cpp:7788
xpath_allocator(xpath_memory_block *root, bool *error=0)
Definition pugixml.cpp:7700
xpath_context(const xpath_node &n_, size_t position_, size_t size_)
Definition pugixml.cpp:9099
xpath_node n
Definition pugixml.cpp:9096
size_t position
Definition pugixml.cpp:9097
const char_t * begin
Definition pugixml.cpp:9137
const char_t * end
Definition pugixml.cpp:9138
bool operator==(const char_t *other) const
Definition pugixml.cpp:9144
char data[xpath_memory_page_size]
Definition pugixml.cpp:7689
xpath_memory_block * next
Definition pugixml.cpp:7684
xpath_value_type rettype
binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_)
static binary_op_t parse(xpath_lexer &lexer)
xpath_ast_node * error(const char *message)
xpath_ast_node * parse_step(xpath_ast_node *set)
xpath_ast_node * error_oom()
xpath_variable_set * _variables
xpath_parser(const char_t *query, xpath_variable_set *variables, xpath_allocator *alloc, xpath_parse_result *result)
xpath_ast_node * alloc_node(ast_type_t type, xpath_ast_node *left, axis_t axis, nodetest_t test, const char_t *contents)
xpath_ast_node * parse_primary_expression()
xpath_ast_node * alloc_node(ast_type_t type, xpath_ast_node *left, xpath_ast_node *right, predicate_t test)
xpath_lexer _lexer
xpath_ast_node * alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node *left=0, xpath_ast_node *right=0)
xpath_ast_node * parse_filter_expression()
xpath_ast_node * parse_expression(int limit=0)
nodetest_t parse_node_test_type(const xpath_lexer_string &name)
const char_t * alloc_string(const xpath_lexer_string &value)
void * alloc_node()
xpath_parse_result * _result
xpath_ast_node * parse_function(const xpath_lexer_string &name, size_t argc, xpath_ast_node *args[2])
xpath_ast_node * alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable *value)
xpath_ast_node * parse()
xpath_ast_node * alloc_node(ast_type_t type, xpath_value_type rettype, double value)
char_t _scratch[32]
static xpath_ast_node * parse(const char_t *query, xpath_variable_set *variables, xpath_allocator *alloc, xpath_parse_result *result)
const char_t * _query
xpath_ast_node * parse_path_or_unary_expression()
xpath_allocator * _alloc
axis_t parse_axis_name(const xpath_lexer_string &name, bool &specified)
xpath_ast_node * parse_expression_rec(xpath_ast_node *lhs, int limit)
xpath_ast_node * parse_relative_location_path(xpath_ast_node *set)
xpath_ast_node * alloc_node(ast_type_t type, xpath_value_type rettype, const char_t *value)
xpath_ast_node * parse_location_path()
xpath_memory_block block
static void destroy(xpath_query_impl *impl)
xpath_ast_node * root
static xpath_query_impl * create()
xpath_allocator alloc
xpath_allocator temp
Definition pugixml.cpp:7848
xpath_memory_block blocks[2]
Definition pugixml.cpp:7846
xpath_allocator result
Definition pugixml.cpp:7847
xpath_stack stack
Definition pugixml.cpp:7849
xpath_allocator * temp
Definition pugixml.cpp:7841
xpath_allocator * result
Definition pugixml.cpp:7840
xpath_node_set value
Definition pugixml.cpp:8767