mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2024-10-26 20:25:14 +03:00
Optimization of count(): eliminated sorting (see bug #165547).
* xpath.c: Optimization of count(): eliminated sorting (see bug #165547). Optimization of XPATH_OP_FILTER if the predicate is a [1] (disable with XP_OPTIMIZED_FILTER_FIRST if it produces trouble). Tiny opt in xmlXPathNodeSetMerge().
This commit is contained in:
parent
a512d76edc
commit
5691f436d9
@ -1,3 +1,10 @@
|
|||||||
|
Mon May 22 17:14:00 CEST 2006 Kasimier Buchcik <libxml2-cvs@cazic.net>
|
||||||
|
|
||||||
|
* xpath.c: Optimization of count(): eliminated sorting
|
||||||
|
(see bug #165547). Optimization of XPATH_OP_FILTER if the
|
||||||
|
predicate is a [1] (disable with XP_OPTIMIZED_FILTER_FIRST if
|
||||||
|
it produces trouble). Tiny opt in xmlXPathNodeSetMerge().
|
||||||
|
|
||||||
Mon May 22 13:33:12 CEST 2006 Rob Richards <rrichards@ctindustries.net>
|
Mon May 22 13:33:12 CEST 2006 Rob Richards <rrichards@ctindustries.net>
|
||||||
|
|
||||||
* tree.c: Revert behavior change in xmlSetProp to handle attributes
|
* tree.c: Revert behavior change in xmlSetProp to handle attributes
|
||||||
|
484
xpath.c
484
xpath.c
@ -77,7 +77,7 @@
|
|||||||
#define XP_PATTERN_TO_ANY_NODE_ENABLED
|
#define XP_PATTERN_TO_ANY_NODE_ENABLED
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* XP_FAST_NON_ELEM_COMPARISON:
|
* XP_OPTIMIZED_NON_ELEM_COMPARISON:
|
||||||
* If defined, this will use xmlXPathCmpNodesExt() instead of
|
* If defined, this will use xmlXPathCmpNodesExt() instead of
|
||||||
* xmlXPathCmpNodes(). The new function is optimized comparison of
|
* xmlXPathCmpNodes(). The new function is optimized comparison of
|
||||||
* non-element nodes; actually it will speed up comparison only if
|
* non-element nodes; actually it will speed up comparison only if
|
||||||
@ -85,7 +85,15 @@
|
|||||||
* a tree in document order; Libxslt does such an indexing, thus it will
|
* a tree in document order; Libxslt does such an indexing, thus it will
|
||||||
* benefit from this optimization.
|
* benefit from this optimization.
|
||||||
*/
|
*/
|
||||||
#define XP_FAST_NON_ELEM_COMPARISON
|
#define XP_OPTIMIZED_NON_ELEM_COMPARISON
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XP_OPTIMIZED_FILTER_FIRST:
|
||||||
|
* If defined, this will optimize expressions like "key('foo', 'val')[b][1]"
|
||||||
|
* in a way, that it stop evaluation at the first node.
|
||||||
|
*/
|
||||||
|
#define XP_OPTIMIZED_FILTER_FIRST
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO:
|
* TODO:
|
||||||
* There are a few spots where some tests are done which depend upon ascii
|
* There are a few spots where some tests are done which depend upon ascii
|
||||||
@ -402,15 +410,15 @@ typedef enum {
|
|||||||
XPATH_OP_UNION,
|
XPATH_OP_UNION,
|
||||||
XPATH_OP_ROOT,
|
XPATH_OP_ROOT,
|
||||||
XPATH_OP_NODE,
|
XPATH_OP_NODE,
|
||||||
XPATH_OP_RESET,
|
XPATH_OP_RESET, /* 10 */
|
||||||
XPATH_OP_COLLECT,
|
XPATH_OP_COLLECT,
|
||||||
XPATH_OP_VALUE,
|
XPATH_OP_VALUE, /* 12 */
|
||||||
XPATH_OP_VARIABLE,
|
XPATH_OP_VARIABLE,
|
||||||
XPATH_OP_FUNCTION,
|
XPATH_OP_FUNCTION,
|
||||||
XPATH_OP_ARG,
|
XPATH_OP_ARG,
|
||||||
XPATH_OP_PREDICATE,
|
XPATH_OP_PREDICATE,
|
||||||
XPATH_OP_FILTER,
|
XPATH_OP_FILTER, /* 17 */
|
||||||
XPATH_OP_SORT
|
XPATH_OP_SORT /* 18 */
|
||||||
#ifdef LIBXML_XPTR_ENABLED
|
#ifdef LIBXML_XPTR_ENABLED
|
||||||
,XPATH_OP_RANGETO
|
,XPATH_OP_RANGETO
|
||||||
#endif
|
#endif
|
||||||
@ -1702,7 +1710,7 @@ xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
|
|||||||
return(-1); /* assume there is no sibling list corruption */
|
return(-1); /* assume there is no sibling list corruption */
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef XP_FAST_NON_ELEM_COMPARISON
|
#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
|
||||||
/**
|
/**
|
||||||
* xmlXPathCmpNodesExt:
|
* xmlXPathCmpNodesExt:
|
||||||
* @node1: the first node
|
* @node1: the first node
|
||||||
@ -1991,7 +1999,7 @@ turtle_comparison:
|
|||||||
return(1);
|
return(1);
|
||||||
return(-1); /* assume there is no sibling list corruption */
|
return(-1); /* assume there is no sibling list corruption */
|
||||||
}
|
}
|
||||||
#endif /* XP_FAST_NON_ELEM_COMPARISON */
|
#endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* xmlXPathNodeSetSort:
|
* xmlXPathNodeSetSort:
|
||||||
@ -2013,7 +2021,7 @@ xmlXPathNodeSetSort(xmlNodeSetPtr set) {
|
|||||||
for (i = incr; i < len; i++) {
|
for (i = incr; i < len; i++) {
|
||||||
j = i - incr;
|
j = i - incr;
|
||||||
while (j >= 0) {
|
while (j >= 0) {
|
||||||
#ifdef XP_FAST_NON_ELEM_COMPARISON
|
#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
|
||||||
if (xmlXPathCmpNodesExt(set->nodeTab[j],
|
if (xmlXPathCmpNodesExt(set->nodeTab[j],
|
||||||
set->nodeTab[j + incr]) == -1)
|
set->nodeTab[j + incr]) == -1)
|
||||||
#else
|
#else
|
||||||
@ -2133,6 +2141,41 @@ xmlXPathNodeSetCreate(xmlNodePtr val) {
|
|||||||
return(ret);
|
return(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* xmlXPathNodeSetCreateSize:
|
||||||
|
* @val: an initial xmlNodePtr, or NULL
|
||||||
|
* @size: the initial size of the node-sets
|
||||||
|
*
|
||||||
|
* Create a new xmlNodeSetPtr of type double and of value @val
|
||||||
|
*
|
||||||
|
* Returns the newly created object.
|
||||||
|
*/
|
||||||
|
static xmlNodeSetPtr
|
||||||
|
xmlXPathNodeSetCreateSize(int size)
|
||||||
|
{
|
||||||
|
xmlNodeSetPtr ret;
|
||||||
|
|
||||||
|
ret = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet));
|
||||||
|
if (ret == NULL) {
|
||||||
|
xmlXPathErrMemory(NULL, "creating nodeset\n");
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
memset(ret, 0, (size_t) sizeof(xmlNodeSet));
|
||||||
|
if (size > 0) {
|
||||||
|
if (size < XML_NODESET_DEFAULT)
|
||||||
|
size = XML_NODESET_DEFAULT;
|
||||||
|
ret->nodeTab = (xmlNodePtr *) xmlMalloc(size * sizeof(xmlNodePtr));
|
||||||
|
if (ret->nodeTab == NULL) {
|
||||||
|
xmlXPathErrMemory(NULL, "creating nodeset\n");
|
||||||
|
xmlFree(ret);
|
||||||
|
return(NULL);
|
||||||
|
}
|
||||||
|
memset(ret->nodeTab, 0, size * (size_t) sizeof(xmlNodePtr));
|
||||||
|
ret->nodeMax = size;
|
||||||
|
}
|
||||||
|
return(ret);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* xmlXPathNodeSetContains:
|
* xmlXPathNodeSetContains:
|
||||||
* @cur: the node-set
|
* @cur: the node-set
|
||||||
@ -2352,31 +2395,49 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
|
|||||||
xmlNodeSetPtr
|
xmlNodeSetPtr
|
||||||
xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
|
xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
|
||||||
int i, j, initNr, skip;
|
int i, j, initNr, skip;
|
||||||
|
xmlNodePtr n1, n2;
|
||||||
|
|
||||||
if (val2 == NULL) return(val1);
|
if (val2 == NULL) return(val1);
|
||||||
if (val1 == NULL) {
|
if (val1 == NULL) {
|
||||||
val1 = xmlXPathNodeSetCreate(NULL);
|
/*
|
||||||
|
* Optimization: Create an equally sized node-set
|
||||||
|
* and memcpy the content.
|
||||||
|
*/
|
||||||
|
val1 = xmlXPathNodeSetCreateSize(val2->nodeNr);
|
||||||
|
if (val1 == NULL)
|
||||||
|
return(NULL);
|
||||||
|
if (val2->nodeNr != 0) {
|
||||||
|
if (val2->nodeNr == 1)
|
||||||
|
*(val1->nodeTab) = *(val2->nodeTab);
|
||||||
|
else {
|
||||||
|
memcpy(val1->nodeTab, val2->nodeTab,
|
||||||
|
val2->nodeNr * sizeof(xmlNodePtr));
|
||||||
|
}
|
||||||
|
val1->nodeNr = val2->nodeNr;
|
||||||
|
}
|
||||||
|
return(val1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
|
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
|
||||||
initNr = val1->nodeNr;
|
initNr = val1->nodeNr;
|
||||||
|
|
||||||
for (i = 0;i < val2->nodeNr;i++) {
|
for (i = 0;i < val2->nodeNr;i++) {
|
||||||
|
n2 = val2->nodeTab[i];
|
||||||
/*
|
/*
|
||||||
* check against duplicates
|
* check against duplicates
|
||||||
*/
|
*/
|
||||||
skip = 0;
|
skip = 0;
|
||||||
for (j = 0; j < initNr; j++) {
|
for (j = 0; j < initNr; j++) {
|
||||||
if (val1->nodeTab[j] == val2->nodeTab[i]) {
|
n1 = val1->nodeTab[j];
|
||||||
|
if (n1 == n2) {
|
||||||
skip = 1;
|
skip = 1;
|
||||||
break;
|
break;
|
||||||
} else if ((val1->nodeTab[j]->type == XML_NAMESPACE_DECL) &&
|
} else if ((n1->type == XML_NAMESPACE_DECL) &&
|
||||||
(val2->nodeTab[i]->type == XML_NAMESPACE_DECL)) {
|
(n2->type == XML_NAMESPACE_DECL)) {
|
||||||
xmlNsPtr ns1, ns2;
|
if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
|
||||||
ns1 = (xmlNsPtr) val1->nodeTab[j];
|
(xmlStrEqual(((xmlNsPtr) n1)->prefix,
|
||||||
ns2 = (xmlNsPtr) val2->nodeTab[i];
|
((xmlNsPtr) n2)->prefix)))
|
||||||
if ((ns1->next == ns2->next) &&
|
{
|
||||||
(xmlStrEqual(ns1->prefix, ns2->prefix))) {
|
|
||||||
skip = 1;
|
skip = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2410,13 +2471,13 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
|
|||||||
}
|
}
|
||||||
val1->nodeTab = temp;
|
val1->nodeTab = temp;
|
||||||
}
|
}
|
||||||
if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) {
|
if (n2->type == XML_NAMESPACE_DECL) {
|
||||||
xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i];
|
xmlNsPtr ns = (xmlNsPtr) n2;
|
||||||
|
|
||||||
val1->nodeTab[val1->nodeNr++] =
|
val1->nodeTab[val1->nodeNr++] =
|
||||||
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
|
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
|
||||||
} else
|
} else
|
||||||
val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
|
val1->nodeTab[val1->nodeNr++] = n2;
|
||||||
}
|
}
|
||||||
|
|
||||||
return(val1);
|
return(val1);
|
||||||
@ -7640,7 +7701,7 @@ xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
|
|||||||
* a few forward declarations since we use a recursive call based
|
* a few forward declarations since we use a recursive call based
|
||||||
* implementation.
|
* implementation.
|
||||||
*/
|
*/
|
||||||
static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
|
static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort);
|
||||||
static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
|
static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
|
||||||
static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
|
static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
|
||||||
static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
|
static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
|
||||||
@ -8297,6 +8358,7 @@ xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
|
|||||||
xmlChar *name;
|
xmlChar *name;
|
||||||
xmlChar *prefix;
|
xmlChar *prefix;
|
||||||
int nbargs = 0;
|
int nbargs = 0;
|
||||||
|
int sort = 1;
|
||||||
|
|
||||||
name = xmlXPathParseQName(ctxt, &prefix);
|
name = xmlXPathParseQName(ctxt, &prefix);
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
@ -8318,12 +8380,20 @@ xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
|
|||||||
NEXT;
|
NEXT;
|
||||||
SKIP_BLANKS;
|
SKIP_BLANKS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Optimization for count(): we don't need the node-set to be sorted.
|
||||||
|
*/
|
||||||
|
if ((prefix == NULL) && (name[0] == 'c') &&
|
||||||
|
xmlStrEqual(name, BAD_CAST "count"))
|
||||||
|
{
|
||||||
|
sort = 0;
|
||||||
|
}
|
||||||
ctxt->comp->last = -1;
|
ctxt->comp->last = -1;
|
||||||
if (CUR != ')') {
|
if (CUR != ')') {
|
||||||
while (CUR != 0) {
|
while (CUR != 0) {
|
||||||
int op1 = ctxt->comp->last;
|
int op1 = ctxt->comp->last;
|
||||||
ctxt->comp->last = -1;
|
ctxt->comp->last = -1;
|
||||||
xmlXPathCompileExpr(ctxt);
|
xmlXPathCompileExpr(ctxt, sort);
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
|
PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
|
||||||
nbargs++;
|
nbargs++;
|
||||||
@ -8360,7 +8430,7 @@ xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
|
|||||||
else if (CUR == '(') {
|
else if (CUR == '(') {
|
||||||
NEXT;
|
NEXT;
|
||||||
SKIP_BLANKS;
|
SKIP_BLANKS;
|
||||||
xmlXPathCompileExpr(ctxt);
|
xmlXPathCompileExpr(ctxt, 1);
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
if (CUR != ')') {
|
if (CUR != ')') {
|
||||||
XP_ERROR(XPATH_EXPR_ERROR);
|
XP_ERROR(XPATH_EXPR_ERROR);
|
||||||
@ -8868,7 +8938,7 @@ xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
|
|||||||
* Parse and compile an expression
|
* Parse and compile an expression
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
|
xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
|
||||||
xmlXPathCompAndExpr(ctxt);
|
xmlXPathCompAndExpr(ctxt);
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
SKIP_BLANKS;
|
SKIP_BLANKS;
|
||||||
@ -8882,8 +8952,13 @@ xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
|
|||||||
op1 = ctxt->comp->nbStep;
|
op1 = ctxt->comp->nbStep;
|
||||||
SKIP_BLANKS;
|
SKIP_BLANKS;
|
||||||
}
|
}
|
||||||
if (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE) {
|
if ((sort) && (ctxt->comp->steps[ctxt->comp->last].op != XPATH_OP_VALUE)) {
|
||||||
/* more ops could be optimized too */
|
/* more ops could be optimized too */
|
||||||
|
/*
|
||||||
|
* This is the main place to eliminate sorting for
|
||||||
|
* operations which don't require a sorted node-set.
|
||||||
|
* E.g. count().
|
||||||
|
*/
|
||||||
PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
|
PUSH_UNARY_EXPR(XPATH_OP_SORT, ctxt->comp->last , 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8910,7 +8985,7 @@ xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
|
|||||||
SKIP_BLANKS;
|
SKIP_BLANKS;
|
||||||
|
|
||||||
ctxt->comp->last = -1;
|
ctxt->comp->last = -1;
|
||||||
xmlXPathCompileExpr(ctxt);
|
xmlXPathCompileExpr(ctxt, 1);
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
|
|
||||||
if (CUR != ']') {
|
if (CUR != ']') {
|
||||||
@ -9202,7 +9277,7 @@ xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
|
|||||||
NEXT;
|
NEXT;
|
||||||
SKIP_BLANKS;
|
SKIP_BLANKS;
|
||||||
|
|
||||||
xmlXPathCompileExpr(ctxt);
|
xmlXPathCompileExpr(ctxt, 1);
|
||||||
/* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
|
/* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
|
|
||||||
@ -9630,7 +9705,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
|
|||||||
break;
|
break;
|
||||||
if (((t % 256) == 0) &&
|
if (((t % 256) == 0) &&
|
||||||
(first != NULL) && (*first != NULL) &&
|
(first != NULL) && (*first != NULL) &&
|
||||||
#ifdef XP_FAST_NON_ELEM_COMPARISON
|
#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
|
||||||
(xmlXPathCmpNodesExt(*first, cur) >= 0))
|
(xmlXPathCmpNodesExt(*first, cur) >= 0))
|
||||||
#else
|
#else
|
||||||
(xmlXPathCmpNodes(*first, cur) >= 0))
|
(xmlXPathCmpNodes(*first, cur) >= 0))
|
||||||
@ -9640,7 +9715,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
|
|||||||
break;
|
break;
|
||||||
if (((t % 256) == 0) &&
|
if (((t % 256) == 0) &&
|
||||||
(last != NULL) && (*last != NULL) &&
|
(last != NULL) && (*last != NULL) &&
|
||||||
#ifdef XP_FAST_NON_ELEM_COMPARISON
|
#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
|
||||||
(xmlXPathCmpNodesExt(cur, *last) >= 0))
|
(xmlXPathCmpNodesExt(cur, *last) >= 0))
|
||||||
#else
|
#else
|
||||||
(xmlXPathCmpNodes(cur, *last) >= 0))
|
(xmlXPathCmpNodes(cur, *last) >= 0))
|
||||||
@ -10231,6 +10306,10 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
|
|||||||
return(t);
|
return(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
|
||||||
|
xmlXPathStepOpPtr op, xmlNodePtr * first);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* xmlXPathCompOpEvalFirst:
|
* xmlXPathCompOpEvalFirst:
|
||||||
* @ctxt: the XPath parser context with the compiled expression
|
* @ctxt: the XPath parser context with the compiled expression
|
||||||
@ -10267,6 +10346,13 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
|
|||||||
/*
|
/*
|
||||||
* limit tree traversing to first node in the result
|
* limit tree traversing to first node in the result
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
* OPTIMIZE TODO: This implicitely sorts
|
||||||
|
* the result, even if not needed. E.g. if the argument
|
||||||
|
* of the count() function, no sorting is needed.
|
||||||
|
* OPTIMIZE TODO: How do we know if the node-list wasn't
|
||||||
|
* aready sorted?
|
||||||
|
*/
|
||||||
if (ctxt->value->nodesetval->nodeNr > 1)
|
if (ctxt->value->nodesetval->nodeNr > 1)
|
||||||
xmlXPathNodeSetSort(ctxt->value->nodesetval);
|
xmlXPathNodeSetSort(ctxt->value->nodesetval);
|
||||||
*first = ctxt->value->nodesetval->nodeTab[0];
|
*first = ctxt->value->nodesetval->nodeTab[0];
|
||||||
@ -10354,9 +10440,15 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
|
|||||||
CHECK_ERROR0;
|
CHECK_ERROR0;
|
||||||
if ((ctxt->value != NULL)
|
if ((ctxt->value != NULL)
|
||||||
&& (ctxt->value->type == XPATH_NODESET)
|
&& (ctxt->value->type == XPATH_NODESET)
|
||||||
&& (ctxt->value->nodesetval != NULL))
|
&& (ctxt->value->nodesetval != NULL)
|
||||||
|
&& (ctxt->value->nodesetval->nodeNr > 1))
|
||||||
xmlXPathNodeSetSort(ctxt->value->nodesetval);
|
xmlXPathNodeSetSort(ctxt->value->nodesetval);
|
||||||
return (total);
|
return (total);
|
||||||
|
#ifdef XP_OPTIMIZED_FILTER_FIRST
|
||||||
|
case XPATH_OP_FILTER:
|
||||||
|
total =+ xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
|
||||||
|
return (total);
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return (xmlXPathCompOpEval(ctxt, op));
|
return (xmlXPathCompOpEval(ctxt, op));
|
||||||
}
|
}
|
||||||
@ -10514,6 +10606,295 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef XP_OPTIMIZED_FILTER_FIRST
|
||||||
|
static int
|
||||||
|
xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
|
||||||
|
xmlXPathStepOpPtr op, xmlNodePtr * first)
|
||||||
|
{
|
||||||
|
int total = 0;
|
||||||
|
xmlXPathCompExprPtr comp;
|
||||||
|
xmlXPathObjectPtr res;
|
||||||
|
xmlXPathObjectPtr obj;
|
||||||
|
xmlNodeSetPtr oldset;
|
||||||
|
xmlNodePtr oldnode;
|
||||||
|
xmlDocPtr oldDoc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
CHECK_ERROR0;
|
||||||
|
comp = ctxt->comp;
|
||||||
|
/*
|
||||||
|
* Optimization for ()[last()] selection i.e. the last elem
|
||||||
|
*/
|
||||||
|
if ((op->ch1 != -1) && (op->ch2 != -1) &&
|
||||||
|
(comp->steps[op->ch1].op == XPATH_OP_SORT) &&
|
||||||
|
(comp->steps[op->ch2].op == XPATH_OP_SORT)) {
|
||||||
|
int f = comp->steps[op->ch2].ch1;
|
||||||
|
|
||||||
|
if ((f != -1) &&
|
||||||
|
(comp->steps[f].op == XPATH_OP_FUNCTION) &&
|
||||||
|
(comp->steps[f].value5 == NULL) &&
|
||||||
|
(comp->steps[f].value == 0) &&
|
||||||
|
(comp->steps[f].value4 != NULL) &&
|
||||||
|
(xmlStrEqual
|
||||||
|
(comp->steps[f].value4, BAD_CAST "last"))) {
|
||||||
|
xmlNodePtr last = NULL;
|
||||||
|
|
||||||
|
total +=
|
||||||
|
xmlXPathCompOpEvalLast(ctxt,
|
||||||
|
&comp->steps[op->ch1],
|
||||||
|
&last);
|
||||||
|
CHECK_ERROR0;
|
||||||
|
/*
|
||||||
|
* The nodeset should be in document order,
|
||||||
|
* Keep only the last value
|
||||||
|
*/
|
||||||
|
if ((ctxt->value != NULL) &&
|
||||||
|
(ctxt->value->type == XPATH_NODESET) &&
|
||||||
|
(ctxt->value->nodesetval != NULL) &&
|
||||||
|
(ctxt->value->nodesetval->nodeTab != NULL) &&
|
||||||
|
(ctxt->value->nodesetval->nodeNr > 1)) {
|
||||||
|
ctxt->value->nodesetval->nodeTab[0] =
|
||||||
|
ctxt->value->nodesetval->nodeTab[ctxt->
|
||||||
|
value->
|
||||||
|
nodesetval->
|
||||||
|
nodeNr -
|
||||||
|
1];
|
||||||
|
ctxt->value->nodesetval->nodeNr = 1;
|
||||||
|
*first = *(ctxt->value->nodesetval->nodeTab);
|
||||||
|
}
|
||||||
|
return (total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op->ch1 != -1)
|
||||||
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
|
||||||
|
CHECK_ERROR0;
|
||||||
|
if (op->ch2 == -1)
|
||||||
|
return (total);
|
||||||
|
if (ctxt->value == NULL)
|
||||||
|
return (total);
|
||||||
|
|
||||||
|
#ifdef LIBXML_XPTR_ENABLED
|
||||||
|
oldnode = ctxt->context->node;
|
||||||
|
/*
|
||||||
|
* Hum are we filtering the result of an XPointer expression
|
||||||
|
*/
|
||||||
|
if (ctxt->value->type == XPATH_LOCATIONSET) {
|
||||||
|
xmlXPathObjectPtr tmp = NULL;
|
||||||
|
xmlLocationSetPtr newlocset = NULL;
|
||||||
|
xmlLocationSetPtr oldlocset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract the old locset, and then evaluate the result of the
|
||||||
|
* expression for all the element in the locset. use it to grow
|
||||||
|
* up a new locset.
|
||||||
|
*/
|
||||||
|
CHECK_TYPE0(XPATH_LOCATIONSET);
|
||||||
|
obj = valuePop(ctxt);
|
||||||
|
oldlocset = obj->user;
|
||||||
|
ctxt->context->node = NULL;
|
||||||
|
|
||||||
|
if ((oldlocset == NULL) || (oldlocset->locNr == 0)) {
|
||||||
|
ctxt->context->contextSize = 0;
|
||||||
|
ctxt->context->proximityPosition = 0;
|
||||||
|
if (op->ch2 != -1)
|
||||||
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
|
||||||
|
res = valuePop(ctxt);
|
||||||
|
if (res != NULL)
|
||||||
|
xmlXPathFreeObject(res);
|
||||||
|
valuePush(ctxt, obj);
|
||||||
|
CHECK_ERROR0;
|
||||||
|
return (total);
|
||||||
|
}
|
||||||
|
newlocset = xmlXPtrLocationSetCreate(NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < oldlocset->locNr; i++) {
|
||||||
|
/*
|
||||||
|
* Run the evaluation with a node list made of a
|
||||||
|
* single item in the nodelocset.
|
||||||
|
*/
|
||||||
|
ctxt->context->node = oldlocset->locTab[i]->user;
|
||||||
|
ctxt->context->contextSize = oldlocset->locNr;
|
||||||
|
ctxt->context->proximityPosition = i + 1;
|
||||||
|
if (tmp == NULL)
|
||||||
|
tmp = xmlXPathNewNodeSet(ctxt->context->node);
|
||||||
|
else {
|
||||||
|
*(tmp->nodesetval->nodeTab) = ctxt->context->node;
|
||||||
|
tmp->nodesetval->nodeNr = 1;
|
||||||
|
}
|
||||||
|
valuePush(ctxt, tmp);
|
||||||
|
if (op->ch2 != -1)
|
||||||
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
|
||||||
|
if (ctxt->error != XPATH_EXPRESSION_OK) {
|
||||||
|
xmlXPathFreeObject(obj);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The result of the evaluation need to be tested to
|
||||||
|
* decided whether the filter succeeded or not
|
||||||
|
*/
|
||||||
|
res = valuePop(ctxt);
|
||||||
|
if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
|
||||||
|
xmlXPtrLocationSetAdd(newlocset,
|
||||||
|
xmlXPathObjectCopy(oldlocset->locTab[i]));
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Cleanup
|
||||||
|
*/
|
||||||
|
if (res != NULL)
|
||||||
|
xmlXPathFreeObject(res);
|
||||||
|
if (ctxt->value == tmp) {
|
||||||
|
valuePop(ctxt);
|
||||||
|
tmp->nodesetval->nodeNr = 0;
|
||||||
|
/*
|
||||||
|
* REVISIT TODO: Don't free the temporary nodeset
|
||||||
|
* in order to avoid massive recreation inside this
|
||||||
|
* loop.
|
||||||
|
*/
|
||||||
|
/* xmlXPathFreeObject(res); */
|
||||||
|
} else {
|
||||||
|
tmp = xmlXPathNewNodeSet(ctxt->context->node);
|
||||||
|
}
|
||||||
|
ctxt->context->node = NULL;
|
||||||
|
/*
|
||||||
|
* Only put the first node in the result, then leave.
|
||||||
|
*/
|
||||||
|
if (newlocset->locNr > 0) {
|
||||||
|
*first = (xmlNodePtr) oldlocset->locTab[i]->user;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmp != NULL)
|
||||||
|
xmlXPathFreeObject(tmp);
|
||||||
|
/*
|
||||||
|
* The result is used as the new evaluation locset.
|
||||||
|
*/
|
||||||
|
xmlXPathFreeObject(obj);
|
||||||
|
ctxt->context->node = NULL;
|
||||||
|
ctxt->context->contextSize = -1;
|
||||||
|
ctxt->context->proximityPosition = -1;
|
||||||
|
valuePush(ctxt, xmlXPtrWrapLocationSet(newlocset));
|
||||||
|
ctxt->context->node = oldnode;
|
||||||
|
return (total);
|
||||||
|
}
|
||||||
|
#endif /* LIBXML_XPTR_ENABLED */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract the old set, and then evaluate the result of the
|
||||||
|
* expression for all the element in the set. use it to grow
|
||||||
|
* up a new set.
|
||||||
|
*/
|
||||||
|
CHECK_TYPE0(XPATH_NODESET);
|
||||||
|
obj = valuePop(ctxt);
|
||||||
|
oldset = obj->nodesetval;
|
||||||
|
|
||||||
|
oldnode = ctxt->context->node;
|
||||||
|
oldDoc = ctxt->context->doc;
|
||||||
|
ctxt->context->node = NULL;
|
||||||
|
|
||||||
|
if ((oldset == NULL) || (oldset->nodeNr == 0)) {
|
||||||
|
ctxt->context->contextSize = 0;
|
||||||
|
ctxt->context->proximityPosition = 0;
|
||||||
|
/* QUESTION TODO: Why was this code commented out?
|
||||||
|
if (op->ch2 != -1)
|
||||||
|
total +=
|
||||||
|
xmlXPathCompOpEval(ctxt,
|
||||||
|
&comp->steps[op->ch2]);
|
||||||
|
CHECK_ERROR0;
|
||||||
|
res = valuePop(ctxt);
|
||||||
|
if (res != NULL)
|
||||||
|
xmlXPathFreeObject(res);
|
||||||
|
*/
|
||||||
|
valuePush(ctxt, obj);
|
||||||
|
ctxt->context->node = oldnode;
|
||||||
|
CHECK_ERROR0;
|
||||||
|
} else {
|
||||||
|
xmlNodeSetPtr newset;
|
||||||
|
xmlXPathObjectPtr tmp = NULL;
|
||||||
|
/*
|
||||||
|
* Initialize the new set.
|
||||||
|
* Also set the xpath document in case things like
|
||||||
|
* key() evaluation are attempted on the predicate
|
||||||
|
*/
|
||||||
|
newset = xmlXPathNodeSetCreate(NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < oldset->nodeNr; i++) {
|
||||||
|
/*
|
||||||
|
* Run the evaluation with a node list made of
|
||||||
|
* a single item in the nodeset.
|
||||||
|
*/
|
||||||
|
ctxt->context->node = oldset->nodeTab[i];
|
||||||
|
if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
|
||||||
|
(oldset->nodeTab[i]->doc != NULL))
|
||||||
|
ctxt->context->doc = oldset->nodeTab[i]->doc;
|
||||||
|
if (tmp == NULL)
|
||||||
|
tmp = xmlXPathNewNodeSet(ctxt->context->node);
|
||||||
|
else {
|
||||||
|
*(tmp->nodesetval->nodeTab) = ctxt->context->node;
|
||||||
|
tmp->nodesetval->nodeNr = 1;
|
||||||
|
}
|
||||||
|
valuePush(ctxt, tmp);
|
||||||
|
ctxt->context->contextSize = oldset->nodeNr;
|
||||||
|
ctxt->context->proximityPosition = i + 1;
|
||||||
|
if (op->ch2 != -1)
|
||||||
|
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
|
||||||
|
if (ctxt->error != XPATH_EXPRESSION_OK) {
|
||||||
|
xmlXPathFreeNodeSet(newset);
|
||||||
|
xmlXPathFreeObject(obj);
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* The result of the evaluation needs to be tested to
|
||||||
|
* decide whether the filter succeeded or not
|
||||||
|
*/
|
||||||
|
res = valuePop(ctxt);
|
||||||
|
if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
|
||||||
|
xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Cleanup
|
||||||
|
*/
|
||||||
|
if (res != NULL)
|
||||||
|
xmlXPathFreeObject(res);
|
||||||
|
if (ctxt->value == tmp) {
|
||||||
|
valuePop(ctxt);
|
||||||
|
tmp->nodesetval->nodeNr = 0;
|
||||||
|
/*
|
||||||
|
* REVISIT TODO: Don't free the temporary nodeset
|
||||||
|
* in order to avoid massive recreation inside this
|
||||||
|
* loop.
|
||||||
|
*/
|
||||||
|
/* xmlXPathFreeObject(res); */
|
||||||
|
} else {
|
||||||
|
tmp = xmlXPathNewNodeSet(ctxt->context->node);
|
||||||
|
}
|
||||||
|
ctxt->context->node = NULL;
|
||||||
|
/*
|
||||||
|
* Only put the first node in the result, then leave.
|
||||||
|
*/
|
||||||
|
if (newset->nodeNr > 0) {
|
||||||
|
*first = *(newset->nodeTab);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmp != NULL)
|
||||||
|
xmlXPathFreeObject(tmp);
|
||||||
|
/*
|
||||||
|
* The result is used as the new evaluation set.
|
||||||
|
*/
|
||||||
|
xmlXPathFreeObject(obj);
|
||||||
|
ctxt->context->node = NULL;
|
||||||
|
ctxt->context->contextSize = -1;
|
||||||
|
ctxt->context->proximityPosition = -1;
|
||||||
|
/* may want to move this past the '}' later */
|
||||||
|
ctxt->context->doc = oldDoc;
|
||||||
|
valuePush(ctxt, xmlXPathWrapNodeSet(newset));
|
||||||
|
}
|
||||||
|
ctxt->context->node = oldnode;
|
||||||
|
return(total);
|
||||||
|
}
|
||||||
|
#endif /* XP_OPTIMIZED_FILTER_FIRST */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* xmlXPathCompOpEval:
|
* xmlXPathCompOpEval:
|
||||||
* @ctxt: the XPath parser context with the compiled expression
|
* @ctxt: the XPath parser context with the compiled expression
|
||||||
@ -10886,8 +11267,22 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|||||||
* Optimization for ()[1] selection i.e. the first elem
|
* Optimization for ()[1] selection i.e. the first elem
|
||||||
*/
|
*/
|
||||||
if ((op->ch1 != -1) && (op->ch2 != -1) &&
|
if ((op->ch1 != -1) && (op->ch2 != -1) &&
|
||||||
|
#ifdef XP_OPTIMIZED_FILTER_FIRST
|
||||||
|
/*
|
||||||
|
* FILTER TODO: Can we assume that the inner processing
|
||||||
|
* will result in an ordered list if we have an
|
||||||
|
* XPATH_OP_FILTER?
|
||||||
|
* What about an additional field or flag on
|
||||||
|
* xmlXPathObject like @sorted ? This way we wouln'd need
|
||||||
|
* to assume anything, so it would be more robust and
|
||||||
|
* easier to optimize.
|
||||||
|
*/
|
||||||
|
((comp->steps[op->ch1].op == XPATH_OP_SORT) || /* 18 */
|
||||||
|
(comp->steps[op->ch1].op == XPATH_OP_FILTER)) && /* 17 */
|
||||||
|
#else
|
||||||
(comp->steps[op->ch1].op == XPATH_OP_SORT) &&
|
(comp->steps[op->ch1].op == XPATH_OP_SORT) &&
|
||||||
(comp->steps[op->ch2].op == XPATH_OP_VALUE)) {
|
#endif
|
||||||
|
(comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
|
||||||
xmlXPathObjectPtr val;
|
xmlXPathObjectPtr val;
|
||||||
|
|
||||||
val = comp->steps[op->ch2].value4;
|
val = comp->steps[op->ch2].value4;
|
||||||
@ -11087,6 +11482,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|||||||
ctxt->context->node = oldnode;
|
ctxt->context->node = oldnode;
|
||||||
CHECK_ERROR0;
|
CHECK_ERROR0;
|
||||||
} else {
|
} else {
|
||||||
|
tmp = NULL;
|
||||||
/*
|
/*
|
||||||
* Initialize the new set.
|
* Initialize the new set.
|
||||||
* Also set the xpath document in case things like
|
* Also set the xpath document in case things like
|
||||||
@ -11103,7 +11499,12 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|||||||
if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
|
if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
|
||||||
(oldset->nodeTab[i]->doc != NULL))
|
(oldset->nodeTab[i]->doc != NULL))
|
||||||
ctxt->context->doc = oldset->nodeTab[i]->doc;
|
ctxt->context->doc = oldset->nodeTab[i]->doc;
|
||||||
|
if (tmp == NULL)
|
||||||
tmp = xmlXPathNewNodeSet(ctxt->context->node);
|
tmp = xmlXPathNewNodeSet(ctxt->context->node);
|
||||||
|
else {
|
||||||
|
*(tmp->nodesetval->nodeTab) = ctxt->context->node;
|
||||||
|
tmp->nodesetval->nodeNr = 1;
|
||||||
|
}
|
||||||
valuePush(ctxt, tmp);
|
valuePush(ctxt, tmp);
|
||||||
ctxt->context->contextSize = oldset->nodeNr;
|
ctxt->context->contextSize = oldset->nodeNr;
|
||||||
ctxt->context->proximityPosition = i + 1;
|
ctxt->context->proximityPosition = i + 1;
|
||||||
@ -11133,12 +11534,21 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
|
|||||||
if (res != NULL)
|
if (res != NULL)
|
||||||
xmlXPathFreeObject(res);
|
xmlXPathFreeObject(res);
|
||||||
if (ctxt->value == tmp) {
|
if (ctxt->value == tmp) {
|
||||||
res = valuePop(ctxt);
|
valuePop(ctxt);
|
||||||
xmlXPathFreeObject(res);
|
tmp->nodesetval->nodeNr = 0;
|
||||||
}
|
/*
|
||||||
|
* REVISIT TODO: Don't free the temporary nodeset
|
||||||
|
* in order to avoid massive recreation inside this
|
||||||
|
* loop.
|
||||||
|
*/
|
||||||
|
/* xmlXPathFreeObject(res); */
|
||||||
|
} else
|
||||||
|
tmp = xmlXPathNewNodeSet(ctxt->context->node);
|
||||||
|
|
||||||
ctxt->context->node = NULL;
|
ctxt->context->node = NULL;
|
||||||
}
|
}
|
||||||
|
if (tmp != NULL)
|
||||||
|
xmlXPathFreeObject(tmp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The result is used as the new evaluation set.
|
* The result is used as the new evaluation set.
|
||||||
@ -11783,7 +12193,7 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
|
|||||||
xmlXPathInit();
|
xmlXPathInit();
|
||||||
|
|
||||||
pctxt = xmlXPathNewParserContext(str, ctxt);
|
pctxt = xmlXPathNewParserContext(str, ctxt);
|
||||||
xmlXPathCompileExpr(pctxt);
|
xmlXPathCompileExpr(pctxt, 1);
|
||||||
|
|
||||||
if( pctxt->error != XPATH_EXPRESSION_OK )
|
if( pctxt->error != XPATH_EXPRESSION_OK )
|
||||||
{
|
{
|
||||||
@ -11932,7 +12342,7 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
xmlXPathCompileExpr(ctxt);
|
xmlXPathCompileExpr(ctxt, 1);
|
||||||
}
|
}
|
||||||
CHECK_ERROR;
|
CHECK_ERROR;
|
||||||
xmlXPathRunEval(ctxt);
|
xmlXPathRunEval(ctxt);
|
||||||
|
Loading…
Reference in New Issue
Block a user