While investigating bug #350247, I noticed that xmlSchemaIDCMatcher

* xmlschemas.c: While investigating bug #350247, I noticed
  that xmlSchemaIDCMatcher structs are massively recreated
  although only a maximum of 3 structs is used at the same
  time; added a cache for those structures to the
  validation context.
diff --git a/xmlschemas.c b/xmlschemas.c
index 3baae05..11e3eac 100644
--- a/xmlschemas.c
+++ b/xmlschemas.c
@@ -84,6 +84,8 @@
 
 /* #define DEBUG_IDC_NODE_TABLE */
 
+/* #define WXS_ELEM_DECL_CONS_ENABLED */
+
 #ifdef DEBUG_IDC
  #ifndef DEBUG_IDC_NODE_TABLE
   #define DEBUG_IDC_NODE_TABLE
@@ -183,6 +185,8 @@
 
 #define WXS_PARTICLE_TERM(p) (WXS_PARTICLE(p))->children
 
+#define WXS_PARTICLE_TERM_AS_ELEM(p) (WXS_ELEM_CAST WXS_PARTICLE_TERM(p))
+
 #define WXS_PARTICLE_MODEL(p) WXS_MODEL_GROUP_CAST WXS_PARTICLE(p)->children
 /*
 * Macros for model groups definitions.
@@ -242,6 +246,10 @@
 #define WXS_IS_TYPE_NOT_FIXED_1(item) \
     (((item)->type != XML_SCHEMA_TYPE_BASIC) && \
      (((item)->flags & XML_SCHEMAS_TYPE_FIXUP_1) == 0))
+
+#define WXS_TYPE_IS_GLOBAL(t) ((t)->flags & XML_SCHEMAS_TYPE_GLOBAL)
+
+#define WXS_TYPE_IS_LOCAL(t) (((t)->flags & XML_SCHEMAS_TYPE_GLOBAL) == 0)
 /*
 * Macros for exclusively for complex types.
 */
@@ -836,12 +844,12 @@
     int type;
     int depth; /* the tree depth at creation time */
     xmlSchemaIDCMatcherPtr next; /* next in the list */
+    xmlSchemaIDCMatcherPtr nextCached; /* next in the cache list */
     xmlSchemaIDCAugPtr aidc; /* the augmented IDC item */
     int idcType;
     xmlSchemaPSVIIDCKeyPtr **keySeqs; /* the key-sequences of the target
                                          elements */
     int sizeKeySeqs;
-    int targetDepth;
     xmlSchemaItemListPtr targets; /* list of target-node
                                      (xmlSchemaPSVIIDCNodePtr) entries */
 };
@@ -993,6 +1001,7 @@
 
     xmlSchemaIDCStateObjPtr xpathStates; /* first active state object. */
     xmlSchemaIDCStateObjPtr xpathStatePool; /* first stored state object. */
+    xmlSchemaIDCMatcherPtr idcMatcherCache; /* Cache for IDC matcher objects. */
 
     xmlSchemaPSVIIDCNodePtr *idcNodes; /* list of all IDC node-table entries*/
     int nbIdcNodes;
@@ -19744,6 +19753,123 @@
     }
 }
 
+#ifdef WXS_ELEM_DECL_CONS_ENABLED /* enable when finished */
+/**
+ * xmlSchemaCheckElementDeclComponent
+ * @pctxt: the schema parser context
+ * @ctxtComponent: the context component (an element declaration)
+ * @ctxtParticle: the first particle of the context component
+ * @searchParticle: the element declaration particle to be analysed
+ *
+ * Schema Component Constraint: Element Declarations Consistent 
+ */
+static int
+xmlSchemaCheckElementDeclConsistent(xmlSchemaParserCtxtPtr pctxt,
+				    xmlSchemaBasicItemPtr ctxtComponent,
+				    xmlSchemaParticlePtr ctxtParticle,				    
+				    xmlSchemaParticlePtr searchParticle,
+				    xmlSchemaParticlePtr curParticle,
+				    int search)
+{
+    return(0);
+
+    int ret = 0;
+    xmlSchemaParticlePtr cur = curParticle;
+    if (curParticle == NULL) {	
+	return(0);
+    }
+    if (WXS_PARTICLE_TERM(curParticle) == NULL) {
+	/*
+	* Just return in this case. A missing "term" of the particle
+	* might arise due to an invalid "term" component.
+	*/
+	return(0);
+    }    
+    while (cur != NULL) {
+	switch (WXS_PARTICLE_TERM(cur)->type) {
+	    case XML_SCHEMA_TYPE_ANY:
+		break;
+	    case XML_SCHEMA_TYPE_ELEMENT:
+		if (search == 0) {
+		    ret = xmlSchemaCheckElementDeclConsistent(pctxt,
+			ctxtComponent, ctxtParticle, cur, ctxtParticle, 1);
+		    if (ret != 0)
+			return(ret);		    
+		} else {
+		    xmlSchemaElementPtr elem =
+			WXS_ELEM_CAST(WXS_PARTICLE_TERM(cur));
+		    /*
+		    * SPEC Element Declarations Consistent:
+		    * "If the {particles} contains, either directly,
+		    * indirectly (that is, within the {particles} of a
+		    * contained model group, recursively) or ·implicitly·
+		    * two or more element declaration particles with
+		    * the same {name} and {target namespace}, then
+		    * all their type definitions must be the same
+		    * top-level definition [...]"
+		    */
+		    if (xmlStrEqual(WXS_PARTICLE_TERM_AS_ELEM(cur)->name,
+			    WXS_PARTICLE_TERM_AS_ELEM(searchParticle)->name) &&
+			xmlStrEqual(WXS_PARTICLE_TERM_AS_ELEM(cur)->targetNamespace,
+			    WXS_PARTICLE_TERM_AS_ELEM(searchParticle)->targetNamespace))
+		    {
+			xmlChar *strA = NULL, *strB = NULL;
+			
+			xmlSchemaCustomErr(ACTXT_CAST pctxt,
+			    /* TODO: error code */
+			    XML_SCHEMAP_COS_NONAMBIG,
+			    WXS_ITEM_NODE(cur), NULL,
+			    "In the content model of %s, there are multiple "
+			    "element declarations for '%s' with different "
+			    "type definitions",
+			    xmlSchemaGetComponentDesignation(&strA,
+				ctxtComponent),
+			    xmlSchemaFormatQName(&strB,
+				WXS_PARTICLE_TERM_AS_ELEM(cur)->targetNamespace,
+				WXS_PARTICLE_TERM_AS_ELEM(cur)->name));
+			FREE_AND_NULL(strA);
+			FREE_AND_NULL(strB);
+			return(XML_SCHEMAP_COS_NONAMBIG);
+		    }
+		}	    
+		break;
+	    case XML_SCHEMA_TYPE_SEQUENCE: {		
+		break;
+		}
+	    case XML_SCHEMA_TYPE_CHOICE:{
+		/*
+		xmlSchemaTreeItemPtr sub;
+		
+		sub = WXS_PARTICLE_TERM(particle)->children;  (xmlSchemaParticlePtr) 
+		while (sub != NULL) {
+		    ret = xmlSchemaCheckElementDeclConsistent(pctxt, ctxtComponent,
+			ctxtParticle, ctxtElem);
+		    if (ret != 0)
+			return(ret);
+		    sub = sub->next;
+		}
+		*/
+		break;
+		}
+	    case XML_SCHEMA_TYPE_ALL:
+		break;
+	    case XML_SCHEMA_TYPE_GROUP:
+		break;
+	    default:
+		xmlSchemaInternalErr2(ACTXT_CAST pctxt,
+		    "xmlSchemaCheckElementDeclConsistent",
+		    "found unexpected term of type '%s' in content model",
+		    WXS_ITEM_TYPE_NAME(WXS_PARTICLE_TERM(cur)), NULL);
+		return(-1);
+	}
+	cur = (xmlSchemaParticlePtr) cur->next;
+    }
+
+exit:
+    return(ret);
+}
+#endif
+
 /**
  * xmlSchemaCheckElementDeclComponent
  * @item:  an schema element declaration/particle
@@ -19751,8 +19877,7 @@
  * @name:  the name of the attribute
  *
  * Validates the value constraints of an element declaration.
- *
- * Fixes finish doing the computations on the element declarations.
+ * Adds substitution group members. 
  */
 static void
 xmlSchemaCheckElementDeclComponent(xmlSchemaElementPtr elemDecl,
@@ -19763,8 +19888,12 @@
     if (elemDecl->flags & XML_SCHEMAS_ELEM_INTERNAL_CHECKED)
 	return;
     elemDecl->flags |= XML_SCHEMAS_ELEM_INTERNAL_CHECKED;
-    if (xmlSchemaCheckElemPropsCorrect(ctxt, elemDecl) == 0)
-	xmlSchemaCheckElemSubstGroup(ctxt, elemDecl);
+    if (xmlSchemaCheckElemPropsCorrect(ctxt, elemDecl) == 0) {
+	/*
+	* Adds substitution group members.
+	*/
+	xmlSchemaCheckElemSubstGroup(ctxt, elemDecl);	
+    }
 }
 
 /**
@@ -20587,6 +20716,7 @@
     xmlSchemaTreeItemPtr item, *items;
     int nbItems, i, ret = 0;
     xmlSchemaBucketPtr oldbucket = con->bucket;
+    xmlSchemaElementPtr elemDecl;
 
 #define FIXHFAILURE if (pctxt->err == XML_SCHEMAP_INTERNAL) goto exit_failure;
 
@@ -20860,7 +20990,7 @@
     if (pctxt->nberrors != 0)
 	goto exit_error;
     /*
-    * At this point we need all simple types to be builded and checked.
+    * At this point we need build and check all simple types.
     */
     /*
     * Apply contraints for attribute declarations.
@@ -20927,8 +21057,8 @@
     if (pctxt->nberrors != 0)
 	goto exit_error;
 
-    /*
-    * Fixup complex types.
+    /*    
+    * Complex types are builded and checked.
     */
     for (i = 0; i < nbItems; i++) {
 	item = con->pending->items[i];
@@ -20951,11 +21081,8 @@
     * will create particles and model groups in some cases.
     */
     items = (xmlSchemaTreeItemPtr *) con->pending->items;
-    nbItems = con->pending->nbItems;
-
-    /*
-    * At this point all complex types need to be builded and checked.
-    */
+    nbItems = con->pending->nbItems;    
+    
     /*
     * Apply some constraints for element declarations.
     */
@@ -20963,13 +21090,30 @@
 	item = items[i];
 	switch (item->type) {
 	    case XML_SCHEMA_TYPE_ELEMENT:
+		elemDecl = (xmlSchemaElementPtr) item;
 		
-		if ((((xmlSchemaElementPtr) item)->flags & 
-		    XML_SCHEMAS_ELEM_INTERNAL_CHECKED) == 0) {
+		if ((elemDecl->flags & XML_SCHEMAS_ELEM_INTERNAL_CHECKED) == 0)
+		{
 		    xmlSchemaCheckElementDeclComponent(
-			(xmlSchemaElementPtr) item, pctxt);
+			(xmlSchemaElementPtr) elemDecl, pctxt);
 		    FIXHFAILURE;
 		}
+
+#ifdef WXS_ELEM_DECL_CONS_ENABLED
+		/*
+		* Schema Component Constraint: Element Declarations Consistent
+		* Apply this constraint to local types of element declarations.
+		*/
+		if ((WXS_ELEM_TYPEDEF(elemDecl) != NULL) &&
+		    (WXS_IS_COMPLEX(WXS_ELEM_TYPEDEF(elemDecl))) &&
+		    (WXS_TYPE_IS_LOCAL(WXS_ELEM_TYPEDEF(elemDecl))))
+		{
+		    xmlSchemaCheckElementDeclConsistent(pctxt,
+			WXS_BASIC_CAST elemDecl,
+			WXS_TYPE_PARTICLE(WXS_ELEM_TYPEDEF(elemDecl)),
+			NULL, NULL, 0);
+		}
+#endif
 		break;
 	    default:
 		break;
@@ -20977,6 +21121,7 @@
     }
     if (pctxt->nberrors != 0)
 	goto exit_error;
+ 
     /*
     * Finally we can build the automaton from the content model of
     * complex types.
@@ -22013,7 +22158,7 @@
 
     while (matcher != NULL) {
 	next = matcher->next;
-	if (matcher->keySeqs != NULL) {
+	if (matcher->keySeqs != NULL) {	    
 	    int i;
 	    for (i = 0; i < matcher->sizeKeySeqs; i++)
 		if (matcher->keySeqs[i] != NULL)
@@ -22044,6 +22189,63 @@
 }
 
 /**
+ * xmlSchemaIDCReleaseMatcherList:
+ * @vctxt: the WXS validation context
+ * @matcher: the first IDC matcher in the list
+ *
+ * Caches a list of IDC matchers for reuse.
+ */
+static void
+xmlSchemaIDCReleaseMatcherList(xmlSchemaValidCtxtPtr vctxt,
+			       xmlSchemaIDCMatcherPtr matcher)
+{
+    xmlSchemaIDCMatcherPtr next;
+
+    while (matcher != NULL) {
+	next = matcher->next;
+	if (matcher->keySeqs != NULL) {	    
+	    int i;
+	    /*
+	    * Don't free the array, but only the content.
+	    */
+	    for (i = 0; i < matcher->sizeKeySeqs; i++)
+		if (matcher->keySeqs[i] != NULL) {
+		    xmlFree(matcher->keySeqs[i]);
+		    matcher->keySeqs[i] = NULL;
+		}
+	}
+	if (matcher->targets) {
+	    if (matcher->idcType == XML_SCHEMA_TYPE_IDC_KEYREF) {
+		int i;
+		xmlSchemaPSVIIDCNodePtr idcNode;
+		/*
+		* Node-table items for keyrefs are not stored globally
+		* to the validation context, since they are not bubbled.
+		* We need to free them here.
+		*/
+		for (i = 0; i < matcher->targets->nbItems; i++) {
+		    idcNode =
+			(xmlSchemaPSVIIDCNodePtr) matcher->targets->items[i];
+		    xmlFree(idcNode->keys);
+		    xmlFree(idcNode);
+		}
+	    }
+	    xmlSchemaItemListFree(matcher->targets);
+	    matcher->targets = NULL;
+	}	
+	matcher->next = NULL;
+	/*
+	* Cache the matcher.
+	*/
+	if (vctxt->idcMatcherCache != NULL)
+	    matcher->nextCached = vctxt->idcMatcherCache;
+	vctxt->idcMatcherCache = matcher;
+
+	matcher = next;
+    }
+}
+
+/**
  * xmlSchemaIDCAddStateObject:
  * @vctxt: the WXS validation context
  * @matcher: the IDC matcher
@@ -22823,7 +23025,7 @@
 	    /*
 	    * Link it to the pool of reusable state objects.
 	    */
-	    vctxt->xpathStatePool = sto;	    
+	    vctxt->xpathStatePool = sto;
 	    sto = nextsto;
 	} else
 	    sto = sto->next;
@@ -22920,14 +23122,23 @@
 	/*
 	* Create an IDC matcher for every IDC definition.
 	*/
-	matcher = (xmlSchemaIDCMatcherPtr) 
-	    xmlMalloc(sizeof(xmlSchemaIDCMatcher));
-	if (matcher == NULL) {
-	    xmlSchemaVErrMemory(vctxt, 
-		"allocating an IDC matcher", NULL);
-	    return (-1);
+	if (vctxt->idcMatcherCache != NULL) {
+	    /*
+	    * Reuse a cached matcher.
+	    */
+	    matcher = vctxt->idcMatcherCache;
+	    vctxt->idcMatcherCache = matcher->nextCached;
+	    matcher->nextCached = NULL;
+	} else {
+	    matcher = (xmlSchemaIDCMatcherPtr) 
+		xmlMalloc(sizeof(xmlSchemaIDCMatcher));
+	    if (matcher == NULL) {
+		xmlSchemaVErrMemory(vctxt, 
+		    "allocating an IDC matcher", NULL);
+		return (-1);
+	    }
+	    memset(matcher, 0, sizeof(xmlSchemaIDCMatcher));
 	}
-	memset(matcher, 0, sizeof(xmlSchemaIDCMatcher));
 	if (last == NULL)
 	    vctxt->inode->idcMatchers = matcher;
 	else
@@ -23673,8 +23884,14 @@
     return (0);
 }
 
+/**
+ * xmlSchemaClearElemInfo:
+ * @vctxt: the WXS validation context
+ * @ielem: the element information item
+ */
 static void
-xmlSchemaClearElemInfo(xmlSchemaNodeInfoPtr ielem)
+xmlSchemaClearElemInfo(xmlSchemaValidCtxtPtr vctxt,
+		       xmlSchemaNodeInfoPtr ielem)
 {
     ielem->hasKeyrefs = 0;
     ielem->appliedXPath = 0;
@@ -23700,9 +23917,13 @@
     }
     if (ielem->idcMatchers != NULL) {
 	/*
-	* URGENT OPTIMIZE TODO: Use a pool of IDC matchers.
+	* REVISIT OPTIMIZE TODO: Use a pool of IDC matchers.
+	*   Does it work?
 	*/
+	xmlSchemaIDCReleaseMatcherList(vctxt, ielem->idcMatchers);
+#if 0
 	xmlSchemaIDCFreeMatcherList(ielem->idcMatchers);
+#endif
 	ielem->idcMatchers = NULL;
     }
     if (ielem->idcTable != NULL) {
@@ -26068,7 +26289,7 @@
     * VAL TODO: Don't free the PSVI IDC tables if they are
     * requested for the PSVI.
     */
-    xmlSchemaClearElemInfo(inode);
+    xmlSchemaClearElemInfo(vctxt, inode);
     /*
     * Skip further processing if we are on the validation root.
     */
@@ -27168,6 +27389,18 @@
 	} while (cur != NULL);
 	vctxt->aidcs = NULL;
     }
+    if (vctxt->idcMatcherCache != NULL) {
+	xmlSchemaIDCMatcherPtr matcher = vctxt->idcMatcherCache, tmp;
+
+	while (matcher) {
+	    tmp = matcher;
+	    matcher = matcher->nextCached;
+	    xmlSchemaIDCFreeMatcherList(tmp);
+	}
+	vctxt->idcMatcherCache = NULL;
+    }
+
+
     if (vctxt->idcNodes != NULL) {
 	int i;
 	xmlSchemaPSVIIDCNodePtr item;
@@ -27180,6 +27413,7 @@
 	xmlFree(vctxt->idcNodes);
 	vctxt->idcNodes = NULL;
 	vctxt->nbIdcNodes = 0;
+	vctxt->sizeIdcNodes = 0;
     }
     /*
     * Note that we won't delete the XPath state pool here.
@@ -27205,7 +27439,7 @@
 	    ei = vctxt->elemInfos[i];
 	    if (ei == NULL)
 		break;
-	    xmlSchemaClearElemInfo(ei);
+	    xmlSchemaClearElemInfo(vctxt, ei);
 	}
     }    
     xmlSchemaItemListClear(vctxt->nodeQNames);
@@ -27251,10 +27485,14 @@
 	xmlFree(ctxt->idcKeys);
     }
 
-    if (ctxt->xpathStates != NULL)
+    if (ctxt->xpathStates != NULL) {
 	xmlSchemaFreeIDCStateObjList(ctxt->xpathStates);
-    if (ctxt->xpathStatePool != NULL)
+	ctxt->xpathStates = NULL;
+    }
+    if (ctxt->xpathStatePool != NULL) {
 	xmlSchemaFreeIDCStateObjList(ctxt->xpathStatePool);
+	ctxt->xpathStatePool = NULL;
+    }
 
     /*
     * Augmented IDC information.
@@ -27288,7 +27526,7 @@
 	    ei = ctxt->elemInfos[i];
 	    if (ei == NULL)
 		break;
-	    xmlSchemaClearElemInfo(ei);
+	    xmlSchemaClearElemInfo(ctxt, ei);
 	    xmlFree(ei);
 	}
 	xmlFree(ctxt->elemInfos);