From 4363e8a651c2ca24d7fc41e5707bc44ed102e94a Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 25 Oct 2014 03:17:55 +0000 Subject: Remove redundant null pointer checks. When removing a node or attribute, we know that the parent has at least one node/attribute so a null pointer check is redundant. git-svn-id: https://pugixml.googlecode.com/svn/trunk@1078 99668b35-9821-0410-8761-19e4c4f06640 --- src/pugixml.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/pugixml.cpp') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index abe7adf..1187383 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -700,7 +700,7 @@ PUGI__NS_BEGIN if (node->next_sibling) node->next_sibling->prev_sibling_c = node->prev_sibling_c; - else if (parent->first_child) + else parent->first_child->prev_sibling_c = node->prev_sibling_c; if (node->prev_sibling_c->next_sibling) @@ -775,7 +775,7 @@ PUGI__NS_BEGIN { if (attr->next_attribute) attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; - else if (node->first_attribute) + else node->first_attribute->prev_attribute_c = attr->prev_attribute_c; if (attr->prev_attribute_c->next_attribute) -- cgit v1.2.3 From 0b7964917cbfd9d007c8a017eaced636f53ddeb3 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sat, 25 Oct 2014 16:37:16 +0000 Subject: Fix node copying for some out of memory cases A page can fail to allocate during attribute creation; this case was not previously handled. git-svn-id: https://pugixml.googlecode.com/svn/trunk@1080 99668b35-9821-0410-8761-19e4c4f06640 --- src/pugixml.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/pugixml.cpp') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 1187383..109a635 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -164,6 +164,8 @@ PUGI__NS_BEGIN static deallocation_function deallocate; }; + // Global allocation functions are stored in class statics so that in header mode linker deduplicates them + // Without a template<> we'll get multiple definitions of the same static template allocation_function xml_memory_management_function_storage::allocate = default_allocate; template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; @@ -3735,8 +3737,11 @@ PUGI__NS_BEGIN { xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); - node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); - node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + if (da) + { + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } } } -- cgit v1.2.3 From d5e29292c67c24b5cd7ac4f6afce2f8ec293144a Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 26 Oct 2014 09:37:18 -0700 Subject: XPath: Optimize constant filters/predicates If a filter/predicate expression is a constant, we don't need to evaluate it for every nodeset element - we can evaluate it once and pick the right element or keep/discard the entire collection. If the expression is 1, we can early out on first node when evaluating the node set - queries like following::item[1] are now significantly faster. Additionally this change refactors filters/predicates to have additional metadata describing the expression type in _test field that is filled during optimization. Note that predicate_constant selection right now is very simple (but captures most common use cases except for maybe [last()]). --- src/pugixml.cpp | 116 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 95 insertions(+), 21 deletions(-) (limited to 'src/pugixml.cpp') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 109a635..ac36a31 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -1,4 +1,5 @@ /** +{ * pugixml parser - version 1.4 * -------------------------------------------------------- * Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) @@ -8199,7 +8200,6 @@ PUGI__NS_BEGIN ast_op_union, // left | right ast_predicate, // apply predicate to set; next points to next predicate ast_filter, // select * from left where right - ast_filter_posinv, // select * from left where right; proximity position invariant ast_string_constant, // string constant ast_number_constant, // number constant ast_variable, // variable @@ -8275,6 +8275,14 @@ PUGI__NS_BEGIN nodetest_all_in_namespace }; + enum predicate_t + { + predicate_default, + predicate_posinv, + predicate_constant, + predicate_constant_one + }; + enum nodeset_eval_t { nodeset_eval_all, @@ -8296,8 +8304,10 @@ PUGI__NS_BEGIN char _type; char _rettype; - // for ast_step / ast_predicate + // for ast_step char _axis; + + // for ast_step/ast_predicate/ast_filter char _test; // tree node structure @@ -8494,7 +8504,7 @@ PUGI__NS_BEGIN size_t size = ns.size() - first; xpath_node* last = ns.begin() + first; - + // remove_if... or well, sort of for (xpath_node* it = last; it != ns.end(); ++it, ++i) { @@ -8509,17 +8519,57 @@ PUGI__NS_BEGIN if (once) break; } } - else if (expr->eval_boolean(c, stack)) + else { - *last++ = *it; + if (expr->eval_boolean(c, stack)) + { + *last++ = *it; - if (once) break; + if (once) break; + } } } ns.truncate(last); } + static void apply_predicate_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) + { + assert(ns.size() >= first); + + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + xpath_context c(xpath_node(), 1, size); + + if (expr->rettype() == xpath_type_number) + { + double er = expr->eval_number(c, stack); + + if (er >= 1.0 && er <= size) + { + size_t eri = static_cast(er); + + if (er == eri) + { + xpath_node r = last[eri - 1]; + + *last++ = r; + } + } + } + else + { + if (expr->eval_boolean(c, stack)) + { + last += size; + } + } + + ns.truncate(last); + } + void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) { if (ns.size() == first) return; @@ -8528,7 +8578,12 @@ PUGI__NS_BEGIN for (xpath_ast_node* pred = _right; pred; pred = pred->_next) { - apply_predicate(ns, first, pred->_left, stack, !pred->_next && last_once); + assert(pred->_type == ast_predicate); + + if (pred->_test == predicate_constant || pred->_test == predicate_constant_one) + apply_predicate_const(ns, first, pred->_left, stack); + else + apply_predicate(ns, first, pred->_left, stack, !pred->_next && last_once); } } @@ -8938,9 +8993,9 @@ PUGI__NS_BEGIN bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); bool once = - (axis == axis_attribute && _test == nodetest_name) - ? true - : !_right && eval_once(!axis_reverse, eval); + (axis == axis_attribute && _test == nodetest_name) || + (!_right && eval_once(!axis_reverse, eval)) || + (_right && !_right->_next && _right->_test == predicate_constant_one); xpath_node_set_raw ns; ns.set_type(axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted); @@ -9007,6 +9062,7 @@ PUGI__NS_BEGIN xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) { + assert(type == ast_step); _data.nodetest = contents; } @@ -9583,27 +9639,33 @@ PUGI__NS_BEGIN xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); - + // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother rs.set_type(xpath_node_set::type_unsorted); rs.append(ls.begin(), ls.end(), stack.result); rs.remove_duplicates(); - + return rs; } case ast_filter: - case ast_filter_posinv: { - xpath_node_set_raw set = _left->eval_node_set(c, stack, nodeset_eval_all); + xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); // either expression is a number or it contains position() call; sort by document order - if (_type == ast_filter) set.sort_do(); + if (_test != predicate_posinv) set.sort_do(); - bool once = eval_once(set.type() == xpath_node_set::type_sorted, eval); + if (_test == predicate_constant || _test == predicate_constant_one) + { + apply_predicate_const(set, 0, _right, stack); + } + else + { + bool once = eval_once(set.type() == xpath_node_set::type_sorted, eval); - apply_predicate(set, 0, _right, stack, once); + apply_predicate(set, 0, _right, stack, once); + } return set; } @@ -9706,6 +9768,21 @@ PUGI__NS_BEGIN if (_right) _right->optimize(alloc); if (_next) _next->optimize(alloc); + // Classify filter/predicate ops to perform various optimizations during evaluation + if (_type == ast_filter || _type == ast_predicate) + { + xpath_ast_node* expr = (_type == ast_filter) ? _right : _left; + + if (expr->_type == ast_number_constant && expr->_data.number == 1.0) + _test = predicate_constant_one; + else if (expr->_type == ast_number_constant || expr->_type == ast_variable) + _test = predicate_constant; + else if (expr->rettype() != xpath_type_number && expr->is_posinv_expr()) + _test = predicate_posinv; + else + _test = predicate_default; + } + // Replace descendant-or-self::node()/child::foo with descendant::foo // The former is a full form of //foo, the latter is much faster since it executes the node test immediately // Do a similar kind of replacement for self/descendant/descendant-or-self axes @@ -9762,7 +9839,6 @@ PUGI__NS_BEGIN case ast_predicate: case ast_filter: - case ast_filter_posinv: return true; default: @@ -10206,9 +10282,7 @@ PUGI__NS_BEGIN if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); - bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv_expr(); - - n = new (alloc_node()) xpath_ast_node(posinv ? ast_filter_posinv : ast_filter, xpath_type_node_set, n, expr); + n = new (alloc_node()) xpath_ast_node(ast_filter, xpath_type_node_set, n, expr); if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); -- cgit v1.2.3 From 11ce004e040937e17755b3bdf2a1d7f846214f3f Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 26 Oct 2014 10:07:01 -0700 Subject: XPath: Unify ast_filter/ast_predicate structure Now expression is always _right for filter/predicate nodes to make optimize() simpler. Additionally we now use predicate metadata to make is_posinv_step() faster. This introduces a weak ordering dependency in rewrite rules to optimize() - classification has to be performed before other optimizations. --- src/pugixml.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'src/pugixml.cpp') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index ac36a31..d66f9f9 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -8581,9 +8581,9 @@ PUGI__NS_BEGIN assert(pred->_type == ast_predicate); if (pred->_test == predicate_constant || pred->_test == predicate_constant_one) - apply_predicate_const(ns, first, pred->_left, stack); + apply_predicate_const(ns, first, pred->_right, stack); else - apply_predicate(ns, first, pred->_left, stack, !pred->_next && last_once); + apply_predicate(ns, first, pred->_right, stack, !pred->_next && last_once); } } @@ -9066,6 +9066,12 @@ PUGI__NS_BEGIN _data.nodetest = contents; } + xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): + _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast(test)), _left(left), _right(right), _next(0) + { + assert(type == ast_filter || type == ast_predicate); + } + void set_next(xpath_ast_node* value) { _next = value; @@ -9771,16 +9777,14 @@ PUGI__NS_BEGIN // Classify filter/predicate ops to perform various optimizations during evaluation if (_type == ast_filter || _type == ast_predicate) { - xpath_ast_node* expr = (_type == ast_filter) ? _right : _left; + assert(_test == predicate_default); - if (expr->_type == ast_number_constant && expr->_data.number == 1.0) + if (_right->_type == ast_number_constant && _right->_data.number == 1.0) _test = predicate_constant_one; - else if (expr->_type == ast_number_constant || expr->_type == ast_variable) + else if (_right->_type == ast_number_constant || _right->_type == ast_variable) _test = predicate_constant; - else if (expr->rettype() != xpath_type_number && expr->is_posinv_expr()) + else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) _test = predicate_posinv; - else - _test = predicate_default; } // Replace descendant-or-self::node()/child::foo with descendant::foo @@ -9814,7 +9818,7 @@ PUGI__NS_BEGIN // Use optimized path for @attr = 'value' or @attr = $value if (_type == ast_op_equal && _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && - (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->rettype() == xpath_type_string))) + (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) { _type = ast_opt_compare_attribute; } @@ -9859,10 +9863,7 @@ PUGI__NS_BEGIN { assert(n->_type == ast_predicate); - xpath_ast_node* expr = n->_left; - bool posinv = expr->rettype() != xpath_type_number && expr->is_posinv_expr(); - - if (!posinv) + if (n->_test != predicate_posinv) return false; } @@ -10282,7 +10283,7 @@ PUGI__NS_BEGIN if (n->rettype() != xpath_type_node_set) throw_error("Predicate has to be applied to node set"); - n = new (alloc_node()) xpath_ast_node(ast_filter, xpath_type_node_set, n, expr); + n = new (alloc_node()) xpath_ast_node(ast_filter, n, expr, predicate_default); if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); @@ -10426,7 +10427,7 @@ PUGI__NS_BEGIN xpath_ast_node* expr = parse_expression(); - xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, xpath_type_node_set, expr); + xpath_ast_node* pred = new (alloc_node()) xpath_ast_node(ast_predicate, 0, expr, predicate_default); if (_lexer.current() != lex_close_square_brace) throw_error("Unmatched square brace"); -- cgit v1.2.3 From dbfe85a717b7ac8b9bd30eb51796811c0c651da9 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Sun, 26 Oct 2014 10:24:57 -0700 Subject: XPath: Only recognize numeric constant expressions Numeric and boolean constant expressions in filters are different in that to evaluate numeric expressions we need a sorted order, but to evaluate boolean expressions we don't. The previously implemented optimization adds an extra sorting step for constant boolean filters that will be more expensive than redundant computations. Since constant booleans are sort of an edge case, don't do this optimization. This allows us to simplify apply_predicate_const to only handle numbers. --- src/pugixml.cpp | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) (limited to 'src/pugixml.cpp') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index d66f9f9..67591a3 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -8536,6 +8536,7 @@ PUGI__NS_BEGIN static void apply_predicate_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) { assert(ns.size() >= first); + assert(expr->rettype() == xpath_type_number); size_t size = ns.size() - first; @@ -8543,27 +8544,17 @@ PUGI__NS_BEGIN xpath_context c(xpath_node(), 1, size); - if (expr->rettype() == xpath_type_number) + double er = expr->eval_number(c, stack); + + if (er >= 1.0 && er <= size) { - double er = expr->eval_number(c, stack); + size_t eri = static_cast(er); - if (er >= 1.0 && er <= size) + if (er == eri) { - size_t eri = static_cast(er); - - if (er == eri) - { - xpath_node r = last[eri - 1]; + xpath_node r = last[eri - 1]; - *last++ = r; - } - } - } - else - { - if (expr->eval_boolean(c, stack)) - { - last += size; + *last++ = r; } } @@ -9781,16 +9772,16 @@ PUGI__NS_BEGIN if (_right->_type == ast_number_constant && _right->_data.number == 1.0) _test = predicate_constant_one; - else if (_right->_type == ast_number_constant || _right->_type == ast_variable) + else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable)) _test = predicate_constant; else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) _test = predicate_posinv; } - // Replace descendant-or-self::node()/child::foo with descendant::foo + // Rewrite descendant-or-self::node()/child::foo with descendant::foo // The former is a full form of //foo, the latter is much faster since it executes the node test immediately - // Do a similar kind of replacement for self/descendant/descendant-or-self axes - // Note that we only replace positionally invariant steps (//foo[1] != /descendant::foo[1]) + // Do a similar kind of rewrite for self/descendant/descendant-or-self axes + // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && is_posinv_step()) @@ -9803,7 +9794,7 @@ PUGI__NS_BEGIN _left = _left->_left; } - // Replace translate() with constant arguments with a table + // Use optimized lookup table implementation for translate() with constant arguments if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) { unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); -- cgit v1.2.3 From c64d4820b142e6df93ccf612d0e4717159a36591 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 27 Oct 2014 18:50:09 -0700 Subject: XPath: Optimize [position()=expr] and [last()] To get more benefits from constant predicate/filter optimization we rewrite [position()=expr] predicates into [expr] for numeric expressions. Right now the rewrite is only for entire expressions - it may be beneficial to split complex expressions like [position()=constant and expr] into [constant][expr] but that is more complicated. last() does not depend on the node set contents so is "constant" as far as our optimization is concerned so we can evaluate it once. --- src/pugixml.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/pugixml.cpp') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index 67591a3..f0bfc31 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -9765,6 +9765,14 @@ PUGI__NS_BEGIN if (_right) _right->optimize(alloc); if (_next) _next->optimize(alloc); + // Rewrite [position()=expr] with [expr] + // Note that this step has to go before classification to recognize [position()=1] + if ((_type == ast_filter || _type == ast_predicate) && + _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) + { + _right = _right->_right; + } + // Classify filter/predicate ops to perform various optimizations during evaluation if (_type == ast_filter || _type == ast_predicate) { @@ -9772,7 +9780,7 @@ PUGI__NS_BEGIN if (_right->_type == ast_number_constant && _right->_data.number == 1.0) _test = predicate_constant_one; - else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable)) + else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) _test = predicate_constant; else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) _test = predicate_posinv; -- cgit v1.2.3 From 6229138d80380d582f16931d36b279807dcb82dd Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine Date: Mon, 27 Oct 2014 22:29:14 -0700 Subject: Optimize node printing by using raw pointers This lets us do fewer null pointer checks (making printing 2% faster with -O3) and removes a lot of function calls (making printing 20% faster with -O0). --- src/pugixml.cpp | 81 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 39 deletions(-) (limited to 'src/pugixml.cpp') diff --git a/src/pugixml.cpp b/src/pugixml.cpp index f0bfc31..13ed4b8 100644 --- a/src/pugixml.cpp +++ b/src/pugixml.cpp @@ -3452,36 +3452,37 @@ PUGI__NS_BEGIN writer.write('-', '-', '>'); } - PUGI__FN void node_output_attributes(xml_buffered_writer& writer, const xml_node node, unsigned int flags) + PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - for (xml_attribute a = node.first_attribute(); a; a = a.next_attribute()) + for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { writer.write(' '); - writer.write_string(a.name()[0] ? a.name() : default_name); + writer.write_string(a->name ? a->name : default_name); writer.write('=', '"'); - text_output(writer, a.value(), ctx_special_attr, flags); + if (a->value) + text_output(writer, a->value, ctx_special_attr, flags); writer.write('"'); } } - PUGI__FN bool node_output_start(xml_buffered_writer& writer, const xml_node node, unsigned int flags) + PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node.name()[0] ? node.name() : default_name; + const char_t* name = node->name ? node->name : default_name; writer.write('<'); writer.write_string(name); - if (node.first_attribute()) + if (node->first_attribute) node_output_attributes(writer, node, flags); if (flags & format_raw) { - if (!node.first_child()) + if (!node->first_child) writer.write(' ', '/', '>'); else { @@ -3492,18 +3493,20 @@ PUGI__NS_BEGIN } else { - xml_node first = node.first_child(); + xml_node_struct* first = node->first_child; if (!first) writer.write(' ', '/', '>', '\n'); - else if (!first.next_sibling() && (first.type() == node_pcdata || first.type() == node_cdata)) + else if (!first->next_sibling && (PUGI__NODETYPE(first) == node_pcdata || PUGI__NODETYPE(first) == node_cdata)) { writer.write('>'); - if (first.type() == node_pcdata) - text_output(writer, first.value(), ctx_special_pcdata, flags); + const char_t* value = first->value ? first->value : PUGIXML_TEXT(""); + + if (PUGI__NODETYPE(first) == node_pcdata) + text_output(writer, value, ctx_special_pcdata, flags); else - text_output_cdata(writer, first.value()); + text_output_cdata(writer, value); writer.write('<', '/'); writer.write_string(name); @@ -3520,10 +3523,10 @@ PUGI__NS_BEGIN return false; } - PUGI__FN void node_output_end(xml_buffered_writer& writer, const xml_node node, unsigned int flags) + PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - const char_t* name = node.name()[0] ? node.name() : default_name; + const char_t* name = node->name ? node->name : default_name; writer.write('<', '/'); writer.write_string(name); @@ -3534,35 +3537,35 @@ PUGI__NS_BEGIN writer.write('>', '\n'); } - PUGI__FN void node_output_simple(xml_buffered_writer& writer, const xml_node node, unsigned int flags) + PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); - switch (node.type()) + switch (PUGI__NODETYPE(node)) { case node_pcdata: - text_output(writer, node.value(), ctx_special_pcdata, flags); + text_output(writer, node->value ? node->value : PUGIXML_TEXT(""), ctx_special_pcdata, flags); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_cdata: - text_output_cdata(writer, node.value()); + text_output_cdata(writer, node->value ? node->value : PUGIXML_TEXT("")); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_comment: - node_output_comment(writer, node.value()); + node_output_comment(writer, node->value ? node->value : PUGIXML_TEXT("")); if ((flags & format_raw) == 0) writer.write('\n'); break; case node_pi: writer.write('<', '?'); - writer.write_string(node.name()[0] ? node.name() : default_name); + writer.write_string(node->name ? node->name : default_name); - if (node.value()[0]) + if (node->value) { writer.write(' '); - writer.write_string(node.value()); + writer.write_string(node->value); } writer.write('?', '>'); @@ -3571,7 +3574,7 @@ PUGI__NS_BEGIN case node_declaration: writer.write('<', '?'); - writer.write_string(node.name()[0] ? node.name() : default_name); + writer.write_string(node->name ? node->name : default_name); node_output_attributes(writer, node, flags); writer.write('?', '>'); if ((flags & format_raw) == 0) writer.write('\n'); @@ -3581,10 +3584,10 @@ PUGI__NS_BEGIN writer.write('<', '!', 'D', 'O', 'C'); writer.write('T', 'Y', 'P', 'E'); - if (node.value()[0]) + if (node->value) { writer.write(' '); - writer.write_string(node.value()); + writer.write_string(node->value); } writer.write('>'); @@ -3596,11 +3599,11 @@ PUGI__NS_BEGIN } } - PUGI__FN void node_output(xml_buffered_writer& writer, const xml_node root, const char_t* indent, unsigned int flags, unsigned int depth) + PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) { size_t indent_length = ((flags & (format_indent | format_raw)) == format_indent) ? strlength(indent) : 0; - xml_node node = root; + xml_node_struct* node = root; do { @@ -3610,20 +3613,20 @@ PUGI__NS_BEGIN if (indent_length) text_output_indent(writer, indent, indent_length, depth); - if (node.type() == node_element) + if (PUGI__NODETYPE(node) == node_element) { if (node_output_start(writer, node, flags)) { - node = node.first_child(); + node = node->first_child; depth++; continue; } } - else if (node.type() == node_document) + else if (PUGI__NODETYPE(node) == node_document) { - if (node.first_child()) + if (node->first_child) { - node = node.first_child(); + node = node->first_child; continue; } } @@ -3635,17 +3638,17 @@ PUGI__NS_BEGIN // continue to the next node while (node != root) { - if (node.next_sibling()) + if (node->next_sibling) { - node = node.next_sibling(); + node = node->next_sibling; break; } - node = node.parent(); + node = node->parent; depth--; // write closing node - if (node.type() == node_element) + if (PUGI__NODETYPE(node) == node_element) { if (indent_length) text_output_indent(writer, indent, indent_length, depth); @@ -5383,7 +5386,7 @@ namespace pugi impl::xml_buffered_writer buffered_writer(writer, encoding); - impl::node_output(buffered_writer, *this, indent, flags, depth); + impl::node_output(buffered_writer, _root, indent, flags, depth); } #ifndef PUGIXML_NO_STL @@ -6074,7 +6077,7 @@ namespace pugi if (!(flags & format_raw)) buffered_writer.write('\n'); } - impl::node_output(buffered_writer, *this, indent, flags, 0); + impl::node_output(buffered_writer, _root, indent, flags, 0); } #ifndef PUGIXML_NO_STL -- cgit v1.2.3