Merge branch 'master' into compact

This commit is contained in:
Arseny Kapoulkine 2014-10-21 23:23:17 -07:00
commit a55dfd3afa

View File

@ -7477,9 +7477,9 @@ PUGI__NS_BEGIN
return node_is_before_sibling(ln, rn); return node_is_before_sibling(ln, rn);
} }
PUGI__FN bool node_is_ancestor(xml_node parent, xml_node node) PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node)
{ {
while (node && node != parent) node = node.parent(); while (node && node != parent) node = node->parent;
return parent && node == parent; return parent && node == parent;
} }
@ -8957,6 +8957,11 @@ PUGI__NS_BEGIN
return false; return false;
} }
static bool eval_once(bool forward, nodeset_eval_t eval)
{
return forward ? eval != nodeset_eval_all : eval == nodeset_eval_any;
}
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) 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)
{ {
xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();
@ -9028,7 +9033,7 @@ PUGI__NS_BEGIN
} }
} }
static void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) static void apply_predicate(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
{ {
assert(ns.size() >= first); assert(ns.size() >= first);
@ -9045,37 +9050,47 @@ PUGI__NS_BEGIN
if (expr->rettype() == xpath_type_number) if (expr->rettype() == xpath_type_number)
{ {
if (expr->eval_number(c, stack) == i) if (expr->eval_number(c, stack) == i)
{
*last++ = *it; *last++ = *it;
if (once) break;
}
} }
else if (expr->eval_boolean(c, stack)) else if (expr->eval_boolean(c, stack))
{
*last++ = *it; *last++ = *it;
if (once) break;
}
} }
ns.truncate(last); ns.truncate(last);
} }
void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack) void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval)
{ {
if (ns.size() == first) return; if (ns.size() == first) return;
bool last_once = eval_once(ns.type() == xpath_node_set::type_sorted, eval);
for (xpath_ast_node* pred = _right; pred; pred = pred->_next) for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
{ {
apply_predicate(ns, first, pred->_left, stack); apply_predicate(ns, first, pred->_left, stack, !pred->_next && last_once);
} }
} }
bool step_push(xpath_node_set_raw& ns, const xml_attribute a, const xml_node parent, xpath_allocator* alloc) bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc)
{ {
if (!a) return false; assert(a);
const char_t* name = a.name(); const char_t* name = a->name ? a->name : PUGIXML_TEXT("");
switch (_test) switch (_test)
{ {
case nodetest_name: case nodetest_name:
if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) if (strequal(name, _data.nodetest) && is_xpath_attribute(name))
{ {
ns.push_back(xpath_node(a, parent), alloc); ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
return true; return true;
} }
break; break;
@ -9084,7 +9099,7 @@ PUGI__NS_BEGIN
case nodetest_all: case nodetest_all:
if (is_xpath_attribute(name)) if (is_xpath_attribute(name))
{ {
ns.push_back(xpath_node(a, parent), alloc); ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
return true; return true;
} }
break; break;
@ -9092,7 +9107,7 @@ PUGI__NS_BEGIN
case nodetest_all_in_namespace: case nodetest_all_in_namespace:
if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) if (starts_with(name, _data.nodetest) && is_xpath_attribute(name))
{ {
ns.push_back(xpath_node(a, parent), alloc); ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
return true; return true;
} }
break; break;
@ -9104,68 +9119,70 @@ PUGI__NS_BEGIN
return false; return false;
} }
bool step_push(xpath_node_set_raw& ns, const xml_node n, xpath_allocator* alloc) bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc)
{ {
if (!n) return false; assert(n);
xml_node_type type = PUGI__NODETYPE(n);
switch (_test) switch (_test)
{ {
case nodetest_name: case nodetest_name:
if (n.type() == node_element && strequal(n.name(), _data.nodetest)) if (type == node_element && n->name && strequal(n->name, _data.nodetest))
{ {
ns.push_back(n, alloc); ns.push_back(xml_node(n), alloc);
return true; return true;
} }
break; break;
case nodetest_type_node: case nodetest_type_node:
ns.push_back(n, alloc); ns.push_back(xml_node(n), alloc);
return true; return true;
case nodetest_type_comment: case nodetest_type_comment:
if (n.type() == node_comment) if (type == node_comment)
{ {
ns.push_back(n, alloc); ns.push_back(xml_node(n), alloc);
return true; return true;
} }
break; break;
case nodetest_type_text: case nodetest_type_text:
if (n.type() == node_pcdata || n.type() == node_cdata) if (type == node_pcdata || type == node_cdata)
{ {
ns.push_back(n, alloc); ns.push_back(xml_node(n), alloc);
return true; return true;
} }
break; break;
case nodetest_type_pi: case nodetest_type_pi:
if (n.type() == node_pi) if (type == node_pi)
{ {
ns.push_back(n, alloc); ns.push_back(xml_node(n), alloc);
return true; return true;
} }
break; break;
case nodetest_pi: case nodetest_pi:
if (n.type() == node_pi && strequal(n.name(), _data.nodetest)) if (type == node_pi && n->name && strequal(n->name, _data.nodetest))
{ {
ns.push_back(n, alloc); ns.push_back(xml_node(n), alloc);
return true; return true;
} }
break; break;
case nodetest_all: case nodetest_all:
if (n.type() == node_element) if (type == node_element)
{ {
ns.push_back(n, alloc); ns.push_back(xml_node(n), alloc);
return true; return true;
} }
break; break;
case nodetest_all_in_namespace: case nodetest_all_in_namespace:
if (n.type() == node_element && starts_with(n.name(), _data.nodetest)) if (type == node_element && n->name && starts_with(n->name, _data.nodetest))
{ {
ns.push_back(n, alloc); ns.push_back(xml_node(n), alloc);
return true; return true;
} }
break; break;
@ -9177,7 +9194,7 @@ PUGI__NS_BEGIN
return false; return false;
} }
template <class T> void step_fill(xpath_node_set_raw& ns, const xml_node n, xpath_allocator* alloc, bool once, T) template <class T> void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T)
{ {
const axis_t axis = T::axis; const axis_t axis = T::axis;
@ -9185,7 +9202,7 @@ PUGI__NS_BEGIN
{ {
case axis_attribute: case axis_attribute:
{ {
for (xml_attribute a = n.first_attribute(); a; a = a.next_attribute()) for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute)
if (step_push(ns, a, n, alloc) & once) if (step_push(ns, a, n, alloc) & once)
return; return;
@ -9194,7 +9211,7 @@ PUGI__NS_BEGIN
case axis_child: case axis_child:
{ {
for (xml_node c = n.first_child(); c; c = c.next_sibling()) for (xml_node_struct* c = n->first_child; c; c = c->next_sibling)
if (step_push(ns, c, alloc) & once) if (step_push(ns, c, alloc) & once)
return; return;
@ -9208,23 +9225,25 @@ PUGI__NS_BEGIN
if (step_push(ns, n, alloc) & once) if (step_push(ns, n, alloc) & once)
return; return;
xml_node cur = n.first_child(); xml_node_struct* cur = n->first_child;
while (cur && cur != n) while (cur)
{ {
if (step_push(ns, cur, alloc) & once) if (step_push(ns, cur, alloc) & once)
return; return;
if (cur.first_child()) if (cur->first_child)
cur = cur.first_child(); cur = cur->first_child;
else if (cur.next_sibling())
cur = cur.next_sibling();
else else
{ {
while (!cur.next_sibling() && cur != n) while (!cur->next_sibling)
cur = cur.parent(); {
cur = cur->parent;
if (cur != n) cur = cur.next_sibling(); if (cur == n) return;
}
cur = cur->next_sibling;
} }
} }
@ -9233,7 +9252,7 @@ PUGI__NS_BEGIN
case axis_following_sibling: case axis_following_sibling:
{ {
for (xml_node c = n.next_sibling(); c; c = c.next_sibling()) for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling)
if (step_push(ns, c, alloc) & once) if (step_push(ns, c, alloc) & once)
return; return;
@ -9242,7 +9261,7 @@ PUGI__NS_BEGIN
case axis_preceding_sibling: case axis_preceding_sibling:
{ {
for (xml_node c = n.previous_sibling(); c; c = c.previous_sibling()) for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c)
if (step_push(ns, c, alloc) & once) if (step_push(ns, c, alloc) & once)
return; return;
@ -9251,27 +9270,35 @@ PUGI__NS_BEGIN
case axis_following: case axis_following:
{ {
xml_node cur = n; xml_node_struct* cur = n;
// exit from this node so that we don't include descendants // exit from this node so that we don't include descendants
while (cur && !cur.next_sibling()) cur = cur.parent(); while (!cur->next_sibling)
cur = cur.next_sibling(); {
cur = cur->parent;
if (!cur) return;
}
cur = cur->next_sibling;
while (cur) while (cur)
{ {
if (step_push(ns, cur, alloc) & once) if (step_push(ns, cur, alloc) & once)
return; return;
if (cur.first_child()) if (cur->first_child)
cur = cur.first_child(); cur = cur->first_child;
else if (cur.next_sibling())
cur = cur.next_sibling();
else else
{ {
while (cur && !cur.next_sibling()) cur = cur.parent(); while (!cur->next_sibling)
cur = cur.next_sibling(); {
cur = cur->parent;
if (!cur) break; if (!cur) return;
}
cur = cur->next_sibling;
} }
} }
@ -9280,40 +9307,40 @@ PUGI__NS_BEGIN
case axis_preceding: case axis_preceding:
{ {
xml_node cur = n; xml_node_struct* cur = n;
while (cur && !cur.previous_sibling()) cur = cur.parent(); // exit from this node so that we don't include descendants
cur = cur.previous_sibling(); while (!cur->prev_sibling_c->next_sibling)
{
cur = cur->parent;
if (!cur) return;
}
cur = cur->prev_sibling_c;
while (cur) while (cur)
{ {
if (cur.last_child()) if (cur->first_child)
cur = cur.last_child(); cur = cur->first_child->prev_sibling_c;
else else
{ {
// leaf node, can't be ancestor // leaf node, can't be ancestor
if (step_push(ns, cur, alloc) & once) if (step_push(ns, cur, alloc) & once)
return; return;
if (cur.previous_sibling()) while (!cur->prev_sibling_c->next_sibling)
cur = cur.previous_sibling();
else
{ {
do cur = cur->parent;
{
cur = cur.parent();
if (!cur) break;
if (!node_is_ancestor(cur, n)) if (!cur) return;
if (step_push(ns, cur, alloc) & once)
return;
}
while (!cur.previous_sibling());
cur = cur.previous_sibling(); if (!node_is_ancestor(cur, n))
if (step_push(ns, cur, alloc) & once)
if (!cur) break; return;
} }
cur = cur->prev_sibling_c;
} }
} }
@ -9327,14 +9354,14 @@ PUGI__NS_BEGIN
if (step_push(ns, n, alloc) & once) if (step_push(ns, n, alloc) & once)
return; return;
xml_node cur = n.parent(); xml_node_struct* cur = n->parent;
while (cur) while (cur)
{ {
if (step_push(ns, cur, alloc) & once) if (step_push(ns, cur, alloc) & once)
return; return;
cur = cur.parent(); cur = cur->parent;
} }
break; break;
@ -9349,7 +9376,8 @@ PUGI__NS_BEGIN
case axis_parent: case axis_parent:
{ {
if (n.parent()) step_push(ns, n.parent(), alloc); if (n->parent)
step_push(ns, n->parent, alloc);
break; break;
} }
@ -9359,7 +9387,7 @@ PUGI__NS_BEGIN
} }
} }
template <class T> void step_fill(xpath_node_set_raw& ns, const xml_attribute a, const xml_node p, xpath_allocator* alloc, bool once, T v) 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)
{ {
const axis_t axis = T::axis; const axis_t axis = T::axis;
@ -9372,14 +9400,14 @@ PUGI__NS_BEGIN
if (step_push(ns, a, p, alloc) & once) if (step_push(ns, a, p, alloc) & once)
return; return;
xml_node cur = p; xml_node_struct* cur = p;
while (cur) while (cur)
{ {
if (step_push(ns, cur, alloc) & once) if (step_push(ns, cur, alloc) & once)
return; return;
cur = cur.parent(); cur = cur->parent;
} }
break; break;
@ -9396,20 +9424,22 @@ PUGI__NS_BEGIN
case axis_following: case axis_following:
{ {
xml_node cur = p; xml_node_struct* cur = p;
for (;;) while (cur)
{ {
if (cur.first_child()) if (cur->first_child)
cur = cur.first_child(); cur = cur->first_child;
else if (cur.next_sibling())
cur = cur.next_sibling();
else else
{ {
while (cur && !cur.next_sibling()) cur = cur.parent(); while (!cur->next_sibling)
cur = cur.next_sibling(); {
cur = cur->parent;
if (!cur) break; if (!cur) return;
}
cur = cur->next_sibling;
} }
if (step_push(ns, cur, alloc) & once) if (step_push(ns, cur, alloc) & once)
@ -9438,16 +9468,26 @@ PUGI__NS_BEGIN
} }
} }
template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) template <class T> void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v)
{ {
const axis_t axis = T::axis; const axis_t axis = T::axis;
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); 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);
if (xn.node())
step_fill(ns, xn.node().internal_object(), alloc, once, v);
else if (axis_has_attributes && xn.attribute() && xn.parent())
step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v);
}
template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v)
{
const axis_t axis = T::axis;
bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);
bool once = bool once =
(axis == axis_attribute && _test == nodetest_name) (axis == axis_attribute && _test == nodetest_name)
? true ? true
: !_right && (axis_reverse ? eval == nodeset_eval_any : eval != nodeset_eval_all); : !_right && eval_once(!axis_reverse, eval);
xpath_node_set_raw ns; xpath_node_set_raw ns;
ns.set_type(axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted); ns.set_type(axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted);
@ -9466,22 +9506,14 @@ PUGI__NS_BEGIN
// in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes
if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);
if (it->node()) step_fill(ns, *it, stack.result, once, v);
step_fill(ns, it->node(), stack.result, once, v); apply_predicates(ns, size, stack, eval);
else if (axis_has_attributes && it->attribute() && it->parent())
step_fill(ns, it->attribute(), it->parent(), stack.result, once, v);
apply_predicates(ns, size, stack);
} }
} }
else else
{ {
if (c.n.node()) step_fill(ns, c.n, stack.result, once, v);
step_fill(ns, c.n.node(), stack.result, once, v); apply_predicates(ns, 0, stack, eval);
else if (axis_has_attributes && c.n.attribute() && c.n.parent())
step_fill(ns, c.n.attribute(), c.n.parent(), stack.result, once, v);
apply_predicates(ns, 0, stack);
} }
// child, attribute and self axes always generate unique set of nodes // child, attribute and self axes always generate unique set of nodes
@ -10116,7 +10148,9 @@ PUGI__NS_BEGIN
// either expression is a number or it contains position() call; sort by document order // either expression is a number or it contains position() call; sort by document order
if (_type == ast_filter) set.sort_do(); if (_type == ast_filter) set.sort_do();
apply_predicate(set, 0, _right, stack); bool once = eval_once(set.type() == xpath_node_set::type_sorted, eval);
apply_predicate(set, 0, _right, stack, once);
return set; return set;
} }