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().
diff --git a/xpath.c b/xpath.c
index d851eb3..2a33db2 100644
--- a/xpath.c
+++ b/xpath.c
@@ -77,7 +77,7 @@
#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
* xmlXPathCmpNodes(). The new function is optimized comparison of
* 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
* 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:
* There are a few spots where some tests are done which depend upon ascii
@@ -402,15 +410,15 @@
XPATH_OP_UNION,
XPATH_OP_ROOT,
XPATH_OP_NODE,
- XPATH_OP_RESET,
+ XPATH_OP_RESET, /* 10 */
XPATH_OP_COLLECT,
- XPATH_OP_VALUE,
+ XPATH_OP_VALUE, /* 12 */
XPATH_OP_VARIABLE,
XPATH_OP_FUNCTION,
XPATH_OP_ARG,
XPATH_OP_PREDICATE,
- XPATH_OP_FILTER,
- XPATH_OP_SORT
+ XPATH_OP_FILTER, /* 17 */
+ XPATH_OP_SORT /* 18 */
#ifdef LIBXML_XPTR_ENABLED
,XPATH_OP_RANGETO
#endif
@@ -1702,7 +1710,7 @@
return(-1); /* assume there is no sibling list corruption */
}
-#ifdef XP_FAST_NON_ELEM_COMPARISON
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
/**
* xmlXPathCmpNodesExt:
* @node1: the first node
@@ -1991,7 +1999,7 @@
return(1);
return(-1); /* assume there is no sibling list corruption */
}
-#endif /* XP_FAST_NON_ELEM_COMPARISON */
+#endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
/**
* xmlXPathNodeSetSort:
@@ -2013,7 +2021,7 @@
for (i = incr; i < len; i++) {
j = i - incr;
while (j >= 0) {
-#ifdef XP_FAST_NON_ELEM_COMPARISON
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
if (xmlXPathCmpNodesExt(set->nodeTab[j],
set->nodeTab[j + incr]) == -1)
#else
@@ -2134,6 +2142,41 @@
}
/**
+ * 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:
* @cur: the node-set
* @val: the node
@@ -2352,31 +2395,49 @@
xmlNodeSetPtr
xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
int i, j, initNr, skip;
+ xmlNodePtr n1, n2;
if (val2 == NULL) return(val1);
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 @@ */
initNr = val1->nodeNr;
for (i = 0;i < val2->nodeNr;i++) {
+ n2 = val2->nodeTab[i];
/*
* check against duplicates
*/
skip = 0;
for (j = 0; j < initNr; j++) {
- if (val1->nodeTab[j] == val2->nodeTab[i]) {
+ n1 = val1->nodeTab[j];
+ if (n1 == n2) {
skip = 1;
break;
- } else if ((val1->nodeTab[j]->type == XML_NAMESPACE_DECL) &&
- (val2->nodeTab[i]->type == XML_NAMESPACE_DECL)) {
- xmlNsPtr ns1, ns2;
- ns1 = (xmlNsPtr) val1->nodeTab[j];
- ns2 = (xmlNsPtr) val2->nodeTab[i];
- if ((ns1->next == ns2->next) &&
- (xmlStrEqual(ns1->prefix, ns2->prefix))) {
+ } else if ((n1->type == XML_NAMESPACE_DECL) &&
+ (n2->type == XML_NAMESPACE_DECL)) {
+ if ((((xmlNsPtr) n1)->next == ((xmlNsPtr) n2)->next) &&
+ (xmlStrEqual(((xmlNsPtr) n1)->prefix,
+ ((xmlNsPtr) n2)->prefix)))
+ {
skip = 1;
break;
}
@@ -2410,13 +2471,13 @@
}
val1->nodeTab = temp;
}
- if (val2->nodeTab[i]->type == XML_NAMESPACE_DECL) {
- xmlNsPtr ns = (xmlNsPtr) val2->nodeTab[i];
+ if (n2->type == XML_NAMESPACE_DECL) {
+ xmlNsPtr ns = (xmlNsPtr) n2;
val1->nodeTab[val1->nodeNr++] =
xmlXPathNodeSetDupNs((xmlNodePtr) ns->next, ns);
} else
- val1->nodeTab[val1->nodeNr++] = val2->nodeTab[i];
+ val1->nodeTab[val1->nodeNr++] = n2;
}
return(val1);
@@ -7640,7 +7701,7 @@
* a few forward declarations since we use a recursive call based
* implementation.
*/
-static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt);
+static void xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort);
static void xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter);
static void xmlXPathCompLocationPath(xmlXPathParserContextPtr ctxt);
static void xmlXPathCompRelativeLocationPath(xmlXPathParserContextPtr ctxt);
@@ -8297,6 +8358,7 @@
xmlChar *name;
xmlChar *prefix;
int nbargs = 0;
+ int sort = 1;
name = xmlXPathParseQName(ctxt, &prefix);
if (name == NULL) {
@@ -8318,12 +8380,20 @@
NEXT;
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;
if (CUR != ')') {
while (CUR != 0) {
int op1 = ctxt->comp->last;
ctxt->comp->last = -1;
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, sort);
CHECK_ERROR;
PUSH_BINARY_EXPR(XPATH_OP_ARG, op1, ctxt->comp->last, 0, 0);
nbargs++;
@@ -8360,7 +8430,7 @@
else if (CUR == '(') {
NEXT;
SKIP_BLANKS;
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, 1);
CHECK_ERROR;
if (CUR != ')') {
XP_ERROR(XPATH_EXPR_ERROR);
@@ -8868,7 +8938,7 @@
* Parse and compile an expression
*/
static void
-xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
+xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
xmlXPathCompAndExpr(ctxt);
CHECK_ERROR;
SKIP_BLANKS;
@@ -8882,8 +8952,13 @@
op1 = ctxt->comp->nbStep;
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 */
+ /*
+ * 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);
}
}
@@ -8910,7 +8985,7 @@
SKIP_BLANKS;
ctxt->comp->last = -1;
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, 1);
CHECK_ERROR;
if (CUR != ']') {
@@ -9202,7 +9277,7 @@
NEXT;
SKIP_BLANKS;
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, 1);
/* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
CHECK_ERROR;
@@ -9630,7 +9705,7 @@
break;
if (((t % 256) == 0) &&
(first != NULL) && (*first != NULL) &&
-#ifdef XP_FAST_NON_ELEM_COMPARISON
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
(xmlXPathCmpNodesExt(*first, cur) >= 0))
#else
(xmlXPathCmpNodes(*first, cur) >= 0))
@@ -9640,7 +9715,7 @@
break;
if (((t % 256) == 0) &&
(last != NULL) && (*last != NULL) &&
-#ifdef XP_FAST_NON_ELEM_COMPARISON
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
(xmlXPathCmpNodesExt(cur, *last) >= 0))
#else
(xmlXPathCmpNodes(cur, *last) >= 0))
@@ -10231,6 +10306,10 @@
return(t);
}
+static int
+xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
+ xmlXPathStepOpPtr op, xmlNodePtr * first);
+
/**
* xmlXPathCompOpEvalFirst:
* @ctxt: the XPath parser context with the compiled expression
@@ -10267,6 +10346,13 @@
/*
* 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)
xmlXPathNodeSetSort(ctxt->value->nodesetval);
*first = ctxt->value->nodesetval->nodeTab[0];
@@ -10354,9 +10440,15 @@
CHECK_ERROR0;
if ((ctxt->value != NULL)
&& (ctxt->value->type == XPATH_NODESET)
- && (ctxt->value->nodesetval != NULL))
+ && (ctxt->value->nodesetval != NULL)
+ && (ctxt->value->nodesetval->nodeNr > 1))
xmlXPathNodeSetSort(ctxt->value->nodesetval);
return (total);
+#ifdef XP_OPTIMIZED_FILTER_FIRST
+ case XPATH_OP_FILTER:
+ total =+ xmlXPathCompOpEvalFilterFirst(ctxt, op, first);
+ return (total);
+#endif
default:
return (xmlXPathCompOpEval(ctxt, op));
}
@@ -10514,6 +10606,295 @@
}
}
+#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:
* @ctxt: the XPath parser context with the compiled expression
@@ -10886,8 +11267,22 @@
* Optimization for ()[1] selection i.e. the first elem
*/
if ((op->ch1 != -1) && (op->ch2 != -1) &&
- (comp->steps[op->ch1].op == XPATH_OP_SORT) &&
- (comp->steps[op->ch2].op == XPATH_OP_VALUE)) {
+#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) &&
+#endif
+ (comp->steps[op->ch2].op == XPATH_OP_VALUE)) { /* 12 */
xmlXPathObjectPtr val;
val = comp->steps[op->ch2].value4;
@@ -11087,6 +11482,7 @@
ctxt->context->node = oldnode;
CHECK_ERROR0;
} else {
+ tmp = NULL;
/*
* Initialize the new set.
* Also set the xpath document in case things like
@@ -11103,7 +11499,12 @@
if ((oldset->nodeTab[i]->type != XML_NAMESPACE_DECL) &&
(oldset->nodeTab[i]->doc != NULL))
ctxt->context->doc = oldset->nodeTab[i]->doc;
- tmp = xmlXPathNewNodeSet(ctxt->context->node);
+ 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;
@@ -11133,12 +11534,21 @@
if (res != NULL)
xmlXPathFreeObject(res);
if (ctxt->value == tmp) {
- res = valuePop(ctxt);
- xmlXPathFreeObject(res);
- }
+ 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;
}
+ if (tmp != NULL)
+ xmlXPathFreeObject(tmp);
/*
* The result is used as the new evaluation set.
@@ -11783,7 +12193,7 @@
xmlXPathInit();
pctxt = xmlXPathNewParserContext(str, ctxt);
- xmlXPathCompileExpr(pctxt);
+ xmlXPathCompileExpr(pctxt, 1);
if( pctxt->error != XPATH_EXPRESSION_OK )
{
@@ -11932,7 +12342,7 @@
} else
#endif
{
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, 1);
}
CHECK_ERROR;
xmlXPathRunEval(ctxt);