summaryrefslogtreecommitdiff
path: root/xpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'xpath.c')
-rw-r--r--xpath.c3512
1 files changed, 3093 insertions, 419 deletions
diff --git a/xpath.c b/xpath.c
index 6734145..c6c2274 100644
--- a/xpath.c
+++ b/xpath.c
@@ -2,7 +2,7 @@
* xpath.c: XML Path Language implementation
* XPath is a language for addressing parts of an XML document,
* designed to be used by both XSLT and XPointer
- *
+ *f
* Reference: W3C Recommendation 16 November 1999
* http://www.w3.org/TR/1999/REC-xpath-19991116
* Public reference:
@@ -75,6 +75,32 @@
* only element-nodes and the document node.
*/
#define XP_PATTERN_TO_ANY_NODE_ENABLED
+
+/*
+* 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
+* xmlXPathOrderDocElems() was called in order to index the elements of
+* a tree in document order; Libxslt does such an indexing, thus it will
+* benefit from this optimization.
+*/
+#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
+
+/*
+* XP_DEBUG_OBJ_USAGE:
+* Internal flag to enable tracking of how much XPath objects have been
+* created.
+*/
+/* #define XP_DEBUG_OBJ_USAGE */
+
/*
* TODO:
* There are a few spots where some tests are done which depend upon ascii
@@ -85,6 +111,16 @@
#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
/************************************************************************
* *
+ * Forward declarations *
+ * *
+ ************************************************************************/
+static void
+xmlXPathFreeValueTree(xmlNodeSetPtr obj);
+static void
+xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj);
+
+/************************************************************************
+ * *
* Floating point stuff *
* *
************************************************************************/
@@ -332,6 +368,10 @@ xmlXPathErr(xmlXPathParserContextPtr ctxt, int error)
xmlXPathErrorMessages[error]);
return;
}
+
+ /* cleanup current last error */
+ xmlResetError(&ctxt->context->lastError);
+
ctxt->context->lastError.domain = XML_FROM_XPATH;
ctxt->context->lastError.code = error + XML_XPATH_EXPRESSION_OK -
XPATH_EXPRESSION_OK;
@@ -369,6 +409,101 @@ xmlXPatherror(xmlXPathParserContextPtr ctxt, const char *file ATTRIBUTE_UNUSED,
xmlXPathErr(ctxt, no);
}
+/************************************************************************
+ * *
+ * Utilities *
+ * *
+ ************************************************************************/
+
+/**
+ * xsltPointerList:
+ *
+ * Pointer-list for various purposes.
+ */
+typedef struct _xmlPointerList xmlPointerList;
+typedef xmlPointerList *xmlPointerListPtr;
+struct _xmlPointerList {
+ void **items;
+ int number;
+ int size;
+};
+/*
+* TODO: Since such a list-handling is used in xmlschemas.c and libxslt
+* and here, we should make the functions public.
+*/
+static int
+xmlPointerListAddSize(xmlPointerListPtr list,
+ void *item,
+ int initialSize)
+{
+ if (list->items == NULL) {
+ if (initialSize <= 0)
+ initialSize = 1;
+ list->items = (void **) xmlMalloc(
+ initialSize * sizeof(void *));
+ if (list->items == NULL) {
+ xmlXPathErrMemory(NULL,
+ "xmlPointerListCreate: allocating item\n");
+ return(-1);
+ }
+ list->number = 0;
+ list->size = initialSize;
+ } else if (list->size <= list->number) {
+ list->size *= 2;
+ list->items = (void **) xmlRealloc(list->items,
+ list->size * sizeof(void *));
+ if (list->items == NULL) {
+ xmlXPathErrMemory(NULL,
+ "xmlPointerListCreate: re-allocating item\n");
+ list->size = 0;
+ return(-1);
+ }
+ }
+ list->items[list->number++] = item;
+ return(0);
+}
+
+/**
+ * xsltPointerListCreate:
+ *
+ * Creates an xsltPointerList structure.
+ *
+ * Returns a xsltPointerList structure or NULL in case of an error.
+ */
+static xmlPointerListPtr
+xmlPointerListCreate(int initialSize)
+{
+ xmlPointerListPtr ret;
+
+ ret = xmlMalloc(sizeof(xmlPointerList));
+ if (ret == NULL) {
+ xmlXPathErrMemory(NULL,
+ "xmlPointerListCreate: allocating item\n");
+ return (NULL);
+ }
+ memset(ret, 0, sizeof(xmlPointerList));
+ if (initialSize > 0) {
+ xmlPointerListAddSize(ret, NULL, initialSize);
+ ret->number = 0;
+ }
+ return (ret);
+}
+
+/**
+ * xsltPointerListFree:
+ *
+ * Frees the xsltPointerList structure. This does not free
+ * the content of the list.
+ */
+static void
+xmlPointerListFree(xmlPointerListPtr list)
+{
+ if (list == NULL)
+ return;
+ if (list->items != NULL)
+ xmlFree(list->items);
+ xmlFree(list);
+}
/************************************************************************
* *
@@ -391,15 +526,15 @@ typedef enum {
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
@@ -418,7 +553,7 @@ typedef enum {
AXIS_PARENT,
AXIS_PRECEDING,
AXIS_PRECEDING_SIBLING,
- AXIS_SELF
+ AXIS_SELF
} xmlXPathAxisVal;
typedef enum {
@@ -434,9 +569,10 @@ typedef enum {
NODE_TYPE_NODE = 0,
NODE_TYPE_COMMENT = XML_COMMENT_NODE,
NODE_TYPE_TEXT = XML_TEXT_NODE,
- NODE_TYPE_PI = XML_PI_NODE
+ NODE_TYPE_PI = XML_PI_NODE
} xmlXPathTypeVal;
+#define XP_REWRITE_DOS_CHILD_ELEM 1
typedef struct _xmlXPathStepOp xmlXPathStepOp;
typedef xmlXPathStepOp *xmlXPathStepOpPtr;
@@ -451,6 +587,7 @@ struct _xmlXPathStepOp {
void *value5;
void *cache;
void *cacheURI;
+ int rewriteType;
};
struct _xmlXPathCompExpr {
@@ -673,6 +810,58 @@ xmlXPathCompExprAdd(ctxt->comp, (ch1), (ch2), (op), \
/************************************************************************
* *
+ * XPath object cache structures *
+ * *
+ ************************************************************************/
+
+/* #define XP_DEFAULT_CACHE_ON */
+
+#define XP_HAS_CACHE(c) ((c != NULL) && ((c)->cache != NULL))
+
+typedef struct _xmlXPathContextCache xmlXPathContextCache;
+typedef xmlXPathContextCache *xmlXPathContextCachePtr;
+struct _xmlXPathContextCache {
+ xmlPointerListPtr nodesetObjs; /* contains xmlXPathObjectPtr */
+ xmlPointerListPtr stringObjs; /* contains xmlXPathObjectPtr */
+ xmlPointerListPtr booleanObjs; /* contains xmlXPathObjectPtr */
+ xmlPointerListPtr numberObjs; /* contains xmlXPathObjectPtr */
+ xmlPointerListPtr miscObjs; /* contains xmlXPathObjectPtr */
+ int maxNodeset;
+ int maxString;
+ int maxBoolean;
+ int maxNumber;
+ int maxMisc;
+#ifdef XP_DEBUG_OBJ_USAGE
+ int dbgCachedAll;
+ int dbgCachedNodeset;
+ int dbgCachedString;
+ int dbgCachedBool;
+ int dbgCachedNumber;
+ int dbgCachedPoint;
+ int dbgCachedRange;
+ int dbgCachedLocset;
+ int dbgCachedUsers;
+ int dbgCachedXSLTTree;
+ int dbgCachedUndefined;
+
+
+ int dbgReusedAll;
+ int dbgReusedNodeset;
+ int dbgReusedString;
+ int dbgReusedBool;
+ int dbgReusedNumber;
+ int dbgReusedPoint;
+ int dbgReusedRange;
+ int dbgReusedLocset;
+ int dbgReusedUsers;
+ int dbgReusedXSLTTree;
+ int dbgReusedUndefined;
+
+#endif
+};
+
+/************************************************************************
+ * *
* Debugging related functions *
* *
************************************************************************/
@@ -1118,10 +1307,1096 @@ xmlXPathDebugDumpCompExpr(FILE *output, xmlXPathCompExprPtr comp,
i = comp->last;
xmlXPathDebugDumpStepOp(output, comp, &comp->steps[i], depth + 1);
}
+
+#ifdef XP_DEBUG_OBJ_USAGE
+
+/*
+* XPath object usage related debugging variables.
+*/
+static int xmlXPathDebugObjCounterUndefined = 0;
+static int xmlXPathDebugObjCounterNodeset = 0;
+static int xmlXPathDebugObjCounterBool = 0;
+static int xmlXPathDebugObjCounterNumber = 0;
+static int xmlXPathDebugObjCounterString = 0;
+static int xmlXPathDebugObjCounterPoint = 0;
+static int xmlXPathDebugObjCounterRange = 0;
+static int xmlXPathDebugObjCounterLocset = 0;
+static int xmlXPathDebugObjCounterUsers = 0;
+static int xmlXPathDebugObjCounterXSLTTree = 0;
+static int xmlXPathDebugObjCounterAll = 0;
+
+static int xmlXPathDebugObjTotalUndefined = 0;
+static int xmlXPathDebugObjTotalNodeset = 0;
+static int xmlXPathDebugObjTotalBool = 0;
+static int xmlXPathDebugObjTotalNumber = 0;
+static int xmlXPathDebugObjTotalString = 0;
+static int xmlXPathDebugObjTotalPoint = 0;
+static int xmlXPathDebugObjTotalRange = 0;
+static int xmlXPathDebugObjTotalLocset = 0;
+static int xmlXPathDebugObjTotalUsers = 0;
+static int xmlXPathDebugObjTotalXSLTTree = 0;
+static int xmlXPathDebugObjTotalAll = 0;
+
+static int xmlXPathDebugObjMaxUndefined = 0;
+static int xmlXPathDebugObjMaxNodeset = 0;
+static int xmlXPathDebugObjMaxBool = 0;
+static int xmlXPathDebugObjMaxNumber = 0;
+static int xmlXPathDebugObjMaxString = 0;
+static int xmlXPathDebugObjMaxPoint = 0;
+static int xmlXPathDebugObjMaxRange = 0;
+static int xmlXPathDebugObjMaxLocset = 0;
+static int xmlXPathDebugObjMaxUsers = 0;
+static int xmlXPathDebugObjMaxXSLTTree = 0;
+static int xmlXPathDebugObjMaxAll = 0;
+
+/* REVISIT TODO: Make this static when committing */
+static void
+xmlXPathDebugObjUsageReset(xmlXPathContextPtr ctxt)
+{
+ if (ctxt != NULL) {
+ if (ctxt->cache != NULL) {
+ xmlXPathContextCachePtr cache =
+ (xmlXPathContextCachePtr) ctxt->cache;
+
+ cache->dbgCachedAll = 0;
+ cache->dbgCachedNodeset = 0;
+ cache->dbgCachedString = 0;
+ cache->dbgCachedBool = 0;
+ cache->dbgCachedNumber = 0;
+ cache->dbgCachedPoint = 0;
+ cache->dbgCachedRange = 0;
+ cache->dbgCachedLocset = 0;
+ cache->dbgCachedUsers = 0;
+ cache->dbgCachedXSLTTree = 0;
+ cache->dbgCachedUndefined = 0;
+
+ cache->dbgReusedAll = 0;
+ cache->dbgReusedNodeset = 0;
+ cache->dbgReusedString = 0;
+ cache->dbgReusedBool = 0;
+ cache->dbgReusedNumber = 0;
+ cache->dbgReusedPoint = 0;
+ cache->dbgReusedRange = 0;
+ cache->dbgReusedLocset = 0;
+ cache->dbgReusedUsers = 0;
+ cache->dbgReusedXSLTTree = 0;
+ cache->dbgReusedUndefined = 0;
+ }
+ }
+
+ xmlXPathDebugObjCounterUndefined = 0;
+ xmlXPathDebugObjCounterNodeset = 0;
+ xmlXPathDebugObjCounterBool = 0;
+ xmlXPathDebugObjCounterNumber = 0;
+ xmlXPathDebugObjCounterString = 0;
+ xmlXPathDebugObjCounterPoint = 0;
+ xmlXPathDebugObjCounterRange = 0;
+ xmlXPathDebugObjCounterLocset = 0;
+ xmlXPathDebugObjCounterUsers = 0;
+ xmlXPathDebugObjCounterXSLTTree = 0;
+ xmlXPathDebugObjCounterAll = 0;
+
+ xmlXPathDebugObjTotalUndefined = 0;
+ xmlXPathDebugObjTotalNodeset = 0;
+ xmlXPathDebugObjTotalBool = 0;
+ xmlXPathDebugObjTotalNumber = 0;
+ xmlXPathDebugObjTotalString = 0;
+ xmlXPathDebugObjTotalPoint = 0;
+ xmlXPathDebugObjTotalRange = 0;
+ xmlXPathDebugObjTotalLocset = 0;
+ xmlXPathDebugObjTotalUsers = 0;
+ xmlXPathDebugObjTotalXSLTTree = 0;
+ xmlXPathDebugObjTotalAll = 0;
+
+ xmlXPathDebugObjMaxUndefined = 0;
+ xmlXPathDebugObjMaxNodeset = 0;
+ xmlXPathDebugObjMaxBool = 0;
+ xmlXPathDebugObjMaxNumber = 0;
+ xmlXPathDebugObjMaxString = 0;
+ xmlXPathDebugObjMaxPoint = 0;
+ xmlXPathDebugObjMaxRange = 0;
+ xmlXPathDebugObjMaxLocset = 0;
+ xmlXPathDebugObjMaxUsers = 0;
+ xmlXPathDebugObjMaxXSLTTree = 0;
+ xmlXPathDebugObjMaxAll = 0;
+
+}
+
+static void
+xmlXPathDebugObjUsageRequested(xmlXPathContextPtr ctxt,
+ xmlXPathObjectType objType)
+{
+ int isCached = 0;
+
+ if (ctxt != NULL) {
+ if (ctxt->cache != NULL) {
+ xmlXPathContextCachePtr cache =
+ (xmlXPathContextCachePtr) ctxt->cache;
+
+ isCached = 1;
+
+ cache->dbgReusedAll++;
+ switch (objType) {
+ case XPATH_UNDEFINED:
+ cache->dbgReusedUndefined++;
+ break;
+ case XPATH_NODESET:
+ cache->dbgReusedNodeset++;
+ break;
+ case XPATH_BOOLEAN:
+ cache->dbgReusedBool++;
+ break;
+ case XPATH_NUMBER:
+ cache->dbgReusedNumber++;
+ break;
+ case XPATH_STRING:
+ cache->dbgReusedString++;
+ break;
+ case XPATH_POINT:
+ cache->dbgReusedPoint++;
+ break;
+ case XPATH_RANGE:
+ cache->dbgReusedRange++;
+ break;
+ case XPATH_LOCATIONSET:
+ cache->dbgReusedLocset++;
+ break;
+ case XPATH_USERS:
+ cache->dbgReusedUsers++;
+ break;
+ case XPATH_XSLT_TREE:
+ cache->dbgReusedXSLTTree++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ switch (objType) {
+ case XPATH_UNDEFINED:
+ if (! isCached)
+ xmlXPathDebugObjTotalUndefined++;
+ xmlXPathDebugObjCounterUndefined++;
+ if (xmlXPathDebugObjCounterUndefined >
+ xmlXPathDebugObjMaxUndefined)
+ xmlXPathDebugObjMaxUndefined =
+ xmlXPathDebugObjCounterUndefined;
+ break;
+ case XPATH_NODESET:
+ if (! isCached)
+ xmlXPathDebugObjTotalNodeset++;
+ xmlXPathDebugObjCounterNodeset++;
+ if (xmlXPathDebugObjCounterNodeset >
+ xmlXPathDebugObjMaxNodeset)
+ xmlXPathDebugObjMaxNodeset =
+ xmlXPathDebugObjCounterNodeset;
+ break;
+ case XPATH_BOOLEAN:
+ if (! isCached)
+ xmlXPathDebugObjTotalBool++;
+ xmlXPathDebugObjCounterBool++;
+ if (xmlXPathDebugObjCounterBool >
+ xmlXPathDebugObjMaxBool)
+ xmlXPathDebugObjMaxBool =
+ xmlXPathDebugObjCounterBool;
+ break;
+ case XPATH_NUMBER:
+ if (! isCached)
+ xmlXPathDebugObjTotalNumber++;
+ xmlXPathDebugObjCounterNumber++;
+ if (xmlXPathDebugObjCounterNumber >
+ xmlXPathDebugObjMaxNumber)
+ xmlXPathDebugObjMaxNumber =
+ xmlXPathDebugObjCounterNumber;
+ break;
+ case XPATH_STRING:
+ if (! isCached)
+ xmlXPathDebugObjTotalString++;
+ xmlXPathDebugObjCounterString++;
+ if (xmlXPathDebugObjCounterString >
+ xmlXPathDebugObjMaxString)
+ xmlXPathDebugObjMaxString =
+ xmlXPathDebugObjCounterString;
+ break;
+ case XPATH_POINT:
+ if (! isCached)
+ xmlXPathDebugObjTotalPoint++;
+ xmlXPathDebugObjCounterPoint++;
+ if (xmlXPathDebugObjCounterPoint >
+ xmlXPathDebugObjMaxPoint)
+ xmlXPathDebugObjMaxPoint =
+ xmlXPathDebugObjCounterPoint;
+ break;
+ case XPATH_RANGE:
+ if (! isCached)
+ xmlXPathDebugObjTotalRange++;
+ xmlXPathDebugObjCounterRange++;
+ if (xmlXPathDebugObjCounterRange >
+ xmlXPathDebugObjMaxRange)
+ xmlXPathDebugObjMaxRange =
+ xmlXPathDebugObjCounterRange;
+ break;
+ case XPATH_LOCATIONSET:
+ if (! isCached)
+ xmlXPathDebugObjTotalLocset++;
+ xmlXPathDebugObjCounterLocset++;
+ if (xmlXPathDebugObjCounterLocset >
+ xmlXPathDebugObjMaxLocset)
+ xmlXPathDebugObjMaxLocset =
+ xmlXPathDebugObjCounterLocset;
+ break;
+ case XPATH_USERS:
+ if (! isCached)
+ xmlXPathDebugObjTotalUsers++;
+ xmlXPathDebugObjCounterUsers++;
+ if (xmlXPathDebugObjCounterUsers >
+ xmlXPathDebugObjMaxUsers)
+ xmlXPathDebugObjMaxUsers =
+ xmlXPathDebugObjCounterUsers;
+ break;
+ case XPATH_XSLT_TREE:
+ if (! isCached)
+ xmlXPathDebugObjTotalXSLTTree++;
+ xmlXPathDebugObjCounterXSLTTree++;
+ if (xmlXPathDebugObjCounterXSLTTree >
+ xmlXPathDebugObjMaxXSLTTree)
+ xmlXPathDebugObjMaxXSLTTree =
+ xmlXPathDebugObjCounterXSLTTree;
+ break;
+ default:
+ break;
+ }
+ if (! isCached)
+ xmlXPathDebugObjTotalAll++;
+ xmlXPathDebugObjCounterAll++;
+ if (xmlXPathDebugObjCounterAll >
+ xmlXPathDebugObjMaxAll)
+ xmlXPathDebugObjMaxAll =
+ xmlXPathDebugObjCounterAll;
+}
+
+static void
+xmlXPathDebugObjUsageReleased(xmlXPathContextPtr ctxt,
+ xmlXPathObjectType objType)
+{
+ int isCached = 0;
+
+ if (ctxt != NULL) {
+ if (ctxt->cache != NULL) {
+ xmlXPathContextCachePtr cache =
+ (xmlXPathContextCachePtr) ctxt->cache;
+
+ isCached = 1;
+
+ cache->dbgCachedAll++;
+ switch (objType) {
+ case XPATH_UNDEFINED:
+ cache->dbgCachedUndefined++;
+ break;
+ case XPATH_NODESET:
+ cache->dbgCachedNodeset++;
+ break;
+ case XPATH_BOOLEAN:
+ cache->dbgCachedBool++;
+ break;
+ case XPATH_NUMBER:
+ cache->dbgCachedNumber++;
+ break;
+ case XPATH_STRING:
+ cache->dbgCachedString++;
+ break;
+ case XPATH_POINT:
+ cache->dbgCachedPoint++;
+ break;
+ case XPATH_RANGE:
+ cache->dbgCachedRange++;
+ break;
+ case XPATH_LOCATIONSET:
+ cache->dbgCachedLocset++;
+ break;
+ case XPATH_USERS:
+ cache->dbgCachedUsers++;
+ break;
+ case XPATH_XSLT_TREE:
+ cache->dbgCachedXSLTTree++;
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
+ switch (objType) {
+ case XPATH_UNDEFINED:
+ xmlXPathDebugObjCounterUndefined--;
+ break;
+ case XPATH_NODESET:
+ xmlXPathDebugObjCounterNodeset--;
+ break;
+ case XPATH_BOOLEAN:
+ xmlXPathDebugObjCounterBool--;
+ break;
+ case XPATH_NUMBER:
+ xmlXPathDebugObjCounterNumber--;
+ break;
+ case XPATH_STRING:
+ xmlXPathDebugObjCounterString--;
+ break;
+ case XPATH_POINT:
+ xmlXPathDebugObjCounterPoint--;
+ break;
+ case XPATH_RANGE:
+ xmlXPathDebugObjCounterRange--;
+ break;
+ case XPATH_LOCATIONSET:
+ xmlXPathDebugObjCounterLocset--;
+ break;
+ case XPATH_USERS:
+ xmlXPathDebugObjCounterUsers--;
+ break;
+ case XPATH_XSLT_TREE:
+ xmlXPathDebugObjCounterXSLTTree--;
+ break;
+ default:
+ break;
+ }
+ xmlXPathDebugObjCounterAll--;
+}
+
+/* REVISIT TODO: Make this static when committing */
+static void
+xmlXPathDebugObjUsageDisplay(xmlXPathContextPtr ctxt)
+{
+ int reqAll, reqNodeset, reqString, reqBool, reqNumber,
+ reqXSLTTree, reqUndefined;
+ int caAll = 0, caNodeset = 0, caString = 0, caBool = 0,
+ caNumber = 0, caXSLTTree = 0, caUndefined = 0;
+ int reAll = 0, reNodeset = 0, reString = 0, reBool = 0,
+ reNumber = 0, reXSLTTree = 0, reUndefined = 0;
+ int leftObjs = xmlXPathDebugObjCounterAll;
+
+ reqAll = xmlXPathDebugObjTotalAll;
+ reqNodeset = xmlXPathDebugObjTotalNodeset;
+ reqString = xmlXPathDebugObjTotalString;
+ reqBool = xmlXPathDebugObjTotalBool;
+ reqNumber = xmlXPathDebugObjTotalNumber;
+ reqXSLTTree = xmlXPathDebugObjTotalXSLTTree;
+ reqUndefined = xmlXPathDebugObjTotalUndefined;
+
+ printf("# XPath object usage:\n");
+
+ if (ctxt != NULL) {
+ if (ctxt->cache != NULL) {
+ xmlXPathContextCachePtr cache =
+ (xmlXPathContextCachePtr) ctxt->cache;
+
+ reAll = cache->dbgReusedAll;
+ reqAll += reAll;
+ reNodeset = cache->dbgReusedNodeset;
+ reqNodeset += reNodeset;
+ reString = cache->dbgReusedString;
+ reqString += reString;
+ reBool = cache->dbgReusedBool;
+ reqBool += reBool;
+ reNumber = cache->dbgReusedNumber;
+ reqNumber += reNumber;
+ reXSLTTree = cache->dbgReusedXSLTTree;
+ reqXSLTTree += reXSLTTree;
+ reUndefined = cache->dbgReusedUndefined;
+ reqUndefined += reUndefined;
+
+ caAll = cache->dbgCachedAll;
+ caBool = cache->dbgCachedBool;
+ caNodeset = cache->dbgCachedNodeset;
+ caString = cache->dbgCachedString;
+ caNumber = cache->dbgCachedNumber;
+ caXSLTTree = cache->dbgCachedXSLTTree;
+ caUndefined = cache->dbgCachedUndefined;
+
+ if (cache->nodesetObjs)
+ leftObjs -= cache->nodesetObjs->number;
+ if (cache->stringObjs)
+ leftObjs -= cache->stringObjs->number;
+ if (cache->booleanObjs)
+ leftObjs -= cache->booleanObjs->number;
+ if (cache->numberObjs)
+ leftObjs -= cache->numberObjs->number;
+ if (cache->miscObjs)
+ leftObjs -= cache->miscObjs->number;
+ }
+ }
+
+ printf("# all\n");
+ printf("# total : %d\n", reqAll);
+ printf("# left : %d\n", leftObjs);
+ printf("# created: %d\n", xmlXPathDebugObjTotalAll);
+ printf("# reused : %d\n", reAll);
+ printf("# max : %d\n", xmlXPathDebugObjMaxAll);
+
+ printf("# node-sets\n");
+ printf("# total : %d\n", reqNodeset);
+ printf("# created: %d\n", xmlXPathDebugObjTotalNodeset);
+ printf("# reused : %d\n", reNodeset);
+ printf("# max : %d\n", xmlXPathDebugObjMaxNodeset);
+
+ printf("# strings\n");
+ printf("# total : %d\n", reqString);
+ printf("# created: %d\n", xmlXPathDebugObjTotalString);
+ printf("# reused : %d\n", reString);
+ printf("# max : %d\n", xmlXPathDebugObjMaxString);
+
+ printf("# booleans\n");
+ printf("# total : %d\n", reqBool);
+ printf("# created: %d\n", xmlXPathDebugObjTotalBool);
+ printf("# reused : %d\n", reBool);
+ printf("# max : %d\n", xmlXPathDebugObjMaxBool);
+
+ printf("# numbers\n");
+ printf("# total : %d\n", reqNumber);
+ printf("# created: %d\n", xmlXPathDebugObjTotalNumber);
+ printf("# reused : %d\n", reNumber);
+ printf("# max : %d\n", xmlXPathDebugObjMaxNumber);
+
+ printf("# XSLT result tree fragments\n");
+ printf("# total : %d\n", reqXSLTTree);
+ printf("# created: %d\n", xmlXPathDebugObjTotalXSLTTree);
+ printf("# reused : %d\n", reXSLTTree);
+ printf("# max : %d\n", xmlXPathDebugObjMaxXSLTTree);
+
+ printf("# undefined\n");
+ printf("# total : %d\n", reqUndefined);
+ printf("# created: %d\n", xmlXPathDebugObjTotalUndefined);
+ printf("# reused : %d\n", reUndefined);
+ printf("# max : %d\n", xmlXPathDebugObjMaxUndefined);
+
+}
+
+#endif /* XP_DEBUG_OBJ_USAGE */
+
#endif /* LIBXML_DEBUG_ENABLED */
/************************************************************************
* *
+ * XPath object caching *
+ * *
+ ************************************************************************/
+
+/**
+ * xmlXPathNewCache:
+ *
+ * Create a new object cache
+ *
+ * Returns the xmlXPathCache just allocated.
+ */
+static xmlXPathContextCachePtr
+xmlXPathNewCache(void)
+{
+ xmlXPathContextCachePtr ret;
+
+ ret = (xmlXPathContextCachePtr) xmlMalloc(sizeof(xmlXPathContextCache));
+ if (ret == NULL) {
+ xmlXPathErrMemory(NULL, "creating object cache\n");
+ return(NULL);
+ }
+ memset(ret, 0 , (size_t) sizeof(xmlXPathContextCache));
+ ret->maxNodeset = 100;
+ ret->maxString = 100;
+ ret->maxBoolean = 100;
+ ret->maxNumber = 100;
+ ret->maxMisc = 100;
+ return(ret);
+}
+
+static void
+xmlXPathCacheFreeObjectList(xmlPointerListPtr list)
+{
+ int i;
+ xmlXPathObjectPtr obj;
+
+ if (list == NULL)
+ return;
+
+ for (i = 0; i < list->number; i++) {
+ obj = list->items[i];
+ /*
+ * Note that it is already assured that we don't need to
+ * look out for namespace nodes in the node-set.
+ */
+ if (obj->nodesetval != NULL) {
+ if (obj->nodesetval->nodeTab != NULL)
+ xmlFree(obj->nodesetval->nodeTab);
+ xmlFree(obj->nodesetval);
+ }
+ xmlFree(obj);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjCounterAll--;
+#endif
+ }
+ xmlPointerListFree(list);
+}
+
+static void
+xmlXPathFreeCache(xmlXPathContextCachePtr cache)
+{
+ if (cache == NULL)
+ return;
+ if (cache->nodesetObjs)
+ xmlXPathCacheFreeObjectList(cache->nodesetObjs);
+ if (cache->stringObjs)
+ xmlXPathCacheFreeObjectList(cache->stringObjs);
+ if (cache->booleanObjs)
+ xmlXPathCacheFreeObjectList(cache->booleanObjs);
+ if (cache->numberObjs)
+ xmlXPathCacheFreeObjectList(cache->numberObjs);
+ if (cache->miscObjs)
+ xmlXPathCacheFreeObjectList(cache->miscObjs);
+ xmlFree(cache);
+}
+
+/**
+ * xmlXPathContextSetCache:
+ *
+ * @ctxt: the XPath context
+ * @active: enables/disables (creates/frees) the cache
+ * @value: a value with semantics dependant on @options
+ * @options: options (currently only the value 0 is used)
+ *
+ * Creates/frees an object cache on the XPath context.
+ * If activates XPath objects (xmlXPathObject) will be cached internally
+ * to be reused.
+ * @options:
+ * 0: This will set the XPath object caching:
+ * @value:
+ * This will set the maximum number of XPath objects
+ * to be cached per slot
+ * There are 5 slots for: node-set, string, number, boolean, and
+ * misc objects. Use <0 for the default number (100).
+ * Other values for @options have currently no effect.
+ *
+ * Returns 0 if the setting succeeded, and -1 on API or internal errors.
+ */
+int
+xmlXPathContextSetCache(xmlXPathContextPtr ctxt,
+ int active,
+ int value,
+ int options)
+{
+ if (ctxt == NULL)
+ return(-1);
+ if (active) {
+ xmlXPathContextCachePtr cache;
+
+ if (ctxt->cache == NULL) {
+ ctxt->cache = xmlXPathNewCache();
+ if (ctxt->cache == NULL)
+ return(-1);
+ }
+ cache = (xmlXPathContextCachePtr) ctxt->cache;
+ if (options == 0) {
+ if (value < 0)
+ value = 100;
+ cache->maxNodeset = value;
+ cache->maxString = value;
+ cache->maxNumber = value;
+ cache->maxBoolean = value;
+ cache->maxMisc = value;
+ }
+ } else if (ctxt->cache != NULL) {
+ xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
+ ctxt->cache = NULL;
+ }
+ return(0);
+}
+
+/**
+ * xmlXPathCacheWrapNodeSet:
+ * @ctxt: the XPath context
+ * @val: the NodePtr value
+ *
+ * This is the cached version of xmlXPathWrapNodeSet().
+ * Wrap the Nodeset @val in a new xmlXPathObjectPtr
+ *
+ * Returns the created or reused object.
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheWrapNodeSet(xmlXPathContextPtr ctxt, xmlNodeSetPtr val)
+{
+ if ((ctxt != NULL) && (ctxt->cache != NULL)) {
+ xmlXPathContextCachePtr cache =
+ (xmlXPathContextCachePtr) ctxt->cache;
+
+ if ((cache->miscObjs != NULL) &&
+ (cache->miscObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->miscObjs->items[--cache->miscObjs->number];
+ ret->type = XPATH_NODESET;
+ ret->nodesetval = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
+#endif
+ return(ret);
+ }
+ }
+
+ return(xmlXPathWrapNodeSet(val));
+
+}
+
+/**
+ * xmlXPathCacheWrapString:
+ * @ctxt: the XPath context
+ * @val: the xmlChar * value
+ *
+ * This is the cached version of xmlXPathWrapString().
+ * Wraps the @val string into an XPath object.
+ *
+ * Returns the created or reused object.
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheWrapString(xmlXPathContextPtr ctxt, xmlChar *val)
+{
+ if ((ctxt != NULL) && (ctxt->cache != NULL)) {
+ xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
+
+ if ((cache->stringObjs != NULL) &&
+ (cache->stringObjs->number != 0))
+ {
+
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->stringObjs->items[--cache->stringObjs->number];
+ ret->type = XPATH_STRING;
+ ret->stringval = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
+#endif
+ return(ret);
+ } else if ((cache->miscObjs != NULL) &&
+ (cache->miscObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+ /*
+ * Fallback to misc-cache.
+ */
+ ret = (xmlXPathObjectPtr)
+ cache->miscObjs->items[--cache->miscObjs->number];
+
+ ret->type = XPATH_STRING;
+ ret->stringval = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
+#endif
+ return(ret);
+ }
+ }
+ return(xmlXPathWrapString(val));
+}
+
+/**
+ * xmlXPathCacheNewNodeSet:
+ * @ctxt: the XPath context
+ * @val: the NodePtr value
+ *
+ * This is the cached version of xmlXPathNewNodeSet().
+ * Acquire an xmlXPathObjectPtr of type NodeSet and initialize
+ * it with the single Node @val
+ *
+ * Returns the created or reused object.
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheNewNodeSet(xmlXPathContextPtr ctxt, xmlNodePtr val)
+{
+ if ((ctxt != NULL) && (ctxt->cache)) {
+ xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
+
+ if ((cache->nodesetObjs != NULL) &&
+ (cache->nodesetObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+ /*
+ * Use the nodset-cache.
+ */
+ ret = (xmlXPathObjectPtr)
+ cache->nodesetObjs->items[--cache->nodesetObjs->number];
+ ret->type = XPATH_NODESET;
+ ret->boolval = 0;
+ if (val) {
+ if ((ret->nodesetval->nodeMax == 0) ||
+ (val->type == XML_NAMESPACE_DECL))
+ {
+ xmlXPathNodeSetAddUnique(ret->nodesetval, val);
+ } else {
+ ret->nodesetval->nodeTab[0] = val;
+ ret->nodesetval->nodeNr = 1;
+ }
+ }
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
+#endif
+ return(ret);
+ } else if ((cache->miscObjs != NULL) &&
+ (cache->miscObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+ /*
+ * Fallback to misc-cache.
+ */
+
+ ret = (xmlXPathObjectPtr)
+ cache->miscObjs->items[--cache->miscObjs->number];
+
+ ret->type = XPATH_NODESET;
+ ret->boolval = 0;
+ ret->nodesetval = xmlXPathNodeSetCreate(val);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_NODESET);
+#endif
+ return(ret);
+ }
+ }
+ return(xmlXPathNewNodeSet(val));
+}
+
+/**
+ * xmlXPathCacheNewCString:
+ * @ctxt: the XPath context
+ * @val: the char * value
+ *
+ * This is the cached version of xmlXPathNewCString().
+ * Acquire an xmlXPathObjectPtr of type string and of value @val
+ *
+ * Returns the created or reused object.
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheNewCString(xmlXPathContextPtr ctxt, const char *val)
+{
+ if ((ctxt != NULL) && (ctxt->cache)) {
+ xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
+
+ if ((cache->stringObjs != NULL) &&
+ (cache->stringObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->stringObjs->items[--cache->stringObjs->number];
+
+ ret->type = XPATH_STRING;
+ ret->stringval = xmlStrdup(BAD_CAST val);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
+#endif
+ return(ret);
+ } else if ((cache->miscObjs != NULL) &&
+ (cache->miscObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->miscObjs->items[--cache->miscObjs->number];
+
+ ret->type = XPATH_STRING;
+ ret->stringval = xmlStrdup(BAD_CAST val);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
+#endif
+ return(ret);
+ }
+ }
+ return(xmlXPathNewCString(val));
+}
+
+/**
+ * xmlXPathCacheNewString:
+ * @ctxt: the XPath context
+ * @val: the xmlChar * value
+ *
+ * This is the cached version of xmlXPathNewString().
+ * Acquire an xmlXPathObjectPtr of type string and of value @val
+ *
+ * Returns the created or reused object.
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheNewString(xmlXPathContextPtr ctxt, const xmlChar *val)
+{
+ if ((ctxt != NULL) && (ctxt->cache)) {
+ xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
+
+ if ((cache->stringObjs != NULL) &&
+ (cache->stringObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->stringObjs->items[--cache->stringObjs->number];
+ ret->type = XPATH_STRING;
+ if (val != NULL)
+ ret->stringval = xmlStrdup(val);
+ else
+ ret->stringval = xmlStrdup((const xmlChar *)"");
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
+#endif
+ return(ret);
+ } else if ((cache->miscObjs != NULL) &&
+ (cache->miscObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->miscObjs->items[--cache->miscObjs->number];
+
+ ret->type = XPATH_STRING;
+ if (val != NULL)
+ ret->stringval = xmlStrdup(val);
+ else
+ ret->stringval = xmlStrdup((const xmlChar *)"");
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_STRING);
+#endif
+ return(ret);
+ }
+ }
+ return(xmlXPathNewString(val));
+}
+
+/**
+ * xmlXPathCacheNewBoolean:
+ * @ctxt: the XPath context
+ * @val: the boolean value
+ *
+ * This is the cached version of xmlXPathNewBoolean().
+ * Acquires an xmlXPathObjectPtr of type boolean and of value @val
+ *
+ * Returns the created or reused object.
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheNewBoolean(xmlXPathContextPtr ctxt, int val)
+{
+ if ((ctxt != NULL) && (ctxt->cache)) {
+ xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
+
+ if ((cache->booleanObjs != NULL) &&
+ (cache->booleanObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->booleanObjs->items[--cache->booleanObjs->number];
+ ret->type = XPATH_BOOLEAN;
+ ret->boolval = (val != 0);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
+#endif
+ return(ret);
+ } else if ((cache->miscObjs != NULL) &&
+ (cache->miscObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->miscObjs->items[--cache->miscObjs->number];
+
+ ret->type = XPATH_BOOLEAN;
+ ret->boolval = (val != 0);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_BOOLEAN);
+#endif
+ return(ret);
+ }
+ }
+ return(xmlXPathNewBoolean(val));
+}
+
+/**
+ * xmlXPathCacheNewFloat:
+ * @ctxt: the XPath context
+ * @val: the double value
+ *
+ * This is the cached version of xmlXPathNewFloat().
+ * Acquires an xmlXPathObjectPtr of type double and of value @val
+ *
+ * Returns the created or reused object.
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheNewFloat(xmlXPathContextPtr ctxt, double val)
+{
+ if ((ctxt != NULL) && (ctxt->cache)) {
+ xmlXPathContextCachePtr cache = (xmlXPathContextCachePtr) ctxt->cache;
+
+ if ((cache->numberObjs != NULL) &&
+ (cache->numberObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->numberObjs->items[--cache->numberObjs->number];
+ ret->type = XPATH_NUMBER;
+ ret->floatval = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
+#endif
+ return(ret);
+ } else if ((cache->miscObjs != NULL) &&
+ (cache->miscObjs->number != 0))
+ {
+ xmlXPathObjectPtr ret;
+
+ ret = (xmlXPathObjectPtr)
+ cache->miscObjs->items[--cache->miscObjs->number];
+
+ ret->type = XPATH_NUMBER;
+ ret->floatval = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(ctxt, XPATH_NUMBER);
+#endif
+ return(ret);
+ }
+ }
+ return(xmlXPathNewFloat(val));
+}
+
+/**
+ * xmlXPathCacheConvertString:
+ * @ctxt: the XPath context
+ * @val: an XPath object
+ *
+ * This is the cached version of xmlXPathConvertString().
+ * Converts an existing object to its string() equivalent
+ *
+ * Returns a created or reused object, the old one is freed (cached)
+ * (or the operation is done directly on @val)
+ */
+
+static xmlXPathObjectPtr
+xmlXPathCacheConvertString(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
+ xmlChar *res = NULL;
+
+ if (val == NULL)
+ return(xmlXPathCacheNewCString(ctxt, ""));
+
+ switch (val->type) {
+ case XPATH_UNDEFINED:
+#ifdef DEBUG_EXPR
+ xmlGenericError(xmlGenericErrorContext, "STRING: undefined\n");
+#endif
+ break;
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ res = xmlXPathCastNodeSetToString(val->nodesetval);
+ break;
+ case XPATH_STRING:
+ return(val);
+ case XPATH_BOOLEAN:
+ res = xmlXPathCastBooleanToString(val->boolval);
+ break;
+ case XPATH_NUMBER:
+ res = xmlXPathCastNumberToString(val->floatval);
+ break;
+ case XPATH_USERS:
+ case XPATH_POINT:
+ case XPATH_RANGE:
+ case XPATH_LOCATIONSET:
+ TODO;
+ break;
+ }
+ xmlXPathReleaseObject(ctxt, val);
+ if (res == NULL)
+ return(xmlXPathCacheNewCString(ctxt, ""));
+ return(xmlXPathCacheWrapString(ctxt, res));
+}
+
+/**
+ * xmlXPathCacheObjectCopy:
+ * @ctxt: the XPath context
+ * @val: the original object
+ *
+ * This is the cached version of xmlXPathObjectCopy().
+ * Acquire a copy of a given object
+ *
+ * Returns a created or reused created object.
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheObjectCopy(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val)
+{
+ if (val == NULL)
+ return(NULL);
+
+ switch (val->type) {
+ case XPATH_NODESET:
+ if (XP_HAS_CACHE(ctxt))
+ return(xmlXPathCacheWrapNodeSet(ctxt,
+ xmlXPathNodeSetMerge(NULL, val->nodesetval)));
+ case XPATH_STRING:
+ if (XP_HAS_CACHE(ctxt))
+ return(xmlXPathCacheNewString(ctxt, val->stringval));
+ case XPATH_BOOLEAN:
+ if (XP_HAS_CACHE(ctxt))
+ return(xmlXPathCacheNewBoolean(ctxt, val->boolval));
+ case XPATH_NUMBER:
+ if (XP_HAS_CACHE(ctxt))
+ return(xmlXPathCacheNewFloat(ctxt, val->floatval));
+ default:
+ break;
+ }
+ return(xmlXPathObjectCopy(val));
+}
+
+/**
+ * xmlXPathCacheConvertBoolean:
+ * @ctxt: the XPath context
+ * @val: an XPath object
+ *
+ * This is the cached version of xmlXPathConvertBoolean().
+ * Converts an existing object to its boolean() equivalent
+ *
+ * Returns a created or reused object, the old one is freed (or the operation
+ * is done directly on @val)
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheConvertBoolean(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
+ xmlXPathObjectPtr ret;
+
+ if (val == NULL)
+ return(xmlXPathCacheNewBoolean(ctxt, 0));
+ if (val->type == XPATH_BOOLEAN)
+ return(val);
+ ret = xmlXPathCacheNewBoolean(ctxt, xmlXPathCastToBoolean(val));
+ xmlXPathReleaseObject(ctxt, val);
+ return(ret);
+}
+
+/**
+ * xmlXPathCacheConvertNumber:
+ * @ctxt: the XPath context
+ * @val: an XPath object
+ *
+ * This is the cached version of xmlXPathConvertNumber().
+ * Converts an existing object to its number() equivalent
+ *
+ * Returns a created or reused object, the old one is freed (or the operation
+ * is done directly on @val)
+ */
+static xmlXPathObjectPtr
+xmlXPathCacheConvertNumber(xmlXPathContextPtr ctxt, xmlXPathObjectPtr val) {
+ xmlXPathObjectPtr ret;
+
+ if (val == NULL)
+ return(xmlXPathCacheNewFloat(ctxt, 0.0));
+ if (val->type == XPATH_NUMBER)
+ return(val);
+ ret = xmlXPathCacheNewFloat(ctxt, xmlXPathCastToNumber(val));
+ xmlXPathReleaseObject(ctxt, val);
+ return(ret);
+}
+
+/************************************************************************
+ * *
* Parser stacks related functions and macros *
* *
************************************************************************/
@@ -1204,7 +2479,7 @@ xmlXPathPopBoolean (xmlXPathParserContextPtr ctxt) {
ret = xmlXPathCastToBoolean(obj);
else
ret = obj->boolval;
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(ctxt->context, obj);
return(ret);
}
@@ -1231,7 +2506,7 @@ xmlXPathPopNumber (xmlXPathParserContextPtr ctxt) {
ret = xmlXPathCastToNumber(obj);
else
ret = obj->floatval;
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(ctxt->context, obj);
return(ret);
}
@@ -1258,7 +2533,7 @@ xmlXPathPopString (xmlXPathParserContextPtr ctxt) {
/* TODO: needs refactoring somewhere else */
if (obj->stringval == ret)
obj->stringval = NULL;
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(ctxt->context, obj);
return(ret);
}
@@ -1292,7 +2567,8 @@ xmlXPathPopNodeSet (xmlXPathParserContextPtr ctxt) {
if (obj->boolval && obj->user != NULL)
xmlFreeNodeList((xmlNodePtr) obj->user);
#endif
- xmlXPathFreeNodeSetList(obj);
+ obj->nodesetval = NULL;
+ xmlXPathReleaseObject(ctxt->context, obj);
return(ret);
}
@@ -1320,7 +2596,8 @@ xmlXPathPopExternal (xmlXPathParserContextPtr ctxt) {
}
obj = valuePop(ctxt);
ret = obj->user;
- xmlXPathFreeObject(obj);
+ obj->user = NULL;
+ xmlXPathReleaseObject(ctxt->context, obj);
return(ret);
}
@@ -1691,6 +2968,297 @@ xmlXPathCmpNodes(xmlNodePtr node1, xmlNodePtr node2) {
return(-1); /* assume there is no sibling list corruption */
}
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
+/**
+ * xmlXPathCmpNodesExt:
+ * @node1: the first node
+ * @node2: the second node
+ *
+ * Compare two nodes w.r.t document order.
+ * This one is optimized for handling of non-element nodes.
+ *
+ * Returns -2 in case of error 1 if first point < second point, 0 if
+ * it's the same node, -1 otherwise
+ */
+static int
+xmlXPathCmpNodesExt(xmlNodePtr node1, xmlNodePtr node2) {
+ int depth1, depth2;
+ int misc = 0, precedence1 = 0, precedence2 = 0;
+ xmlNodePtr miscNode1 = NULL, miscNode2 = NULL;
+ xmlNodePtr cur, root;
+ long l1, l2;
+
+ if ((node1 == NULL) || (node2 == NULL))
+ return(-2);
+
+ if (node1 == node2)
+ return(0);
+
+ /*
+ * a couple of optimizations which will avoid computations in most cases
+ */
+ switch (node1->type) {
+ case XML_ELEMENT_NODE:
+ if (node2->type == XML_ELEMENT_NODE) {
+ if ((0 > (long) node1->content) && /* TODO: Would a != 0 suffice here? */
+ (0 > (long) node2->content) &&
+ (node1->doc == node2->doc))
+ {
+ l1 = -((long) node1->content);
+ l2 = -((long) node2->content);
+ if (l1 < l2)
+ return(1);
+ if (l1 > l2)
+ return(-1);
+ } else
+ goto turtle_comparison;
+ }
+ break;
+ case XML_ATTRIBUTE_NODE:
+ precedence1 = 1; /* element is owner */
+ miscNode1 = node1;
+ node1 = node1->parent;
+ misc = 1;
+ break;
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_COMMENT_NODE:
+ case XML_PI_NODE: {
+ miscNode1 = node1;
+ /*
+ * Find nearest element node.
+ */
+ if (node1->prev != NULL) {
+ do {
+ node1 = node1->prev;
+ if (node1->type == XML_ELEMENT_NODE) {
+ precedence1 = 3; /* element in prev-sibl axis */
+ break;
+ }
+ if (node1->prev == NULL) {
+ precedence1 = 2; /* element is parent */
+ /*
+ * URGENT TODO: Are there any cases, where the
+ * parent of such a node is not an element node?
+ */
+ node1 = node1->parent;
+ break;
+ }
+ } while (1);
+ } else {
+ precedence1 = 2; /* element is parent */
+ node1 = node1->parent;
+ }
+ if ((node1 == NULL) || (node1->type != XML_ELEMENT_NODE)) {
+ /*
+ * Fallback for whatever case.
+ */
+ node1 = miscNode1;
+ precedence1 = 0;
+ } else
+ misc = 1;
+ }
+ break;
+ case XML_NAMESPACE_DECL:
+ /*
+ * TODO: why do we return 1 for namespace nodes?
+ */
+ return(1);
+ default:
+ break;
+ }
+ switch (node2->type) {
+ case XML_ELEMENT_NODE:
+ break;
+ case XML_ATTRIBUTE_NODE:
+ precedence2 = 1; /* element is owner */
+ miscNode2 = node2;
+ node2 = node2->parent;
+ misc = 1;
+ break;
+ case XML_TEXT_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_COMMENT_NODE:
+ case XML_PI_NODE: {
+ miscNode2 = node2;
+ if (node2->prev != NULL) {
+ do {
+ node2 = node2->prev;
+ if (node2->type == XML_ELEMENT_NODE) {
+ precedence2 = 3; /* element in prev-sibl axis */
+ break;
+ }
+ if (node2->prev == NULL) {
+ precedence2 = 2; /* element is parent */
+ node2 = node2->parent;
+ break;
+ }
+ } while (1);
+ } else {
+ precedence2 = 2; /* element is parent */
+ node2 = node2->parent;
+ }
+ if ((node2 == NULL) || (node2->type != XML_ELEMENT_NODE) ||
+ (0 <= (long) node1->content))
+ {
+ node2 = miscNode2;
+ precedence2 = 0;
+ } else
+ misc = 1;
+ }
+ break;
+ case XML_NAMESPACE_DECL:
+ return(1);
+ default:
+ break;
+ }
+ if (misc) {
+ if (node1 == node2) {
+ if (precedence1 == precedence2) {
+ /*
+ * The ugly case; but normally there aren't many
+ * adjacent non-element nodes around.
+ */
+ cur = miscNode2->prev;
+ while (cur != NULL) {
+ if (cur == miscNode1)
+ return(1);
+ if (cur->type == XML_ELEMENT_NODE)
+ return(-1);
+ cur = cur->prev;
+ }
+ return (-1);
+ } else {
+ /*
+ * Evaluate based on higher precedence wrt to the element.
+ * TODO: This assumes attributes are sorted before content.
+ * Is this 100% correct?
+ */
+ if (precedence1 < precedence2)
+ return(1);
+ else
+ return(-1);
+ }
+ }
+ /*
+ * Special case: One of the helper-elements is contained by the other.
+ * <foo>
+ * <node2>
+ * <node1>Text-1(precedence1 == 2)</node1>
+ * </node2>
+ * Text-6(precedence2 == 3)
+ * </foo>
+ */
+ if ((precedence2 == 3) && (precedence1 > 1)) {
+ cur = node1->parent;
+ while (cur) {
+ if (cur == node2)
+ return(1);
+ cur = cur->parent;
+ }
+ }
+ if ((precedence1 == 3) && (precedence2 > 1)) {
+ cur = node2->parent;
+ while (cur) {
+ if (cur == node1)
+ return(-1);
+ cur = cur->parent;
+ }
+ }
+ }
+
+ /*
+ * Speedup using document order if availble.
+ */
+ if ((node1->type == XML_ELEMENT_NODE) &&
+ (node2->type == XML_ELEMENT_NODE) &&
+ (0 > (long) node1->content) &&
+ (0 > (long) node2->content) &&
+ (node1->doc == node2->doc)) {
+
+ l1 = -((long) node1->content);
+ l2 = -((long) node2->content);
+ if (l1 < l2)
+ return(1);
+ if (l1 > l2)
+ return(-1);
+ }
+
+turtle_comparison:
+
+ if (node1 == node2->prev)
+ return(1);
+ if (node1 == node2->next)
+ return(-1);
+ /*
+ * compute depth to root
+ */
+ for (depth2 = 0, cur = node2;cur->parent != NULL;cur = cur->parent) {
+ if (cur == node1)
+ return(1);
+ depth2++;
+ }
+ root = cur;
+ for (depth1 = 0, cur = node1;cur->parent != NULL;cur = cur->parent) {
+ if (cur == node2)
+ return(-1);
+ depth1++;
+ }
+ /*
+ * Distinct document (or distinct entities :-( ) case.
+ */
+ if (root != cur) {
+ return(-2);
+ }
+ /*
+ * get the nearest common ancestor.
+ */
+ while (depth1 > depth2) {
+ depth1--;
+ node1 = node1->parent;
+ }
+ while (depth2 > depth1) {
+ depth2--;
+ node2 = node2->parent;
+ }
+ while (node1->parent != node2->parent) {
+ node1 = node1->parent;
+ node2 = node2->parent;
+ /* should not happen but just in case ... */
+ if ((node1 == NULL) || (node2 == NULL))
+ return(-2);
+ }
+ /*
+ * Find who's first.
+ */
+ if (node1 == node2->prev)
+ return(1);
+ if (node1 == node2->next)
+ return(-1);
+ /*
+ * Speedup using document order if availble.
+ */
+ if ((node1->type == XML_ELEMENT_NODE) &&
+ (node2->type == XML_ELEMENT_NODE) &&
+ (0 > (long) node1->content) &&
+ (0 > (long) node2->content) &&
+ (node1->doc == node2->doc)) {
+
+ l1 = -((long) node1->content);
+ l2 = -((long) node2->content);
+ if (l1 < l2)
+ return(1);
+ if (l1 > l2)
+ return(-1);
+ }
+
+ for (cur = node1->next;cur != NULL;cur = cur->next)
+ if (cur == node2)
+ return(1);
+ return(-1); /* assume there is no sibling list corruption */
+}
+#endif /* XP_OPTIMIZED_NON_ELEM_COMPARISON */
+
/**
* xmlXPathNodeSetSort:
* @set: the node set
@@ -1711,8 +3279,14 @@ xmlXPathNodeSetSort(xmlNodeSetPtr set) {
for (i = incr; i < len; i++) {
j = i - incr;
while (j >= 0) {
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
+ if (xmlXPathCmpNodesExt(set->nodeTab[j],
+ set->nodeTab[j + incr]) == -1)
+#else
if (xmlXPathCmpNodes(set->nodeTab[j],
- set->nodeTab[j + incr]) == -1) {
+ set->nodeTab[j + incr]) == -1)
+#endif
+ {
tmp = set->nodeTab[j];
set->nodeTab[j] = set->nodeTab[j + incr];
set->nodeTab[j + incr] = tmp;
@@ -2044,31 +3618,60 @@ xmlXPathNodeSetAddUnique(xmlNodeSetPtr cur, xmlNodePtr val) {
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);
+#if 0
+ /*
+ * TODO: The optimization won't work in every case, since
+ * those nasty namespace nodes need to be added with
+ * xmlXPathNodeSetDupNs() to the set; thus a pure
+ * memcpy is not possible.
+ * If there was a flag on the nodesetval, indicating that
+ * some temporary nodes are in, that would be helpfull.
+ */
+ /*
+ * 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);
+#endif
}
/* @@ 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;
}
@@ -2102,13 +3705,13 @@ xmlXPathNodeSetMerge(xmlNodeSetPtr val1, xmlNodeSetPtr val2) {
}
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);
@@ -2253,6 +3856,34 @@ xmlXPathFreeNodeSet(xmlNodeSetPtr obj) {
}
/**
+ * xmlXPathNodeSetClear:
+ * @set: the xmlNodeSetPtr to free
+ *
+ * Clears the list from all temporary XPath objects (e.g. namespace nodes
+ * are feed), but does *not* free the list itself. Sets the length of the
+ * list to 0.
+ */
+static void
+xmlXPathNodeSetClear(xmlNodeSetPtr set)
+{
+ int i;
+ xmlNodePtr node;
+
+ if ((set == NULL) || (set->nodeNr <= 0))
+ return;
+
+ for (i = 0; i < set->nodeNr; i++) {
+ node = set->nodeTab[i];
+ if ((node != NULL) &&
+ (node->type == XML_NAMESPACE_DECL))
+ {
+ xmlXPathNodeSetFreeNs((xmlNsPtr) node);
+ }
+ }
+ set->nodeNr = 0;
+}
+
+/**
* xmlXPathFreeValueTree:
* @obj: the xmlNodeSetPtr to free
*
@@ -2344,6 +3975,9 @@ xmlXPathNewNodeSet(xmlNodePtr val) {
ret->boolval = 0;
ret->nodesetval = xmlXPathNodeSetCreate(val);
/* @@ with_ns to check whether namespace nodes should be looked at @@ */
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
+#endif
return(ret);
}
@@ -2370,6 +4004,9 @@ xmlXPathNewValueTree(xmlNodePtr val) {
ret->boolval = 1;
ret->user = (void *) val;
ret->nodesetval = xmlXPathNodeSetCreate(val);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_XSLT_TREE);
+#endif
return(ret);
}
@@ -2421,6 +4058,9 @@ xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_NODESET;
ret->nodesetval = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_NODESET);
+#endif
return(ret);
}
@@ -2434,6 +4074,9 @@ xmlXPathWrapNodeSet(xmlNodeSetPtr val) {
void
xmlXPathFreeNodeSetList(xmlXPathObjectPtr obj) {
if (obj == NULL) return;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageReleased(NULL, obj->type);
+#endif
xmlFree(obj);
}
@@ -3070,7 +4713,7 @@ xmlXPathVariableLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
if (name == NULL)
return(NULL);
- return(xmlXPathObjectCopy((xmlXPathObjectPtr)
+ return(xmlXPathCacheObjectCopy(ctxt, (xmlXPathObjectPtr)
xmlHashLookup2(ctxt->varHash, name, ns_uri)));
}
@@ -3197,6 +4840,9 @@ xmlXPathNewFloat(double val) {
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_NUMBER;
ret->floatval = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_NUMBER);
+#endif
return(ret);
}
@@ -3220,6 +4866,9 @@ xmlXPathNewBoolean(int val) {
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_BOOLEAN;
ret->boolval = (val != 0);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_BOOLEAN);
+#endif
return(ret);
}
@@ -3246,6 +4895,9 @@ xmlXPathNewString(const xmlChar *val) {
ret->stringval = xmlStrdup(val);
else
ret->stringval = xmlStrdup((const xmlChar *)"");
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
+#endif
return(ret);
}
@@ -3269,6 +4921,9 @@ xmlXPathWrapString (xmlChar *val) {
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_STRING;
ret->stringval = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
+#endif
return(ret);
}
@@ -3292,6 +4947,9 @@ xmlXPathNewCString(const char *val) {
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_STRING;
ret->stringval = xmlStrdup(BAD_CAST val);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_STRING);
+#endif
return(ret);
}
@@ -3328,6 +4986,9 @@ xmlXPathWrapExternal (void *val) {
memset(ret, 0 , (size_t) sizeof(xmlXPathObject));
ret->type = XPATH_USERS;
ret->user = val;
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, XPATH_USERS);
+#endif
return(ret);
}
@@ -3352,6 +5013,9 @@ xmlXPathObjectCopy(xmlXPathObjectPtr val) {
return(NULL);
}
memcpy(ret, val , (size_t) sizeof(xmlXPathObject));
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageRequested(NULL, val->type);
+#endif
switch (val->type) {
case XPATH_BOOLEAN:
case XPATH_NUMBER:
@@ -3436,8 +5100,9 @@ xmlXPathFreeObject(xmlXPathObjectPtr obj) {
xmlFreeNodeList((xmlNodePtr) obj->user);
} else
#endif
+ obj->type = XPATH_XSLT_TREE; /* TODO: Just for debugging. */
if (obj->nodesetval != NULL)
- xmlXPathFreeValueTree(obj->nodesetval);
+ xmlXPathFreeValueTree(obj->nodesetval);
} else {
if (obj->nodesetval != NULL)
xmlXPathFreeNodeSet(obj->nodesetval);
@@ -3451,8 +5116,153 @@ xmlXPathFreeObject(xmlXPathObjectPtr obj) {
if (obj->stringval != NULL)
xmlFree(obj->stringval);
}
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageReleased(NULL, obj->type);
+#endif
+ xmlFree(obj);
+}
- xmlFree(obj);
+/**
+ * xmlXPathReleaseObject:
+ * @obj: the xmlXPathObjectPtr to free or to cache
+ *
+ * Depending on the state of the cache this frees the given
+ * XPath object or stores it in the cache.
+ */
+static void
+xmlXPathReleaseObject(xmlXPathContextPtr ctxt, xmlXPathObjectPtr obj)
+{
+#define XP_CACHE_ADD(sl, o) if (sl == NULL) { \
+ sl = xmlPointerListCreate(10); if (sl == NULL) goto free_obj; } \
+ if (xmlPointerListAddSize(sl, obj, 0) == -1) goto free_obj;
+
+#define XP_CACHE_WANTS(sl, n) ((sl == NULL) || ((sl)->number < n))
+
+ if (obj == NULL)
+ return;
+ if ((ctxt == NULL) || (ctxt->cache == NULL)) {
+ xmlXPathFreeObject(obj);
+ } else {
+ xmlXPathContextCachePtr cache =
+ (xmlXPathContextCachePtr) ctxt->cache;
+
+ switch (obj->type) {
+ case XPATH_NODESET:
+ case XPATH_XSLT_TREE:
+ if (obj->nodesetval != NULL) {
+ if (obj->boolval) {
+ /*
+ * It looks like the @boolval is used for
+ * evaluation if this an XSLT Result Tree Fragment.
+ * TODO: Check if this assumption is correct.
+ */
+ obj->type = XPATH_XSLT_TREE; /* just for debugging */
+ xmlXPathFreeValueTree(obj->nodesetval);
+ obj->nodesetval = NULL;
+ } else if ((obj->nodesetval->nodeMax <= 40) &&
+ (XP_CACHE_WANTS(cache->nodesetObjs,
+ cache->maxNodeset)))
+ {
+ XP_CACHE_ADD(cache->nodesetObjs, obj);
+ goto obj_cached;
+ } else {
+ xmlXPathFreeNodeSet(obj->nodesetval);
+ obj->nodesetval = NULL;
+ }
+ }
+ break;
+ case XPATH_STRING:
+ if (obj->stringval != NULL)
+ xmlFree(obj->stringval);
+
+ if (XP_CACHE_WANTS(cache->stringObjs, cache->maxString)) {
+ XP_CACHE_ADD(cache->stringObjs, obj);
+ goto obj_cached;
+ }
+ break;
+ case XPATH_BOOLEAN:
+ if (XP_CACHE_WANTS(cache->booleanObjs, cache->maxBoolean)) {
+ XP_CACHE_ADD(cache->booleanObjs, obj);
+ goto obj_cached;
+ }
+ break;
+ case XPATH_NUMBER:
+ if (XP_CACHE_WANTS(cache->numberObjs, cache->maxNumber)) {
+ XP_CACHE_ADD(cache->numberObjs, obj);
+ goto obj_cached;
+ }
+ break;
+#ifdef LIBXML_XPTR_ENABLED
+ case XPATH_LOCATIONSET:
+ if (obj->user != NULL) {
+ xmlXPtrFreeLocationSet(obj->user);
+ }
+ goto free_obj;
+#endif
+ default:
+ goto free_obj;
+ }
+
+ /*
+ * Fallback to adding to the misc-objects slot.
+ */
+ if (XP_CACHE_WANTS(cache->miscObjs, cache->maxMisc)) {
+ XP_CACHE_ADD(cache->miscObjs, obj);
+ } else
+ goto free_obj;
+
+obj_cached:
+
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageReleased(ctxt, obj->type);
+#endif
+
+ if (obj->nodesetval != NULL) {
+ xmlNodeSetPtr tmpset = obj->nodesetval;
+
+ /*
+ * TODO: Due to those nasty ns-nodes, we need to traverse
+ * the list and free the ns-nodes.
+ * URGENT TODO: Check if it's actually slowing things down.
+ * Maybe we shouldn't try to preserve the list.
+ */
+ if (tmpset->nodeNr > 1) {
+ int i;
+ xmlNodePtr node;
+
+ for (i = 0; i < tmpset->nodeNr; i++) {
+ node = tmpset->nodeTab[i];
+ if ((node != NULL) &&
+ (node->type == XML_NAMESPACE_DECL))
+ {
+ xmlXPathNodeSetFreeNs((xmlNsPtr) node);
+ }
+ }
+ } else if (tmpset->nodeNr == 1) {
+ if ((tmpset->nodeTab[0] != NULL) &&
+ (tmpset->nodeTab[0]->type == XML_NAMESPACE_DECL))
+ xmlXPathNodeSetFreeNs((xmlNsPtr) tmpset->nodeTab[0]);
+ }
+ tmpset->nodeNr = 0;
+ memset(obj, 0, sizeof(xmlXPathObject));
+ obj->nodesetval = tmpset;
+ } else
+ memset(obj, 0, sizeof(xmlXPathObject));
+
+ return;
+
+free_obj:
+ /*
+ * Cache is full; free the object.
+ */
+ if (obj->nodesetval != NULL)
+ xmlXPathFreeNodeSet(obj->nodesetval);
+#ifdef XP_DEBUG_OBJ_USAGE
+ xmlXPathDebugObjUsageReleased(NULL, obj->type);
+#endif
+ xmlFree(obj);
+ }
+ return;
}
@@ -3540,7 +5350,8 @@ xmlXPathCastNodeSetToString (xmlNodeSetPtr ns) {
if ((ns == NULL) || (ns->nodeNr == 0) || (ns->nodeTab == NULL))
return(xmlStrdup((const xmlChar *) ""));
- xmlXPathNodeSetSort(ns);
+ if (ns->nodeNr > 1)
+ xmlXPathNodeSetSort(ns);
return(xmlXPathCastNodeToString(ns->nodeTab[0]));
}
@@ -3936,8 +5747,15 @@ xmlXPathNewContext(xmlDocPtr doc) {
ret->contextSize = -1;
ret->proximityPosition = -1;
- xmlXPathRegisterAllFunctions(ret);
-
+#ifdef XP_DEFAULT_CACHE_ON
+ if (xmlXPathContextSetCache(ret, 1, -1, 0) == -1) {
+ xmlXPathFreeContext(ret);
+ return(NULL);
+ }
+#endif
+
+ xmlXPathRegisterAllFunctions(ret);
+
return(ret);
}
@@ -3951,6 +5769,8 @@ void
xmlXPathFreeContext(xmlXPathContextPtr ctxt) {
if (ctxt == NULL) return;
+ if (ctxt->cache != NULL)
+ xmlXPathFreeCache((xmlXPathContextCachePtr) ctxt->cache);
xmlXPathRegisteredNsCleanup(ctxt);
xmlXPathRegisteredFuncsCleanup(ctxt);
xmlXPathRegisteredVariablesCleanup(ctxt);
@@ -4256,8 +6076,8 @@ xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
if ((f == NULL) || (arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
- xmlXPathFreeObject(arg);
- xmlXPathFreeObject(f);
+ xmlXPathReleaseObject(ctxt->context, arg);
+ xmlXPathReleaseObject(ctxt->context, f);
return(0);
}
ns = arg->nodesetval;
@@ -4266,18 +6086,18 @@ xmlXPathCompareNodeSetFloat(xmlXPathParserContextPtr ctxt, int inf, int strict,
str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
if (str2 != NULL) {
valuePush(ctxt,
- xmlXPathNewString(str2));
+ xmlXPathCacheNewString(ctxt->context, str2));
xmlFree(str2);
xmlXPathNumberFunction(ctxt, 1);
- valuePush(ctxt, xmlXPathObjectCopy(f));
+ valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, f));
ret = xmlXPathCompareValues(ctxt, inf, strict);
if (ret)
break;
}
}
}
- xmlXPathFreeObject(arg);
- xmlXPathFreeObject(f);
+ xmlXPathReleaseObject(ctxt->context, arg);
+ xmlXPathReleaseObject(ctxt->context, f);
return(ret);
}
@@ -4311,8 +6131,8 @@ xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
if ((s == NULL) || (arg == NULL) ||
((arg->type != XPATH_NODESET) && (arg->type != XPATH_XSLT_TREE))) {
- xmlXPathFreeObject(arg);
- xmlXPathFreeObject(s);
+ xmlXPathReleaseObject(ctxt->context, arg);
+ xmlXPathReleaseObject(ctxt->context, s);
return(0);
}
ns = arg->nodesetval;
@@ -4321,17 +6141,17 @@ xmlXPathCompareNodeSetString(xmlXPathParserContextPtr ctxt, int inf, int strict,
str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
if (str2 != NULL) {
valuePush(ctxt,
- xmlXPathNewString(str2));
+ xmlXPathCacheNewString(ctxt->context, str2));
xmlFree(str2);
- valuePush(ctxt, xmlXPathObjectCopy(s));
+ valuePush(ctxt, xmlXPathCacheObjectCopy(ctxt->context, s));
ret = xmlXPathCompareValues(ctxt, inf, strict);
if (ret)
break;
}
}
}
- xmlXPathFreeObject(arg);
- xmlXPathFreeObject(s);
+ xmlXPathReleaseObject(ctxt->context, arg);
+ xmlXPathReleaseObject(ctxt->context, s);
return(ret);
}
@@ -4575,12 +6395,12 @@ xmlXPathEqualNodeSetFloat(xmlXPathParserContextPtr ctxt,
for (i=0;i<ns->nodeNr;i++) {
str2 = xmlXPathCastNodeToString(ns->nodeTab[i]);
if (str2 != NULL) {
- valuePush(ctxt, xmlXPathNewString(str2));
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, str2));
xmlFree(str2);
xmlXPathNumberFunction(ctxt, 1);
val = valuePop(ctxt);
v = val->floatval;
- xmlXPathFreeObject(val);
+ xmlXPathReleaseObject(ctxt->context, val);
if (!xmlXPathIsNaN(v)) {
if ((!neq) && (v==f)) {
ret = 1;
@@ -4899,8 +6719,8 @@ xmlXPathEqualValuesCommon(xmlXPathParserContextPtr ctxt,
case XPATH_XSLT_TREE:
break;
}
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg1);
+ xmlXPathReleaseObject(ctxt->context, arg2);
return(ret);
}
@@ -4922,9 +6742,9 @@ xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
arg1 = valuePop(ctxt);
if ((arg1 == NULL) || (arg2 == NULL)) {
if (arg1 != NULL)
- xmlXPathFreeObject(arg1);
+ xmlXPathReleaseObject(ctxt->context, arg1);
else
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
@@ -4981,8 +6801,8 @@ xmlXPathEqualValues(xmlXPathParserContextPtr ctxt) {
TODO
break;
}
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg1);
+ xmlXPathReleaseObject(ctxt->context, arg2);
return(ret);
}
@@ -5007,9 +6827,9 @@ xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
arg1 = valuePop(ctxt);
if ((arg1 == NULL) || (arg2 == NULL)) {
if (arg1 != NULL)
- xmlXPathFreeObject(arg1);
+ xmlXPathReleaseObject(ctxt->context, arg1);
else
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
@@ -5018,7 +6838,7 @@ xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
xmlGenericError(xmlGenericErrorContext,
"NotEqual: by pointer\n");
#endif
- xmlXPathFreeObject(arg1);
+ xmlXPathReleaseObject(ctxt->context, arg1);
return(0);
}
@@ -5066,8 +6886,8 @@ xmlXPathNotEqualValues(xmlXPathParserContextPtr ctxt) {
TODO
break;
}
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg1);
+ xmlXPathReleaseObject(ctxt->context, arg2);
return(ret);
}
@@ -5108,9 +6928,9 @@ xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
arg1 = valuePop(ctxt);
if ((arg1 == NULL) || (arg2 == NULL)) {
if (arg1 != NULL)
- xmlXPathFreeObject(arg1);
+ xmlXPathReleaseObject(ctxt->context, arg1);
else
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
@@ -5152,8 +6972,8 @@ xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
arg2 = valuePop(ctxt);
}
if (arg2->type != XPATH_NUMBER) {
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg1);
+ xmlXPathReleaseObject(ctxt->context, arg2);
XP_ERROR0(XPATH_INVALID_OPERAND);
}
/*
@@ -5205,8 +7025,8 @@ xmlXPathCompareValues(xmlXPathParserContextPtr ctxt, int inf, int strict) {
}
}
}
- xmlXPathFreeObject(arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg1);
+ xmlXPathReleaseObject(ctxt->context, arg2);
return(ret);
}
@@ -5256,8 +7076,7 @@ xmlXPathAddValues(xmlXPathParserContextPtr ctxt) {
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
val = xmlXPathCastToNumber(arg);
- xmlXPathFreeObject(arg);
-
+ xmlXPathReleaseObject(ctxt->context, arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
ctxt->value->floatval += val;
@@ -5280,8 +7099,7 @@ xmlXPathSubValues(xmlXPathParserContextPtr ctxt) {
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
val = xmlXPathCastToNumber(arg);
- xmlXPathFreeObject(arg);
-
+ xmlXPathReleaseObject(ctxt->context, arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
ctxt->value->floatval -= val;
@@ -5304,8 +7122,7 @@ xmlXPathMultValues(xmlXPathParserContextPtr ctxt) {
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
val = xmlXPathCastToNumber(arg);
- xmlXPathFreeObject(arg);
-
+ xmlXPathReleaseObject(ctxt->context, arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
ctxt->value->floatval *= val;
@@ -5328,8 +7145,7 @@ xmlXPathDivValues(xmlXPathParserContextPtr ctxt) {
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
val = xmlXPathCastToNumber(arg);
- xmlXPathFreeObject(arg);
-
+ xmlXPathReleaseObject(ctxt->context, arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
if (xmlXPathIsNaN(val) || xmlXPathIsNaN(ctxt->value->floatval))
@@ -5370,8 +7186,7 @@ xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
if (arg == NULL)
XP_ERROR(XPATH_INVALID_OPERAND);
arg2 = xmlXPathCastToNumber(arg);
- xmlXPathFreeObject(arg);
-
+ xmlXPathReleaseObject(ctxt->context, arg);
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
arg1 = ctxt->value->floatval;
@@ -5396,6 +7211,17 @@ xmlXPathModValues(xmlXPathParserContextPtr ctxt) {
typedef xmlNodePtr (*xmlXPathTraversalFunction)
(xmlXPathParserContextPtr ctxt, xmlNodePtr cur);
+/*
+ * xmlXPathTraversalFunctionExt:
+ * A traversal function enumerates nodes along an axis.
+ * Initially it must be called with NULL, and it indicates
+ * termination on the axis by returning NULL.
+ * The context node of the traversal is specified via @contextNode.
+ */
+typedef xmlNodePtr (*xmlXPathTraversalFunctionExt)
+ (xmlNodePtr cur, xmlNodePtr contextNode);
+
+
/**
* xmlXPathNextSelf:
* @ctxt: the XPath Parser context
@@ -5466,6 +7292,157 @@ xmlXPathNextChild(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
}
/**
+ * xmlXPathNextChildElement:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "child" direction and nodes of type element.
+ * The child axis contains the children of the context node in document order.
+ *
+ * Returns the next element following that axis
+ */
+static xmlNodePtr
+xmlXPathNextChildElement(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
+ if ((ctxt == NULL) || (ctxt->context == NULL)) return(NULL);
+ if (cur == NULL) {
+ cur = ctxt->context->node;
+ if (cur == NULL) return(NULL);
+ /*
+ * Get the first element child.
+ */
+ switch (cur->type) {
+ case XML_ELEMENT_NODE:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_ENTITY_REF_NODE: /* URGENT TODO: entify-refs as well? */
+ case XML_ENTITY_NODE:
+ cur = cur->children;
+ if (cur != NULL) {
+ if (cur->type == XML_ELEMENT_NODE)
+ return(cur);
+ do {
+ cur = cur->next;
+ } while ((cur != NULL) &&
+ (cur->type != XML_ELEMENT_NODE));
+ return(cur);
+ }
+ return(NULL);
+ case XML_DOCUMENT_NODE:
+ case XML_HTML_DOCUMENT_NODE:
+#ifdef LIBXML_DOCB_ENABLED
+ case XML_DOCB_DOCUMENT_NODE:
+#endif
+ return(xmlDocGetRootElement((xmlDocPtr) cur));
+ default:
+ return(NULL);
+ }
+ return(NULL);
+ }
+ /*
+ * Get the next sibling element node.
+ */
+ switch (cur->type) {
+ case XML_ELEMENT_NODE:
+ case XML_TEXT_NODE:
+ case XML_ENTITY_REF_NODE:
+ case XML_ENTITY_NODE:
+ case XML_CDATA_SECTION_NODE:
+ case XML_PI_NODE:
+ case XML_COMMENT_NODE:
+ case XML_XINCLUDE_END:
+ break;
+ /* case XML_DTD_NODE: */ /* URGENT TODO: DTD-node as well? */
+ default:
+ return(NULL);
+ }
+ if (cur->next != NULL) {
+ if (cur->next->type == XML_ELEMENT_NODE)
+ return(cur->next);
+ cur = cur->next;
+ do {
+ cur = cur->next;
+ } while ((cur != NULL) && (cur->type != XML_ELEMENT_NODE));
+ return(cur);
+ }
+ return(NULL);
+}
+
+/**
+ * xmlXPathNextDescendantOrSelfElemParent:
+ * @ctxt: the XPath Parser context
+ * @cur: the current node in the traversal
+ *
+ * Traversal function for the "descendant-or-self" axis.
+ * Additionally it returns only nodes which can be parents of
+ * element nodes.
+ *
+ *
+ * Returns the next element following that axis
+ */
+static xmlNodePtr
+xmlXPathNextDescendantOrSelfElemParent(xmlNodePtr cur,
+ xmlNodePtr contextNode)
+{
+ if (cur == NULL) {
+ if (contextNode == NULL)
+ return(NULL);
+ switch (contextNode->type) {
+ case XML_ELEMENT_NODE:
+ case XML_XINCLUDE_START:
+ case XML_DOCUMENT_FRAG_NODE:
+ case XML_DOCUMENT_NODE:
+#ifdef LIBXML_DOCB_ENABLED
+ case XML_DOCB_DOCUMENT_NODE:
+#endif
+ case XML_HTML_DOCUMENT_NODE:
+ return(contextNode);
+ default:
+ return(NULL);
+ }
+ return(NULL);
+ } else {
+ xmlNodePtr start = cur;
+
+ while (cur != NULL) {
+ switch (cur->type) {
+ case XML_ELEMENT_NODE:
+ /* TODO: OK to have XInclude here? */
+ case XML_XINCLUDE_START:
+ case XML_DOCUMENT_FRAG_NODE:
+ if (cur != start)
+ return(cur);
+ if (cur->children != NULL) {
+ cur = cur->children;
+ continue;
+ }
+ break;
+#ifdef LIBXML_DOCB_ENABLED
+ /* Not sure if we need those here. */
+ case XML_DOCUMENT_NODE:
+ case XML_DOCB_DOCUMENT_NODE:
+#endif
+ case XML_HTML_DOCUMENT_NODE:
+ if (cur != start)
+ return(cur);
+ return(xmlDocGetRootElement((xmlDocPtr) cur));
+ default:
+ break;
+ }
+
+next_sibling:
+ if ((cur == NULL) || (cur == contextNode))
+ return(NULL);
+ if (cur->next != NULL) {
+ cur = cur->next;
+ } else {
+ cur = cur->parent;
+ goto next_sibling;
+ }
+ }
+ }
+ return(NULL);
+}
+
+/**
* xmlXPathNextDescendant:
* @ctxt: the XPath Parser context
* @cur: the current node in the traversal
@@ -6051,9 +8028,11 @@ xmlXPathNextAttribute(xmlXPathParserContextPtr ctxt, xmlNodePtr cur) {
*/
void
xmlXPathRoot(xmlXPathParserContextPtr ctxt) {
- if ((ctxt == NULL) || (ctxt->context == NULL)) return;
+ if ((ctxt == NULL) || (ctxt->context == NULL))
+ return;
ctxt->context->node = (xmlNodePtr) ctxt->context->doc;
- valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node));
}
/************************************************************************
@@ -6077,7 +8056,9 @@ void
xmlXPathLastFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(0);
if (ctxt->context->contextSize >= 0) {
- valuePush(ctxt, xmlXPathNewFloat((double) ctxt->context->contextSize));
+ valuePush(ctxt,
+ xmlXPathCacheNewFloat(ctxt->context,
+ (double) ctxt->context->contextSize));
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext,
"last() : %d\n", ctxt->context->contextSize);
@@ -6103,7 +8084,8 @@ xmlXPathPositionFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(0);
if (ctxt->context->proximityPosition >= 0) {
valuePush(ctxt,
- xmlXPathNewFloat((double) ctxt->context->proximityPosition));
+ xmlXPathCacheNewFloat(ctxt->context,
+ (double) ctxt->context->proximityPosition));
#ifdef DEBUG_EXPR
xmlGenericError(xmlGenericErrorContext, "position() : %d\n",
ctxt->context->proximityPosition);
@@ -6133,13 +8115,14 @@ xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
cur = valuePop(ctxt);
if ((cur == NULL) || (cur->nodesetval == NULL))
- valuePush(ctxt, xmlXPathNewFloat((double) 0));
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
else if ((cur->type == XPATH_NODESET) || (cur->type == XPATH_XSLT_TREE)) {
- valuePush(ctxt, xmlXPathNewFloat((double) cur->nodesetval->nodeNr));
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
+ (double) cur->nodesetval->nodeNr));
} else {
if ((cur->nodesetval->nodeNr != 1) ||
(cur->nodesetval->nodeTab == NULL)) {
- valuePush(ctxt, xmlXPathNewFloat((double) 0));
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) 0));
} else {
xmlNodePtr tmp;
int i = 0;
@@ -6152,10 +8135,10 @@ xmlXPathCountFunction(xmlXPathParserContextPtr ctxt, int nargs) {
i++;
}
}
- valuePush(ctxt, xmlXPathNewFloat((double) i));
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, (double) i));
}
}
- xmlXPathFreeObject(cur);
+ xmlXPathReleaseObject(ctxt->context, cur);
}
/**
@@ -6257,17 +8240,14 @@ xmlXPathIdFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlFree(tokens);
}
}
-
- xmlXPathFreeObject(obj);
- valuePush(ctxt, xmlXPathWrapNodeSet(ret));
+ xmlXPathReleaseObject(ctxt->context, obj);
+ valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
return;
}
- obj = xmlXPathConvertString(obj);
-
+ obj = xmlXPathCacheConvertString(ctxt->context, obj);
ret = xmlXPathGetElementsByIds(ctxt->context->doc, obj->stringval);
- valuePush(ctxt, xmlXPathWrapNodeSet(ret));
-
- xmlXPathFreeObject(obj);
+ valuePush(ctxt, xmlXPathCacheWrapNodeSet(ctxt->context, ret));
+ xmlXPathReleaseObject(ctxt->context, obj);
return;
}
@@ -6291,7 +8271,8 @@ xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
if (ctxt == NULL) return;
if (nargs == 0) {
- valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node));
nargs = 1;
}
@@ -6303,7 +8284,7 @@ xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
cur = valuePop(ctxt);
if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
} else {
int i = 0; /* Should be first in document order !!!!! */
switch (cur->nodesetval->nodeTab[i]->type) {
@@ -6311,20 +8292,21 @@ xmlXPathLocalNameFunction(xmlXPathParserContextPtr ctxt, int nargs) {
case XML_ATTRIBUTE_NODE:
case XML_PI_NODE:
if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
else
valuePush(ctxt,
- xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
+ xmlXPathCacheNewString(ctxt->context,
+ cur->nodesetval->nodeTab[i]->name));
break;
case XML_NAMESPACE_DECL:
- valuePush(ctxt, xmlXPathNewString(
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
((xmlNsPtr)cur->nodesetval->nodeTab[i])->prefix));
break;
default:
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
}
}
- xmlXPathFreeObject(cur);
+ xmlXPathReleaseObject(ctxt->context, cur);
}
/**
@@ -6348,7 +8330,8 @@ xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
if (ctxt == NULL) return;
if (nargs == 0) {
- valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node));
nargs = 1;
}
CHECK_ARITY(1);
@@ -6359,23 +8342,23 @@ xmlXPathNamespaceURIFunction(xmlXPathParserContextPtr ctxt, int nargs) {
cur = valuePop(ctxt);
if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
} else {
int i = 0; /* Should be first in document order !!!!! */
switch (cur->nodesetval->nodeTab[i]->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
if (cur->nodesetval->nodeTab[i]->ns == NULL)
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
else
- valuePush(ctxt, xmlXPathNewString(
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
cur->nodesetval->nodeTab[i]->ns->href));
break;
default:
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
}
}
- xmlXPathFreeObject(cur);
+ xmlXPathReleaseObject(ctxt->context, cur);
}
/**
@@ -6406,7 +8389,8 @@ xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
xmlXPathObjectPtr cur;
if (nargs == 0) {
- valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node));
nargs = 1;
}
@@ -6418,7 +8402,7 @@ xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
cur = valuePop(ctxt);
if ((cur->nodesetval == NULL) || (cur->nodesetval->nodeNr == 0)) {
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
} else {
int i = 0; /* Should be first in document order !!!!! */
@@ -6426,12 +8410,13 @@ xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
if (cur->nodesetval->nodeTab[i]->name[0] == ' ')
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt,
+ xmlXPathCacheNewCString(ctxt->context, ""));
else if ((cur->nodesetval->nodeTab[i]->ns == NULL) ||
(cur->nodesetval->nodeTab[i]->ns->prefix == NULL)) {
- valuePush(ctxt,
- xmlXPathNewString(cur->nodesetval->nodeTab[i]->name));
-
+ valuePush(ctxt,
+ xmlXPathCacheNewString(ctxt->context,
+ cur->nodesetval->nodeTab[i]->name));
} else {
xmlChar *fullname;
@@ -6443,16 +8428,17 @@ xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs)
if (fullname == NULL) {
XP_ERROR(XPATH_MEMORY_ERROR);
}
- valuePush(ctxt, xmlXPathWrapString(fullname));
+ valuePush(ctxt, xmlXPathCacheWrapString(
+ ctxt->context, fullname));
}
break;
default:
- valuePush(ctxt,
- xmlXPathNewNodeSet(cur->nodesetval->nodeTab[i]));
+ valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
+ cur->nodesetval->nodeTab[i]));
xmlXPathLocalNameFunction(ctxt, 1);
}
}
- xmlXPathFreeObject(cur);
+ xmlXPathReleaseObject(ctxt->context, cur);
}
@@ -6498,17 +8484,16 @@ xmlXPathStringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
if (ctxt == NULL) return;
if (nargs == 0) {
- valuePush(ctxt,
- xmlXPathWrapString(
- xmlXPathCastNodeToString(ctxt->context->node)));
+ valuePush(ctxt,
+ xmlXPathCacheWrapString(ctxt->context,
+ xmlXPathCastNodeToString(ctxt->context->node)));
return;
}
CHECK_ARITY(1);
cur = valuePop(ctxt);
if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
- cur = xmlXPathConvertString(cur);
- valuePush(ctxt, cur);
+ valuePush(ctxt, xmlXPathCacheConvertString(ctxt->context, cur));
}
/**
@@ -6531,12 +8516,13 @@ xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
if ((ctxt == NULL) || (ctxt->context == NULL))
return;
if (ctxt->context->node == NULL) {
- valuePush(ctxt, xmlXPathNewFloat(0));
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0));
} else {
xmlChar *content;
content = xmlXPathCastNodeToString(ctxt->context->node);
- valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(content)));
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
+ xmlUTF8Strlen(content)));
xmlFree(content);
}
return;
@@ -6545,8 +8531,9 @@ xmlXPathStringLengthFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CAST_TO_STRING;
CHECK_TYPE(XPATH_STRING);
cur = valuePop(ctxt);
- valuePush(ctxt, xmlXPathNewFloat(xmlUTF8Strlen(cur->stringval)));
- xmlXPathFreeObject(cur);
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context,
+ xmlUTF8Strlen(cur->stringval)));
+ xmlXPathReleaseObject(ctxt->context, cur);
}
/**
@@ -6571,7 +8558,7 @@ xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CAST_TO_STRING;
cur = valuePop(ctxt);
if ((cur == NULL) || (cur->type != XPATH_STRING)) {
- xmlXPathFreeObject(cur);
+ xmlXPathReleaseObject(ctxt->context, cur);
return;
}
nargs--;
@@ -6580,15 +8567,14 @@ xmlXPathConcatFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CAST_TO_STRING;
newobj = valuePop(ctxt);
if ((newobj == NULL) || (newobj->type != XPATH_STRING)) {
- xmlXPathFreeObject(newobj);
- xmlXPathFreeObject(cur);
+ xmlXPathReleaseObject(ctxt->context, newobj);
+ xmlXPathReleaseObject(ctxt->context, cur);
XP_ERROR(XPATH_INVALID_TYPE);
}
tmp = xmlStrcat(newobj->stringval, cur->stringval);
newobj->stringval = cur->stringval;
cur->stringval = tmp;
-
- xmlXPathFreeObject(newobj);
+ xmlXPathReleaseObject(ctxt->context, newobj);
nargs--;
}
valuePush(ctxt, cur);
@@ -6614,17 +8600,18 @@ xmlXPathContainsFunction(xmlXPathParserContextPtr ctxt, int nargs) {
needle = valuePop(ctxt);
CAST_TO_STRING;
hay = valuePop(ctxt);
+
if ((hay == NULL) || (hay->type != XPATH_STRING)) {
- xmlXPathFreeObject(hay);
- xmlXPathFreeObject(needle);
+ xmlXPathReleaseObject(ctxt->context, hay);
+ xmlXPathReleaseObject(ctxt->context, needle);
XP_ERROR(XPATH_INVALID_TYPE);
}
if (xmlStrstr(hay->stringval, needle->stringval))
- valuePush(ctxt, xmlXPathNewBoolean(1));
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
else
- valuePush(ctxt, xmlXPathNewBoolean(0));
- xmlXPathFreeObject(hay);
- xmlXPathFreeObject(needle);
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
+ xmlXPathReleaseObject(ctxt->context, hay);
+ xmlXPathReleaseObject(ctxt->context, needle);
}
/**
@@ -6648,18 +8635,19 @@ xmlXPathStartsWithFunction(xmlXPathParserContextPtr ctxt, int nargs) {
needle = valuePop(ctxt);
CAST_TO_STRING;
hay = valuePop(ctxt);
+
if ((hay == NULL) || (hay->type != XPATH_STRING)) {
- xmlXPathFreeObject(hay);
- xmlXPathFreeObject(needle);
+ xmlXPathReleaseObject(ctxt->context, hay);
+ xmlXPathReleaseObject(ctxt->context, needle);
XP_ERROR(XPATH_INVALID_TYPE);
}
n = xmlStrlen(needle->stringval);
if (xmlStrncmp(hay->stringval, needle->stringval, n))
- valuePush(ctxt, xmlXPathNewBoolean(0));
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
else
- valuePush(ctxt, xmlXPathNewBoolean(1));
- xmlXPathFreeObject(hay);
- xmlXPathFreeObject(needle);
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
+ xmlXPathReleaseObject(ctxt->context, hay);
+ xmlXPathReleaseObject(ctxt->context, needle);
}
/**
@@ -6711,14 +8699,14 @@ xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_TYPE(XPATH_NUMBER);
len = valuePop(ctxt);
le = len->floatval;
- xmlXPathFreeObject(len);
+ xmlXPathReleaseObject(ctxt->context, len);
}
CAST_TO_NUMBER;
CHECK_TYPE(XPATH_NUMBER);
start = valuePop(ctxt);
in = start->floatval;
- xmlXPathFreeObject(start);
+ xmlXPathReleaseObject(ctxt->context, start);
CAST_TO_STRING;
CHECK_TYPE(XPATH_STRING);
str = valuePop(ctxt);
@@ -6777,15 +8765,13 @@ xmlXPathSubstringFunction(xmlXPathParserContextPtr ctxt, int nargs) {
else {
ret = NULL;
}
-
if (ret == NULL)
- valuePush(ctxt, xmlXPathNewCString(""));
+ valuePush(ctxt, xmlXPathCacheNewCString(ctxt->context, ""));
else {
- valuePush(ctxt, xmlXPathNewString(ret));
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context, ret));
xmlFree(ret);
}
-
- xmlXPathFreeObject(str);
+ xmlXPathReleaseObject(ctxt->context, str);
}
/**
@@ -6822,12 +8808,12 @@ xmlXPathSubstringBeforeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
offset = (int)(point - str->stringval);
xmlBufferAdd(target, str->stringval, offset);
}
- valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
+ xmlBufferContent(target)));
xmlBufferFree(target);
}
-
- xmlXPathFreeObject(str);
- xmlXPathFreeObject(find);
+ xmlXPathReleaseObject(ctxt->context, str);
+ xmlXPathReleaseObject(ctxt->context, find);
}
/**
@@ -6866,12 +8852,12 @@ xmlXPathSubstringAfterFunction(xmlXPathParserContextPtr ctxt, int nargs) {
xmlBufferAdd(target, &str->stringval[offset],
xmlStrlen(str->stringval) - offset);
}
- valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
+ xmlBufferContent(target)));
xmlBufferFree(target);
}
-
- xmlXPathFreeObject(str);
- xmlXPathFreeObject(find);
+ xmlXPathReleaseObject(ctxt->context, str);
+ xmlXPathReleaseObject(ctxt->context, find);
}
/**
@@ -6898,9 +8884,9 @@ xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
if (ctxt == NULL) return;
if (nargs == 0) {
/* Use current context node */
- valuePush(ctxt,
- xmlXPathWrapString(
- xmlXPathCastNodeToString(ctxt->context->node)));
+ valuePush(ctxt,
+ xmlXPathCacheWrapString(ctxt->context,
+ xmlXPathCastNodeToString(ctxt->context->node)));
nargs = 1;
}
@@ -6931,11 +8917,11 @@ xmlXPathNormalizeFunction(xmlXPathParserContextPtr ctxt, int nargs) {
}
source++;
}
-
- valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
+ xmlBufferContent(target)));
xmlBufferFree(target);
}
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(ctxt->context, obj);
}
/**
@@ -7014,11 +9000,12 @@ xmlXPathTranslateFunction(xmlXPathParserContextPtr ctxt, int nargs) {
}
}
}
- valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
+ xmlBufferContent(target)));
xmlBufferFree(target);
- xmlXPathFreeObject(str);
- xmlXPathFreeObject(from);
- xmlXPathFreeObject(to);
+ xmlXPathReleaseObject(ctxt->context, str);
+ xmlXPathReleaseObject(ctxt->context, from);
+ xmlXPathReleaseObject(ctxt->context, to);
}
/**
@@ -7041,7 +9028,7 @@ xmlXPathBooleanFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(1);
cur = valuePop(ctxt);
if (cur == NULL) XP_ERROR(XPATH_INVALID_OPERAND);
- cur = xmlXPathConvertBoolean(cur);
+ cur = xmlXPathCacheConvertBoolean(ctxt->context, cur);
valuePush(ctxt, cur);
}
@@ -7074,7 +9061,7 @@ xmlXPathNotFunction(xmlXPathParserContextPtr ctxt, int nargs) {
void
xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(0);
- valuePush(ctxt, xmlXPathNewBoolean(1));
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 1));
}
/**
@@ -7088,7 +9075,7 @@ xmlXPathTrueFunction(xmlXPathParserContextPtr ctxt, int nargs) {
void
xmlXPathFalseFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(0);
- valuePush(ctxt, xmlXPathNewBoolean(0));
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, 0));
}
/**
@@ -7136,8 +9123,9 @@ xmlXPathLangFunction(xmlXPathParserContextPtr ctxt, int nargs) {
not_equal:
if (theLang != NULL)
xmlFree((void *)theLang);
- xmlXPathFreeObject(val);
- valuePush(ctxt, xmlXPathNewBoolean(ret));
+
+ xmlXPathReleaseObject(ctxt->context, val);
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
}
/**
@@ -7156,12 +9144,12 @@ xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
if (ctxt == NULL) return;
if (nargs == 0) {
if (ctxt->context->node == NULL) {
- valuePush(ctxt, xmlXPathNewFloat(0.0));
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, 0.0));
} else {
xmlChar* content = xmlNodeGetContent(ctxt->context->node);
res = xmlXPathStringEvalNumber(content);
- valuePush(ctxt, xmlXPathNewFloat(res));
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
xmlFree(content);
}
return;
@@ -7169,8 +9157,7 @@ xmlXPathNumberFunction(xmlXPathParserContextPtr ctxt, int nargs) {
CHECK_ARITY(1);
cur = valuePop(ctxt);
- cur = xmlXPathConvertNumber(cur);
- valuePush(ctxt, cur);
+ valuePush(ctxt, xmlXPathCacheConvertNumber(ctxt->context, cur));
}
/**
@@ -7201,8 +9188,8 @@ xmlXPathSumFunction(xmlXPathParserContextPtr ctxt, int nargs) {
res += xmlXPathCastNodeToNumber(cur->nodesetval->nodeTab[i]);
}
}
- valuePush(ctxt, xmlXPathNewFloat(res));
- xmlXPathFreeObject(cur);
+ valuePush(ctxt, xmlXPathCacheNewFloat(ctxt->context, res));
+ xmlXPathReleaseObject(ctxt->context, cur);
}
/*
@@ -7331,7 +9318,7 @@ xmlXPathRoundFunction(xmlXPathParserContextPtr ctxt, int nargs) {
* 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);
@@ -7810,7 +9797,7 @@ xmlXPathCompNumber(xmlXPathParserContextPtr ctxt)
ret *= pow(10.0, (double) exponent);
}
PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_NUMBER, 0, 0,
- xmlXPathNewFloat(ret), NULL);
+ xmlXPathCacheNewFloat(ctxt->context, ret), NULL);
}
/**
@@ -7900,7 +9887,7 @@ xmlXPathCompLiteral(xmlXPathParserContextPtr ctxt) {
}
if (ret == NULL) return;
PUSH_LONG_EXPR(XPATH_OP_VALUE, XPATH_STRING, 0, 0,
- xmlXPathNewString(ret), NULL);
+ xmlXPathCacheNewString(ctxt->context, ret), NULL);
xmlFree(ret);
}
@@ -7988,6 +9975,7 @@ xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
xmlChar *name;
xmlChar *prefix;
int nbargs = 0;
+ int sort = 1;
name = xmlXPathParseQName(ctxt, &prefix);
if (name == NULL) {
@@ -8009,12 +9997,20 @@ xmlXPathCompFunctionCall(xmlXPathParserContextPtr ctxt) {
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++;
@@ -8051,7 +10047,7 @@ xmlXPathCompPrimaryExpr(xmlXPathParserContextPtr ctxt) {
else if (CUR == '(') {
NEXT;
SKIP_BLANKS;
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, 1);
CHECK_ERROR;
if (CUR != ')') {
XP_ERROR(XPATH_EXPR_ERROR);
@@ -8559,7 +10555,7 @@ xmlXPathCompAndExpr(xmlXPathParserContextPtr ctxt) {
* Parse and compile an expression
*/
static void
-xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
+xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt, int sort) {
xmlXPathCompAndExpr(ctxt);
CHECK_ERROR;
SKIP_BLANKS;
@@ -8573,8 +10569,13 @@ xmlXPathCompileExpr(xmlXPathParserContextPtr ctxt) {
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);
}
}
@@ -8601,7 +10602,7 @@ xmlXPathCompPredicate(xmlXPathParserContextPtr ctxt, int filter) {
SKIP_BLANKS;
ctxt->comp->last = -1;
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, 1);
CHECK_ERROR;
if (CUR != ']') {
@@ -8893,7 +10894,7 @@ xmlXPathCompStep(xmlXPathParserContextPtr ctxt) {
NEXT;
SKIP_BLANKS;
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, 1);
/* PUSH_BINARY_EXPR(XPATH_OP_RANGETO, op2, ctxt->comp->last, 0, 0); */
CHECK_ERROR;
@@ -9119,32 +11120,41 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
const xmlChar *URI = NULL;
#ifdef DEBUG_STEP
- int n = 0;
+ int nbMatches = 0;
#endif
- int i, t = 0;
- xmlNodeSetPtr ret, list;
+ int inputIdx, total = 0, specialNodeInSet = 0;
+ xmlNodeSetPtr inputList, resultList, list;
xmlXPathTraversalFunction next = NULL;
+ xmlXPathTraversalFunctionExt compoundNext = NULL;
void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
xmlNodeSetPtr (*mergeNodeSet) (xmlNodeSetPtr, xmlNodeSetPtr);
- xmlNodePtr cur = NULL;
+ xmlNodePtr oldContextNode, contextNode, cur, compoundContextNode;
xmlXPathObjectPtr obj;
- xmlNodeSetPtr nodelist;
- xmlNodePtr tmp;
+ xmlXPathContextPtr xpctxt = ctxt->context;
CHECK_TYPE0(XPATH_NODESET);
obj = valuePop(ctxt);
- addNode = xmlXPathNodeSetAdd;
- mergeNodeSet = xmlXPathNodeSetMerge;
+
+ /*
+ * Setup wrt namespaces.
+ */
if (prefix != NULL) {
- URI = xmlXPathNsLookup(ctxt->context, prefix);
+ URI = xmlXPathNsLookup(xpctxt, prefix);
if (URI == NULL) {
xmlXPathFreeObject(obj);
XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
}
}
+
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, "new step : ");
#endif
+
+ /*
+ * Setup wrt the axis.
+ */
+ addNode = xmlXPathNodeSetAdd;
+ mergeNodeSet = xmlXPathNodeSetMerge;
switch (axis) {
case AXIS_ANCESTOR:
#ifdef DEBUG_STEP
@@ -9175,7 +11185,20 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
#endif
last = NULL;
- next = xmlXPathNextChild;
+ if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) {
+ /*
+ * This iterator will give us only nodes which can
+ * hold element nodes.
+ */
+ compoundNext = xmlXPathNextDescendantOrSelfElemParent;
+ }
+ if ((test == NODE_TEST_NAME) && (type == NODE_TYPE_NODE)) {
+ /*
+ * Optimization if an element node type is 'element'.
+ */
+ next = xmlXPathNextChildElement;
+ } else
+ next = xmlXPathNextChild;
mergeNodeSet = xmlXPathNodeSetMergeUnique;
break;
case AXIS_DESCENDANT:
@@ -9250,18 +11273,17 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
break;
}
if (next == NULL) {
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(xpctxt, obj);
return(0);
}
- nodelist = obj->nodesetval;
- if (nodelist == NULL) {
- xmlXPathFreeObject(obj);
- valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
+ inputList = obj->nodesetval;
+ if ((inputList == NULL) || (inputList->nodeNr <= 0)) {
+ xmlXPathReleaseObject(xpctxt, obj);
+ valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL));
return(0);
- }
- addNode = xmlXPathNodeSetAddUnique;
- ret = NULL;
+ }
+
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext,
" context contains %d nodes\n", nodelist->nodeNr);
@@ -9307,37 +11329,87 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
* principal node type. For example, child::* will
* select all element children of the context node
*/
- tmp = ctxt->context->node;
- for (i = 0; i < nodelist->nodeNr; i++) {
- ctxt->context->node = nodelist->nodeTab[i];
-
- cur = NULL;
- list = xmlXPathNodeSetCreate(NULL);
+ oldContextNode = xpctxt->node;
+ addNode = xmlXPathNodeSetAddUnique;
+ resultList = NULL;
+ list = NULL;
+ compoundContextNode = NULL;
+ contextNode = NULL;
+ inputIdx = 0;
+
+ while ((inputIdx < inputList->nodeNr) || (contextNode != NULL)) {
+ if (compoundNext != NULL) {
+ /*
+ * This is a compound traversal.
+ */
+ if (contextNode == NULL) {
+ /*
+ * Set the context for the initial traversal.
+ */
+ compoundContextNode = inputList->nodeTab[inputIdx++];
+ contextNode = compoundNext(NULL, compoundContextNode);
+ } else
+ contextNode = compoundNext(contextNode, compoundContextNode);
+ if (contextNode == NULL)
+ continue;
+ /*
+ * Set the context for the main traversal.
+ */
+ xpctxt->node = contextNode;
+ } else
+ xpctxt->node = inputList->nodeTab[inputIdx++];
+
+ if (list == NULL) {
+ list = xmlXPathNodeSetCreate(NULL);
+ if (list == NULL) {
+ total = 0;
+ goto error;
+ }
+ }
+ cur = NULL;
+ specialNodeInSet = 0;
do {
cur = next(ctxt, cur);
if (cur == NULL)
break;
- if ((first != NULL) && (*first == cur))
- break;
- if (((t % 256) == 0) &&
- (first != NULL) && (*first != NULL) &&
- (xmlXPathCmpNodes(*first, cur) >= 0))
- break;
- if ((last != NULL) && (*last == cur))
- break;
- if (((t % 256) == 0) &&
- (last != NULL) && (*last != NULL) &&
- (xmlXPathCmpNodes(cur, *last) >= 0))
- break;
- t++;
+
+ if (first != NULL) {
+ if (*first == cur)
+ break;
+ if ((*first != NULL) &&
+ ((total % 256) == 0) &&
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
+ (xmlXPathCmpNodesExt(*first, cur) >= 0))
+#else
+ (xmlXPathCmpNodes(*first, cur) >= 0))
+#endif
+ {
+ break;
+ }
+ }
+ if (last != NULL) {
+ if (*last == cur)
+ break;
+ if ((*last != NULL) &&
+ ((total % 256) == 0) &&
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
+ (xmlXPathCmpNodesExt(cur, *last) >= 0))
+#else
+ (xmlXPathCmpNodes(cur, *last) >= 0))
+#endif
+ {
+ break;
+ }
+ }
+
+ total++;
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext, " %s", cur->name);
#endif
switch (test) {
- case NODE_TEST_NONE:
- ctxt->context->node = tmp;
- xmlXPathFreeObject(obj);
- STRANGE return(t);
+ case NODE_TEST_NONE:
+ STRANGE
+ goto error;
case NODE_TEST_TYPE:
if ((cur->type == type) ||
((type == NODE_TYPE_NODE) &&
@@ -9351,20 +11423,26 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
(cur->type == XML_CDATA_SECTION_NODE) ||
(cur->type == XML_TEXT_NODE))) ||
((type == NODE_TYPE_TEXT) &&
- (cur->type == XML_CDATA_SECTION_NODE))) {
+ (cur->type == XML_CDATA_SECTION_NODE)))
+ {
#ifdef DEBUG_STEP
- n++;
-#endif
+ nbMatches++;
+#endif
+ if (cur->type == XML_NAMESPACE_DECL)
+ specialNodeInSet = 1;
+ /*
+ * TODO: Don't we need to use xmlXPathNodeSetAddNs()
+ * for namespace nodes here ?
+ */
addNode(list, cur);
}
break;
case NODE_TEST_PI:
- if (cur->type == XML_PI_NODE) {
- if ((name != NULL) &&
- (!xmlStrEqual(name, cur->name)))
- break;
+ if ((cur->type == XML_PI_NODE) &&
+ ((name == NULL) || xmlStrEqual(name, cur->name)))
+ {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
addNode(list, cur);
}
@@ -9373,29 +11451,31 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
if (axis == AXIS_ATTRIBUTE) {
if (cur->type == XML_ATTRIBUTE_NODE) {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
addNode(list, cur);
}
} else if (axis == AXIS_NAMESPACE) {
if (cur->type == XML_NAMESPACE_DECL) {
#ifdef DEBUG_STEP
- n++;
-#endif
- xmlXPathNodeSetAddNs(list, ctxt->context->node,
- (xmlNsPtr) cur);
+ nbMatches++;
+#endif
+ specialNodeInSet = 1;
+ xmlXPathNodeSetAddNs(list, xpctxt->node,
+ (xmlNsPtr) cur);
}
} else {
if (cur->type == XML_ELEMENT_NODE) {
if (prefix == NULL) {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
addNode(list, cur);
} else if ((cur->ns != NULL) &&
- (xmlStrEqual(URI, cur->ns->href))) {
+ (xmlStrEqual(URI, cur->ns->href)))
+ {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
addNode(list, cur);
}
@@ -9409,20 +11489,21 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
case NODE_TEST_NAME:
switch (cur->type) {
case XML_ELEMENT_NODE:
- if (xmlStrEqual(name, cur->name)) {
+ if (xmlStrEqual(name, cur->name)) {
if (prefix == NULL) {
if (cur->ns == NULL) {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
addNode(list, cur);
}
} else {
if ((cur->ns != NULL) &&
(xmlStrEqual(URI,
- cur->ns->href))) {
+ cur->ns->href)))
+ {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
addNode(list, cur);
}
@@ -9437,7 +11518,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
if ((attr->ns == NULL) ||
(attr->ns->prefix == NULL)) {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
addNode(list,
(xmlNodePtr) attr);
@@ -9446,9 +11527,10 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
if ((attr->ns != NULL) &&
(xmlStrEqual(URI,
attr->ns->
- href))) {
+ href)))
+ {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
addNode(list,
(xmlNodePtr) attr);
@@ -9462,21 +11544,22 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
xmlNsPtr ns = (xmlNsPtr) cur;
if ((ns->prefix != NULL) && (name != NULL)
- && (xmlStrEqual(ns->prefix, name))) {
+ && (xmlStrEqual(ns->prefix, name)))
+ {
#ifdef DEBUG_STEP
- n++;
+ nbMatches++;
#endif
+ specialNodeInSet = 1;
xmlXPathNodeSetAddNs(list,
- ctxt->context->node, (xmlNsPtr) cur);
+ xpctxt->node, (xmlNsPtr) cur);
}
}
break;
default:
break;
- }
- break;
- break;
- }
+ } /* switch (cur->type) */
+ break; /* case NODE_TEST_NAME: */
+ } /* switch (test) */
} while (cur != NULL);
/*
@@ -9485,48 +11568,84 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
if ((op->ch2 != -1) && (list != NULL) && (list->nodeNr > 0)) {
xmlXPathObjectPtr obj2;
- valuePush(ctxt, xmlXPathWrapNodeSet(list));
+ valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, list));
xmlXPathCompOpEval(ctxt, &ctxt->comp->steps[op->ch2]);
CHECK_TYPE0(XPATH_NODESET);
obj2 = valuePop(ctxt);
list = obj2->nodesetval;
obj2->nodesetval = NULL;
- xmlXPathFreeObject(obj2);
+ xmlXPathReleaseObject(xpctxt, obj2);
+
if (ctxt->error != XPATH_EXPRESSION_OK) {
- xmlXPathFreeObject(obj);
- xmlXPathFreeNodeSet(list);
- return(0);
+ total = 0;
+ goto error;
}
}
- if (ret == NULL) {
- ret = list;
- } else {
- ret = mergeNodeSet(ret, list);
- xmlXPathFreeNodeSet(list);
+ if (resultList == NULL) {
+ resultList = list;
+ list = NULL;
+ } else if ((list != NULL) && (list->nodeNr > 0)) {
+ resultList = mergeNodeSet(resultList, list);
+ /*
+ * This is the list containing the current matching nodes.
+ * Avoid massive creation/freeing and preserve it for the
+ * next iterations.
+ */
+ /* If a namespace node was put it, then we need a more
+ * time consuming cleanup.
+ */
+ if (specialNodeInSet)
+ xmlXPathNodeSetClear(list);
+ else
+ list->nodeNr = 0;
}
}
- ctxt->context->node = tmp;
+
+ xpctxt->node = oldContextNode;
+ /*
+ * Cleanup the temporary list of current node-test matches.
+ */
+ if ((list != NULL) && (list != resultList)) {
+ xmlXPathFreeNodeSet(list);
+ list = NULL;
+ }
+
#ifdef DEBUG_STEP
xmlGenericError(xmlGenericErrorContext,
"\nExamined %d nodes, found %d nodes at that step\n",
- t, n);
+ total, nbMatches);
#endif
- valuePush(ctxt, xmlXPathWrapNodeSet(ret));
+
+ valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, resultList));
+
if ((obj->boolval) && (obj->user != NULL)) {
+ /*
+ * QUESTION TODO: What does this do and why?
+ */
ctxt->value->boolval = 1;
ctxt->value->user = obj->user;
obj->user = NULL;
obj->boolval = 0;
}
- xmlXPathFreeObject(obj);
- return(t);
+ xmlXPathReleaseObject(xpctxt, obj);
+ return(total);
+
+error:
+ xpctxt->node = oldContextNode;
+ xmlXPathReleaseObject(xpctxt, obj);
+ if ((list != NULL) && (list != resultList)) {
+ xmlXPathFreeNodeSet(list);
+ }
+ if (resultList != NULL)
+ xmlXPathFreeNodeSet(resultList);
+ return(total);
}
/**
* xmlXPathNodeCollectAndTestNth:
* @ctxt: the XPath Parser context
* @op: the XPath precompiled step operation
- * @indx: the index to collect
+ * @reqpos: the requested position wrt to the axis
* @first: pointer to the first element in document order
* @last: pointer to the last element in document order
*
@@ -9539,7 +11658,7 @@ xmlXPathNodeCollectAndTest(xmlXPathParserContextPtr ctxt,
*/
static int
xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
- xmlXPathStepOpPtr op, int indx,
+ xmlXPathStepOpPtr op, int reqpos,
xmlNodePtr * first, xmlNodePtr * last)
{
xmlXPathAxisVal axis = (xmlXPathAxisVal) op->value;
@@ -9548,27 +11667,30 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
const xmlChar *prefix = op->value4;
const xmlChar *name = op->value5;
const xmlChar *URI = NULL;
- int n = 0, t = 0;
+ int pos; /* The current context position */
- int i;
- xmlNodeSetPtr list;
+ int inputIdx, total = 0;
+ xmlNodeSetPtr inputList, list;
xmlXPathTraversalFunction next = NULL;
+ xmlXPathTraversalFunctionExt compoundNext = NULL;
void (*addNode) (xmlNodeSetPtr, xmlNodePtr);
- xmlNodePtr cur = NULL;
+ xmlNodePtr oldContextNode, contextNode, cur, compoundContextNode;
xmlXPathObjectPtr obj;
- xmlNodeSetPtr nodelist;
- xmlNodePtr tmp;
+ xmlXPathContextPtr xpctxt = ctxt->context;
+
CHECK_TYPE0(XPATH_NODESET);
obj = valuePop(ctxt);
addNode = xmlXPathNodeSetAdd;
+
if (prefix != NULL) {
- URI = xmlXPathNsLookup(ctxt->context, prefix);
+ URI = xmlXPathNsLookup(xpctxt, prefix);
if (URI == NULL) {
xmlXPathFreeObject(obj);
XP_ERROR0(XPATH_UNDEF_PREFIX_ERROR);
}
}
+
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext, "new step : ");
if (first != NULL) {
@@ -9615,7 +11737,20 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
xmlGenericError(xmlGenericErrorContext, "axis 'child' ");
#endif
last = NULL;
- next = xmlXPathNextChild;
+ if (op->rewriteType == XP_REWRITE_DOS_CHILD_ELEM) {
+ /*
+ * This iterator will give us only nodes which can
+ * hold element nodes.
+ */
+ compoundNext = xmlXPathNextDescendantOrSelfElemParent;
+ }
+ if ((test == NODE_TEST_NAME) && (type == NODE_TYPE_NODE)) {
+ /*
+ * Optimization if an element node type is 'element'.
+ */
+ next = xmlXPathNextChildElement;
+ } else
+ next = xmlXPathNextChild;
break;
case AXIS_DESCENDANT:
#ifdef DEBUG_STEP_NTH
@@ -9687,17 +11822,17 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
break;
}
if (next == NULL) {
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(xpctxt, obj);
return(0);
}
- nodelist = obj->nodesetval;
- if (nodelist == NULL) {
- xmlXPathFreeObject(obj);
- valuePush(ctxt, xmlXPathWrapNodeSet(NULL));
+ inputList = obj->nodesetval;
+ if ((inputList == NULL) || (inputList->nodeNr <= 0)) {
+ xmlXPathReleaseObject(xpctxt, obj);
+ valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, NULL));
return(0);
}
- addNode = xmlXPathNodeSetAddUnique;
+
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext,
" context contains %d nodes\n", nodelist->nodeNr);
@@ -9743,34 +11878,78 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
* principal node type. For example, child::* will
* select all element children of the context node
*/
- tmp = ctxt->context->node;
+ oldContextNode = xpctxt->node;
+ addNode = xmlXPathNodeSetAddUnique;
+ list = NULL;
+ compoundContextNode = NULL;
+ contextNode = NULL;
+ inputIdx = 0;
list = xmlXPathNodeSetCreate(NULL);
- for (i = 0; i < nodelist->nodeNr; i++) {
- ctxt->context->node = nodelist->nodeTab[i];
+
+ while ((inputIdx < inputList->nodeNr) || (contextNode != NULL)) {
+ if (compoundNext != NULL) {
+ /*
+ * This is a compound traversal.
+ */
+ if (contextNode == NULL) {
+ /*
+ * Set the context for the initial traversal.
+ */
+ compoundContextNode = inputList->nodeTab[inputIdx++];
+ contextNode = compoundNext(NULL, compoundContextNode);
+ } else
+ contextNode = compoundNext(contextNode, compoundContextNode);
+ if (contextNode == NULL)
+ continue;
+ /*
+ * Set the context for the main traversal.
+ */
+ xpctxt->node = contextNode;
+ } else
+ xpctxt->node = inputList->nodeTab[inputIdx++];
cur = NULL;
- n = 0;
+ pos = 0;
do {
cur = next(ctxt, cur);
if (cur == NULL)
break;
- if ((first != NULL) && (*first == cur))
- break;
- if (((t % 256) == 0) &&
- (first != NULL) && (*first != NULL) &&
- (xmlXPathCmpNodes(*first, cur) >= 0))
- break;
- if ((last != NULL) && (*last == cur))
- break;
- if (((t % 256) == 0) &&
- (last != NULL) && (*last != NULL) &&
- (xmlXPathCmpNodes(cur, *last) >= 0))
- break;
- t++;
+
+ if (first != NULL) {
+ if (*first == cur)
+ break;
+ if ((*first != NULL) &&
+ ((total % 256) == 0) &&
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
+ (xmlXPathCmpNodesExt(*first, cur) >= 0))
+#else
+ (xmlXPathCmpNodes(*first, cur) >= 0))
+#endif
+ {
+ break;
+ }
+ }
+ if (last != NULL) {
+ if (*last == cur)
+ break;
+ if ((*last != NULL) &&
+ ((total % 256) == 0) &&
+#ifdef XP_OPTIMIZED_NON_ELEM_COMPARISON
+ (xmlXPathCmpNodesExt(cur, *last) >= 0))
+#else
+ (xmlXPathCmpNodes(cur, *last) >= 0))
+#endif
+ {
+ break;
+ }
+ }
+
+ total++;
switch (test) {
case NODE_TEST_NONE:
- ctxt->context->node = tmp;
- STRANGE return(0);
+ total = 0;
+ STRANGE
+ goto error;
case NODE_TEST_TYPE:
if ((cur->type == type) ||
((type == NODE_TYPE_NODE) &&
@@ -9783,8 +11962,8 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
(cur->type == XML_TEXT_NODE))) ||
((type == NODE_TYPE_TEXT) &&
(cur->type == XML_CDATA_SECTION_NODE))) {
- n++;
- if (n == indx)
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
}
break;
@@ -9793,35 +11972,35 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
if ((name != NULL) &&
(!xmlStrEqual(name, cur->name)))
break;
- n++;
- if (n == indx)
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
}
break;
case NODE_TEST_ALL:
if (axis == AXIS_ATTRIBUTE) {
if (cur->type == XML_ATTRIBUTE_NODE) {
- n++;
- if (n == indx)
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
}
} else if (axis == AXIS_NAMESPACE) {
if (cur->type == XML_NAMESPACE_DECL) {
- n++;
- if (n == indx)
- xmlXPathNodeSetAddNs(list, ctxt->context->node,
+ pos++;
+ if (pos == reqpos)
+ xmlXPathNodeSetAddNs(list, xpctxt->node,
(xmlNsPtr) cur);
}
} else {
if (cur->type == XML_ELEMENT_NODE) {
if (prefix == NULL) {
- n++;
- if (n == indx)
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
} else if ((cur->ns != NULL) &&
(xmlStrEqual(URI, cur->ns->href))) {
- n++;
- if (n == indx)
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
}
}
@@ -9837,16 +12016,17 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
if (xmlStrEqual(name, cur->name)) {
if (prefix == NULL) {
if (cur->ns == NULL) {
- n++;
- if (n == indx)
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
}
} else {
if ((cur->ns != NULL) &&
(xmlStrEqual(URI,
- cur->ns->href))) {
- n++;
- if (n == indx)
+ cur->ns->href)))
+ {
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
}
}
@@ -9858,18 +12038,19 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
if (xmlStrEqual(name, attr->name)) {
if (prefix == NULL) {
if ((attr->ns == NULL) ||
- (attr->ns->prefix == NULL)) {
- n++;
- if (n == indx)
+ (attr->ns->prefix == NULL))
+ {
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
}
} else {
if ((attr->ns != NULL) &&
(xmlStrEqual(URI,
- attr->ns->
- href))) {
- n++;
- if (n == indx)
+ attr->ns->href)))
+ {
+ pos++;
+ if (pos == reqpos)
addNode(list, cur);
}
}
@@ -9882,10 +12063,10 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
if ((ns->prefix != NULL) && (name != NULL)
&& (xmlStrEqual(ns->prefix, name))) {
- n++;
- if (n == indx)
+ pos++;
+ if (pos == reqpos)
xmlXPathNodeSetAddNs(list,
- ctxt->context->node, (xmlNsPtr) cur);
+ xpctxt->node, (xmlNsPtr) cur);
}
}
break;
@@ -9893,27 +12074,40 @@ xmlXPathNodeCollectAndTestNth(xmlXPathParserContextPtr ctxt,
break;
}
break;
- break;
}
- } while (n < indx);
+ } while (pos < reqpos);
}
- ctxt->context->node = tmp;
+ xpctxt->node = oldContextNode;
+
#ifdef DEBUG_STEP_NTH
xmlGenericError(xmlGenericErrorContext,
"\nExamined %d nodes, found %d nodes at that step\n",
- t, list->nodeNr);
+ total, list->nodeNr);
#endif
- valuePush(ctxt, xmlXPathWrapNodeSet(list));
+
+ valuePush(ctxt, xmlXPathCacheWrapNodeSet(xpctxt, list));
+
if ((obj->boolval) && (obj->user != NULL)) {
ctxt->value->boolval = 1;
ctxt->value->user = obj->user;
obj->user = NULL;
obj->boolval = 0;
}
- xmlXPathFreeObject(obj);
- return(t);
+ xmlXPathReleaseObject(xpctxt, obj);
+ return(total);
+
+error:
+ xpctxt->node = oldContextNode;
+ xmlXPathReleaseObject(xpctxt, obj);
+ if (list != NULL)
+ xmlXPathFreeNodeSet(list);
+ return(total);
}
+static int
+xmlXPathCompOpEvalFilterFirst(xmlXPathParserContextPtr ctxt,
+ xmlXPathStepOpPtr op, xmlNodePtr * first);
+
/**
* xmlXPathCompOpEvalFirst:
* @ctxt: the XPath parser context with the compiled expression
@@ -9950,7 +12144,15 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
/*
* limit tree traversing to first node in the result
*/
- xmlXPathNodeSetSort(ctxt->value->nodesetval);
+ /*
+ * 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];
}
cur =
@@ -9966,7 +12168,7 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
arg2->nodesetval);
valuePush(ctxt, arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg2);
/* optimizer */
if (total > cur)
xmlXPathCompSwap(op);
@@ -9981,7 +12183,8 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
- valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node));
return (total);
case XPATH_OP_RESET:
if (op->ch1 != -1)
@@ -10026,7 +12229,8 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
}
case XPATH_OP_VALUE:
valuePush(ctxt,
- xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
+ xmlXPathCacheObjectCopy(ctxt->context,
+ (xmlXPathObjectPtr) op->value4));
return (0);
case XPATH_OP_SORT:
if (op->ch1 != -1)
@@ -10036,9 +12240,15 @@ xmlXPathCompOpEvalFirst(xmlXPathParserContextPtr ctxt,
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));
}
@@ -10087,7 +12297,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
/*
* limit tree traversing to first node in the result
*/
- xmlXPathNodeSetSort(ctxt->value->nodesetval);
+ if (ctxt->value->nodesetval->nodeNr > 1)
+ xmlXPathNodeSetSort(ctxt->value->nodesetval);
*last =
ctxt->value->nodesetval->nodeTab[ctxt->value->
nodesetval->nodeNr -
@@ -10103,7 +12314,7 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
if ((ctxt->value != NULL)
&& (ctxt->value->type == XPATH_NODESET)
&& (ctxt->value->nodesetval != NULL)
- && (ctxt->value->nodesetval->nodeNr >= 1)) {
+ && (ctxt->value->nodesetval->nodeNr >= 1)) { /* TODO: NOP ? */
}
CHECK_TYPE0(XPATH_NODESET);
arg2 = valuePop(ctxt);
@@ -10114,7 +12325,7 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
arg2->nodesetval);
valuePush(ctxt, arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg2);
/* optimizer */
if (total > cur)
xmlXPathCompSwap(op);
@@ -10129,7 +12340,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
- valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node));
return (total);
case XPATH_OP_RESET:
if (op->ch1 != -1)
@@ -10176,7 +12388,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
}
case XPATH_OP_VALUE:
valuePush(ctxt,
- xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
+ xmlXPathCacheObjectCopy(ctxt->context,
+ (xmlXPathObjectPtr) op->value4));
return (0);
case XPATH_OP_SORT:
if (op->ch1 != -1)
@@ -10186,7 +12399,8 @@ xmlXPathCompOpEvalLast(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op,
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);
default:
@@ -10194,6 +12408,299 @@ 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) {
+ xmlXPathReleaseObject(ctxt->context, 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 = xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node);
+ } else {
+ xmlXPathNodeSetAddUnique(tmp->nodesetval,
+ ctxt->context->node);
+ }
+ 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,
+ xmlXPathCacheObjectCopy(ctxt->context,
+ oldlocset->locTab[i]));
+ }
+ /*
+ * Cleanup
+ */
+ if (res != NULL) {
+ xmlXPathReleaseObject(ctxt->context, res);
+ }
+ if (ctxt->value == tmp) {
+ valuePop(ctxt);
+ xmlXPathNodeSetClear(tmp->nodesetval);
+ /*
+ * REVISIT TODO: Don't create a temporary nodeset
+ * for everly iteration.
+ */
+ /* OLD: xmlXPathFreeObject(res); */
+ } else
+ tmp = NULL;
+ 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) {
+ xmlXPathReleaseObject(ctxt->context, tmp);
+ }
+ /*
+ * The result is used as the new evaluation locset.
+ */
+ xmlXPathReleaseObject(ctxt->context, 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 = xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node);
+ } else {
+ xmlXPathNodeSetAddUnique(tmp->nodesetval,
+ ctxt->context->node);
+ }
+ 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) {
+ xmlXPathReleaseObject(ctxt->context, res);
+ }
+ if (ctxt->value == tmp) {
+ valuePop(ctxt);
+ /*
+ * Don't free the temporary nodeset
+ * in order to avoid massive recreation inside this
+ * loop.
+ */
+ xmlXPathNodeSetClear(tmp->nodesetval);
+ } else
+ tmp = NULL;
+ 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) {
+ xmlXPathReleaseObject(ctxt->context, tmp);
+ }
+ /*
+ * The result is used as the new evaluation set.
+ */
+ xmlXPathReleaseObject(ctxt->context, 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, xmlXPathCacheWrapNodeSet(ctxt->context, newset));
+ }
+ ctxt->context->node = oldnode;
+ return(total);
+}
+#endif /* XP_OPTIMIZED_FILTER_FIRST */
+
/**
* xmlXPathCompOpEval:
* @ctxt: the XPath parser context with the compiled expression
@@ -10243,7 +12750,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
arg1 = valuePop(ctxt);
arg1->boolval &= arg2->boolval;
valuePush(ctxt, arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg2);
return (total);
case XPATH_OP_OR:
bakd = ctxt->context->doc;
@@ -10269,7 +12776,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
arg1 = valuePop(ctxt);
arg1->boolval |= arg2->boolval;
valuePush(ctxt, arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg2);
return (total);
case XPATH_OP_EQUAL:
bakd = ctxt->context->doc;
@@ -10288,7 +12795,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
equal = xmlXPathEqualValues(ctxt);
else
equal = xmlXPathNotEqualValues(ctxt);
- valuePush(ctxt, xmlXPathNewBoolean(equal));
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, equal));
return (total);
case XPATH_OP_CMP:
bakd = ctxt->context->doc;
@@ -10304,7 +12811,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
ret = xmlXPathCompareValues(ctxt, op->value, op->value2);
- valuePush(ctxt, xmlXPathNewBoolean(ret));
+ valuePush(ctxt, xmlXPathCacheNewBoolean(ctxt->context, ret));
return (total);
case XPATH_OP_PLUS:
bakd = ctxt->context->doc;
@@ -10371,10 +12878,16 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
CHECK_TYPE0(XPATH_NODESET);
arg1 = valuePop(ctxt);
- arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
- arg2->nodesetval);
+ if ((arg1->nodesetval == NULL) ||
+ ((arg2->nodesetval != NULL) &&
+ (arg2->nodesetval->nodeNr != 0)))
+ {
+ arg1->nodesetval = xmlXPathNodeSetMerge(arg1->nodesetval,
+ arg2->nodesetval);
+ }
+
valuePush(ctxt, arg1);
- xmlXPathFreeObject(arg2);
+ xmlXPathReleaseObject(ctxt->context, arg2);
return (total);
case XPATH_OP_ROOT:
xmlXPathRoot(ctxt);
@@ -10386,7 +12899,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
if (op->ch2 != -1)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch2]);
CHECK_ERROR0;
- valuePush(ctxt, xmlXPathNewNodeSet(ctxt->context->node));
+ valuePush(ctxt, xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node));
return (total);
case XPATH_OP_RESET:
if (op->ch1 != -1)
@@ -10403,18 +12917,18 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
total += xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
CHECK_ERROR0;
-
- /*
- * Optimization for [n] selection where n is a number
- */
- if ((op->ch2 != -1) &&
+
+ if ((op->ch2 != -1) &&
(comp->steps[op->ch2].op == XPATH_OP_PREDICATE) &&
(comp->steps[op->ch2].ch1 == -1) &&
(comp->steps[op->ch2].ch2 != -1) &&
(comp->steps[comp->steps[op->ch2].ch2].op ==
- XPATH_OP_VALUE)) {
+ XPATH_OP_VALUE))
+ {
xmlXPathObjectPtr val;
-
+ /*
+ * Optimization for [n] selection where n is a number
+ */
val = comp->steps[comp->steps[op->ch2].ch2].value4;
if ((val != NULL) && (val->type == XPATH_NUMBER)) {
int indx = (int) val->floatval;
@@ -10433,7 +12947,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
}
case XPATH_OP_VALUE:
valuePush(ctxt,
- xmlXPathObjectCopy((xmlXPathObjectPtr) op->value4));
+ xmlXPathCacheObjectCopy(ctxt->context,
+ (xmlXPathObjectPtr) op->value4));
return (total);
case XPATH_OP_VARIABLE:{
xmlXPathObjectPtr val;
@@ -10560,8 +13075,22 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
* 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;
@@ -10628,7 +13157,17 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
return (total);
}
}
-
+ /*
+ * Process inner predicates first.
+ * Example "index[parent::book][1]":
+ * ...
+ * PREDICATE <-- we are here "[1]"
+ * PREDICATE <-- process "[parent::book]" first
+ * SORT
+ * COLLECT 'parent' 'name' 'node' book
+ * NODE
+ * ELEM Object is a number : 1
+ */
if (op->ch1 != -1)
total +=
xmlXPathCompOpEval(ctxt, &comp->steps[op->ch1]);
@@ -10666,8 +13205,9 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
xmlXPathCompOpEval(ctxt,
&comp->steps[op->ch2]);
res = valuePop(ctxt);
- if (res != NULL)
- xmlXPathFreeObject(res);
+ if (res != NULL) {
+ xmlXPathReleaseObject(ctxt->context, res);
+ }
valuePush(ctxt, obj);
CHECK_ERROR0;
return (total);
@@ -10682,9 +13222,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
ctxt->context->node = oldlocset->locTab[i]->user;
ctxt->context->contextSize = oldlocset->locNr;
ctxt->context->proximityPosition = i + 1;
- tmp = xmlXPathNewNodeSet(ctxt->context->node);
+ tmp = xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node);
valuePush(ctxt, tmp);
-
+
if (op->ch2 != -1)
total +=
xmlXPathCompOpEval(ctxt,
@@ -10708,11 +13249,12 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
/*
* Cleanup
*/
- if (res != NULL)
- xmlXPathFreeObject(res);
+ if (res != NULL) {
+ xmlXPathReleaseObject(ctxt->context, res);
+ }
if (ctxt->value == tmp) {
res = valuePop(ctxt);
- xmlXPathFreeObject(res);
+ xmlXPathReleaseObject(ctxt->context, res);
}
ctxt->context->node = NULL;
@@ -10721,7 +13263,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
/*
* The result is used as the new evaluation locset.
*/
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(ctxt->context, obj);
ctxt->context->node = NULL;
ctxt->context->contextSize = -1;
ctxt->context->proximityPosition = -1;
@@ -10761,13 +13303,39 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
ctxt->context->node = oldnode;
CHECK_ERROR0;
} else {
+ 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);
-
+ /*
+ * SPEC XPath 1.0:
+ * "For each node in the node-set to be filtered, the
+ * PredicateExpr is evaluated with that node as the
+ * context node, with the number of nodes in the
+ * node-set as the context size, and with the proximity
+ * position of the node in the node-set with respect to
+ * the axis as the context position;"
+ * @oldset is the node-set" to be filtered.
+ *
+ * SPEC XPath 1.0:
+ * "only predicates change the context position and
+ * context size (see [2.4 Predicates])."
+ * Example:
+ * node-set context pos
+ * nA 1
+ * nB 2
+ * nC 3
+ * After applying predicate [position() > 1] :
+ * node-set context pos
+ * nB 1
+ * nC 2
+ *
+ * removed the first node in the node-set, then
+ * the context position of the
+ */
for (i = 0; i < oldset->nodeNr; i++) {
/*
* Run the evaluation with a node list made of
@@ -10777,11 +13345,21 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
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 = xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node);
+ } else {
+ xmlXPathNodeSetAddUnique(tmp->nodesetval,
+ ctxt->context->node);
+ }
valuePush(ctxt, tmp);
ctxt->context->contextSize = oldset->nodeNr;
ctxt->context->proximityPosition = i + 1;
-
+ /*
+ * Evaluate the predicate against the context node.
+ * Can/should we optimize position() predicates
+ * here (e.g. "[1]")?
+ */
if (op->ch2 != -1)
total +=
xmlXPathCompOpEval(ctxt,
@@ -10796,6 +13374,10 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
* The result of the evaluation needs to be tested to
* decide whether the filter succeeded or not
*/
+ /*
+ * OPTIMIZE TODO: Can we use
+ * xmlXPathNodeSetAdd*Unique()* instead?
+ */
res = valuePop(ctxt);
if (xmlXPathEvaluatePredicateResult(ctxt, res)) {
xmlXPathNodeSetAdd(newset, oldset->nodeTab[i]);
@@ -10804,26 +13386,34 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
/*
* Cleanup
*/
- if (res != NULL)
- xmlXPathFreeObject(res);
+ if (res != NULL) {
+ xmlXPathReleaseObject(ctxt->context, res);
+ }
if (ctxt->value == tmp) {
- res = valuePop(ctxt);
- xmlXPathFreeObject(res);
- }
-
+ valuePop(ctxt);
+ xmlXPathNodeSetClear(tmp->nodesetval);
+ /*
+ * Don't free the temporary nodeset
+ * in order to avoid massive recreation inside this
+ * loop.
+ */
+ } else
+ tmp = NULL;
ctxt->context->node = NULL;
}
-
+ if (tmp != NULL)
+ xmlXPathReleaseObject(ctxt->context, tmp);
/*
* The result is used as the new evaluation set.
*/
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(ctxt->context, 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));
+ valuePush(ctxt,
+ xmlXPathCacheWrapNodeSet(ctxt->context, newset));
}
ctxt->context->node = oldnode;
return (total);
@@ -10834,8 +13424,11 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
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 LIBXML_XPTR_ENABLED
case XPATH_OP_RANGETO:{
@@ -10869,8 +13462,9 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
ctxt->context->proximityPosition = 0;
total += xmlXPathCompOpEval(ctxt,&comp->steps[op->ch2]);
res = valuePop(ctxt);
- if (res != NULL)
- xmlXPathFreeObject(res);
+ if (res != NULL) {
+ xmlXPathReleaseObject(ctxt->context, res);
+ }
valuePush(ctxt, obj);
CHECK_ERROR0;
return (total);
@@ -10885,7 +13479,8 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
ctxt->context->node = oldlocset->locTab[i]->user;
ctxt->context->contextSize = oldlocset->locNr;
ctxt->context->proximityPosition = i + 1;
- tmp = xmlXPathNewNodeSet(ctxt->context->node);
+ tmp = xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node);
valuePush(ctxt, tmp);
if (op->ch2 != -1)
@@ -10922,11 +13517,12 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
/*
* Cleanup
*/
- if (res != NULL)
- xmlXPathFreeObject(res);
+ if (res != NULL) {
+ xmlXPathReleaseObject(ctxt->context, res);
+ }
if (ctxt->value == tmp) {
res = valuePop(ctxt);
- xmlXPathFreeObject(res);
+ xmlXPathReleaseObject(ctxt->context, res);
}
ctxt->context->node = NULL;
@@ -10946,7 +13542,11 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
* in the nodeset.
*/
ctxt->context->node = oldset->nodeTab[i];
- tmp = xmlXPathNewNodeSet(ctxt->context->node);
+ /*
+ * OPTIMIZE TODO: Avoid recreation for every iteration.
+ */
+ tmp = xmlXPathCacheNewNodeSet(ctxt->context,
+ ctxt->context->node);
valuePush(ctxt, tmp);
if (op->ch2 != -1)
@@ -10969,11 +13569,12 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
/*
* Cleanup
*/
- if (res != NULL)
- xmlXPathFreeObject(res);
+ if (res != NULL) {
+ xmlXPathReleaseObject(ctxt->context, res);
+ }
if (ctxt->value == tmp) {
res = valuePop(ctxt);
- xmlXPathFreeObject(res);
+ xmlXPathReleaseObject(ctxt->context, res);
}
ctxt->context->node = NULL;
@@ -10984,7 +13585,7 @@ xmlXPathCompOpEval(xmlXPathParserContextPtr ctxt, xmlXPathStepOpPtr op)
/*
* The result is used as the new evaluation set.
*/
- xmlXPathFreeObject(obj);
+ xmlXPathReleaseObject(ctxt->context, obj);
ctxt->context->node = NULL;
ctxt->context->contextSize = -1;
ctxt->context->proximityPosition = -1;
@@ -11036,7 +13637,7 @@ xmlXPathRunStreamEval(xmlXPathContextPtr ctxt, xmlPatternPtr comp) {
printf("stream eval: depth %d from root %d\n", max_depth, from_root);
#endif
- retval = xmlXPathNewNodeSet(NULL);
+ retval = xmlXPathCacheNewNodeSet(ctxt, NULL);
if (retval == NULL)
return(NULL);
@@ -11378,12 +13979,18 @@ xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
const xmlChar *tmp;
/*
- * We don't try to handle :: constructs, just the simplied form at
- * this point
+ * We don't try to handle expressions using the verbose axis
+ * specifiers ("::"), just the simplied form at this point.
+ * Additionally, if there is no list of namespaces available and
+ * there's a ":" in the expression, indicating a prefixed QName,
+ * then we won't try to compile either. xmlPatterncompile() needs
+ * to have a list of namespaces at compilation time in order to
+ * compile prefixed name tests.
*/
tmp = xmlStrchr(str, ':');
- if ((tmp != NULL) && (tmp[1] == ':'))
- return(NULL);
+ if ((tmp != NULL) &&
+ ((ctxt == NULL) || (ctxt->nsNr == 0) || (tmp[1] == ':')))
+ return(NULL);
if (ctxt != NULL) {
dict = ctxt->dict;
@@ -11405,6 +14012,9 @@ xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
stream = xmlPatterncompile(str, dict, XML_PATTERN_XPATH,
&namespaces[0]);
+ if (namespaces != NULL) {
+ xmlFree((xmlChar **)namespaces);
+ }
if ((stream != NULL) && (xmlPatternStreamable(stream) == 1)) {
comp = xmlXPathNewCompExpr();
if (comp == NULL) {
@@ -11423,6 +14033,59 @@ xmlXPathTryStreamCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
}
#endif /* XPATH_STREAMING */
+static int
+xmlXPathCanRewriteDosExpression(xmlChar *expr)
+{
+ if (expr == NULL)
+ return(0);
+ do {
+ if ((*expr == '/') && (*(++expr) == '/'))
+ return(1);
+ } while (*expr++);
+ return(0);
+}
+static void
+xmlXPathRewriteDOSExpression(xmlXPathCompExprPtr comp, xmlXPathStepOpPtr op)
+{
+ /*
+ * Try to rewrite "descendant-or-self::node()/foo" to an optimized
+ * internal representation.
+ */
+ if (op->ch1 != -1) {
+ if ((op->op == XPATH_OP_COLLECT /* 11 */) &&
+ ((xmlXPathAxisVal) op->value == AXIS_CHILD /* 4 */) &&
+ ((xmlXPathTestVal) op->value2 == NODE_TEST_NAME /* 5 */) &&
+ ((xmlXPathTypeVal) op->value3 == NODE_TYPE_NODE /* 0 */))
+ {
+ /*
+ * This is an "foo"
+ */
+ xmlXPathStepOpPtr prevop = &comp->steps[op->ch1];
+
+ if ((prevop->op == XPATH_OP_COLLECT /* 11 */) &&
+ (prevop->ch1 != -1) &&
+ ((xmlXPathAxisVal) prevop->value ==
+ AXIS_DESCENDANT_OR_SELF) &&
+ (prevop->ch2 == -1) &&
+ ((xmlXPathTestVal) prevop->value2 == NODE_TEST_TYPE) &&
+ ((xmlXPathTypeVal) prevop->value3 == NODE_TYPE_NODE) &&
+ (comp->steps[prevop->ch1].op == XPATH_OP_ROOT))
+ {
+ /*
+ * This is a "descendant-or-self::node()" without predicates.
+ * Eliminate it.
+ */
+ op->ch1 = prevop->ch1;
+ op->rewriteType = XP_REWRITE_DOS_CHILD_ELEM;
+ }
+ }
+ if (op->ch1 != -1)
+ xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch1]);
+ }
+ if (op->ch2 != -1)
+ xmlXPathRewriteDOSExpression(comp, &comp->steps[op->ch2]);
+}
+
/**
* xmlXPathCtxtCompile:
* @ctxt: an XPath context
@@ -11447,7 +14110,7 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
xmlXPathInit();
pctxt = xmlXPathNewParserContext(str, ctxt);
- xmlXPathCompileExpr(pctxt);
+ xmlXPathCompileExpr(pctxt, 1);
if( pctxt->error != XPATH_EXPRESSION_OK )
{
@@ -11474,7 +14137,12 @@ xmlXPathCtxtCompile(xmlXPathContextPtr ctxt, const xmlChar *str) {
#ifdef DEBUG_EVAL_COUNTS
comp->string = xmlStrdup(str);
comp->nb = 0;
-#endif
+#endif
+ if ((comp->nbStep > 2) &&
+ (xmlXPathCanRewriteDosExpression(comp->expr) == 1))
+ {
+ xmlXPathRewriteDOSExpression(comp, &comp->steps[comp->last]);
+ }
}
return(comp);
}
@@ -11547,8 +14215,8 @@ xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
tmp = valuePop(ctxt);
if (tmp != NULL) {
if (tmp != init)
- stack++;
- xmlXPathFreeObject(tmp);
+ stack++;
+ xmlXPathReleaseObject(ctx, tmp);
}
} while (tmp != NULL);
if ((stack != 0) && (res != NULL)) {
@@ -11559,9 +14227,7 @@ xmlXPathCompiledEval(xmlXPathCompExprPtr comp, xmlXPathContextPtr ctx) {
if (ctxt->error != XPATH_EXPRESSION_OK) {
xmlXPathFreeObject(res);
res = NULL;
- }
-
-
+ }
ctxt->comp = NULL;
xmlXPathFreeParserContext(ctxt);
#ifndef LIBXML_THREAD_ENABLED
@@ -11596,10 +14262,17 @@ xmlXPathEvalExpr(xmlXPathParserContextPtr ctxt) {
} else
#endif
{
- xmlXPathCompileExpr(ctxt);
+ xmlXPathCompileExpr(ctxt, 1);
+ if ((ctxt->comp != NULL) &&
+ (ctxt->comp->nbStep > 2) &&
+ (xmlXPathCanRewriteDosExpression(ctxt->comp->expr) == 1))
+ {
+ xmlXPathRewriteDOSExpression(ctxt->comp,
+ &ctxt->comp->steps[comp->last]);
+ }
}
CHECK_ERROR;
- xmlXPathRunEval(ctxt);
+ xmlXPathRunEval(ctxt);
}
/**
@@ -11644,8 +14317,8 @@ xmlXPathEval(const xmlChar *str, xmlXPathContextPtr ctx) {
tmp = valuePop(ctxt);
if (tmp != NULL) {
if (tmp != init)
- stack++;
- xmlXPathFreeObject(tmp);
+ stack++;
+ xmlXPathReleaseObject(ctx, tmp);
}
} while (tmp != NULL);
if ((stack != 0) && (res != NULL)) {
@@ -11694,7 +14367,7 @@ xmlXPathEvalExpression(const xmlChar *str, xmlXPathContextPtr ctxt) {
do {
tmp = valuePop(pctxt);
if (tmp != NULL) {
- xmlXPathFreeObject(tmp);
+ xmlXPathReleaseObject(ctxt, tmp);
stack++;
}
} while (tmp != NULL);
@@ -11815,9 +14488,10 @@ xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
}
}
}
- valuePush(ctxt, xmlXPathNewString(xmlBufferContent(target)));
+ valuePush(ctxt, xmlXPathCacheNewString(ctxt->context,
+ xmlBufferContent(target)));
xmlBufferFree(target);
- xmlXPathFreeObject(str);
+ xmlXPathReleaseObject(ctxt->context, str);
}
/**