From d3e3526111097560cf7c002613e2cb1d469b59e0 Mon Sep 17 00:00:00 2001
From: Nick Wellnhofer <wellnhofer@aevum.de>
Date: Sat, 21 Dec 2024 16:03:46 +0100
Subject: [PATCH] xpath: Use separate static hash table for standard functions

This avoids registering standard functions when creating an XPath
context.

Lookup of extension functions is a bit slower now, but ultimately, all
function lookups should be moved to the compilation phase.

(cherry picked from commit bf5fcf6e646bb51a0f6a3655a1d64bea97274867)
---
 xpath.c | 170 ++++++++++++++++++++++++++++++++------------------------
 1 file changed, 98 insertions(+), 72 deletions(-)

diff --git a/xpath.c b/xpath.c
index 485d7747..21711653 100644
--- a/xpath.c
+++ b/xpath.c
@@ -136,11 +136,48 @@
 
 #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
 
-/************************************************************************
- *									*
- *			Floating point stuff				*
- *									*
- ************************************************************************/
+static void
+xmlXPathNameFunction(xmlXPathParserContextPtr ctxt, int nargs);
+
+static const struct {
+    const char *name;
+    xmlXPathFunction func;
+} xmlXPathStandardFunctions[] = {
+    { "boolean", xmlXPathBooleanFunction },
+    { "ceiling", xmlXPathCeilingFunction },
+    { "count", xmlXPathCountFunction },
+    { "concat", xmlXPathConcatFunction },
+    { "contains", xmlXPathContainsFunction },
+    { "id", xmlXPathIdFunction },
+    { "false", xmlXPathFalseFunction },
+    { "floor", xmlXPathFloorFunction },
+    { "last", xmlXPathLastFunction },
+    { "lang", xmlXPathLangFunction },
+    { "local-name", xmlXPathLocalNameFunction },
+    { "not", xmlXPathNotFunction },
+    { "name", xmlXPathNameFunction },
+    { "namespace-uri", xmlXPathNamespaceURIFunction },
+    { "normalize-space", xmlXPathNormalizeFunction },
+    { "number", xmlXPathNumberFunction },
+    { "position", xmlXPathPositionFunction },
+    { "round", xmlXPathRoundFunction },
+    { "string", xmlXPathStringFunction },
+    { "string-length", xmlXPathStringLengthFunction },
+    { "starts-with", xmlXPathStartsWithFunction },
+    { "substring", xmlXPathSubstringFunction },
+    { "substring-before", xmlXPathSubstringBeforeFunction },
+    { "substring-after", xmlXPathSubstringAfterFunction },
+    { "sum", xmlXPathSumFunction },
+    { "true", xmlXPathTrueFunction },
+    { "translate", xmlXPathTranslateFunction }
+};
+
+#define NUM_STANDARD_FUNCTIONS \
+    (sizeof(xmlXPathStandardFunctions) / sizeof(xmlXPathStandardFunctions[0]))
+
+#define SF_HASH_SIZE 64
+
+static unsigned char xmlXPathSFHash[SF_HASH_SIZE];
 
 double xmlXPathNAN = 0.0;
 double xmlXPathPINF = 0.0;
@@ -156,6 +193,18 @@ xmlXPathInit(void) {
     xmlInitParser();
 }
 
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static unsigned
+xmlXPathSFComputeHash(const xmlChar *name) {
+    unsigned hashValue = 5381;
+    const xmlChar *ptr;
+
+    for (ptr = name; *ptr; ptr++)
+        hashValue = hashValue * 33 + *ptr;
+
+    return(hashValue);
+}
+
 /**
  * xmlInitXPathInternal:
  *
@@ -164,6 +213,8 @@ xmlXPathInit(void) {
 ATTRIBUTE_NO_SANITIZE("float-divide-by-zero")
 void
 xmlInitXPathInternal(void) {
+    size_t i;
+
 #if defined(NAN) && defined(INFINITY)
     xmlXPathNAN = NAN;
     xmlXPathPINF = INFINITY;
@@ -175,8 +226,34 @@ xmlInitXPathInternal(void) {
     xmlXPathPINF = 1.0 / zero;
     xmlXPathNINF = -xmlXPathPINF;
 #endif
+
+    /*
+     * Initialize hash table for standard functions
+     */
+
+    for (i = 0; i < SF_HASH_SIZE; i++)
+        xmlXPathSFHash[i] = UCHAR_MAX;
+
+    for (i = 0; i < NUM_STANDARD_FUNCTIONS; i++) {
+        const char *name = xmlXPathStandardFunctions[i].name;
+        int bucketIndex = xmlXPathSFComputeHash(BAD_CAST name) % SF_HASH_SIZE;
+
+        while (xmlXPathSFHash[bucketIndex] != UCHAR_MAX) {
+            bucketIndex += 1;
+            if (bucketIndex >= SF_HASH_SIZE)
+                bucketIndex = 0;
+        }
+
+        xmlXPathSFHash[bucketIndex] = i;
+    }
 }
 
+/************************************************************************
+ *									*
+ *			Floating point stuff				*
+ *									*
+ ************************************************************************/
+
 /**
  * xmlXPathIsNaN:
  * @val:  a double value
@@ -3979,18 +4056,6 @@ xmlXPathRegisterFuncLookup (xmlXPathContextPtr ctxt,
  */
 xmlXPathFunction
 xmlXPathFunctionLookup(xmlXPathContextPtr ctxt, const xmlChar *name) {
-    if (ctxt == NULL)
-	return (NULL);
-
-    if (ctxt->funcLookupFunc != NULL) {
-	xmlXPathFunction ret;
-	xmlXPathFuncLookupFunc f;
-
-	f = ctxt->funcLookupFunc;
-	ret = f(ctxt->funcLookupData, name, NULL);
-	if (ret != NULL)
-	    return(ret);
-    }
     return(xmlXPathFunctionLookupNS(ctxt, name, NULL));
 }
 
@@ -4015,6 +4080,22 @@ xmlXPathFunctionLookupNS(xmlXPathContextPtr ctxt, const xmlChar *name,
     if (name == NULL)
 	return(NULL);
 
+    if (ns_uri == NULL) {
+        int bucketIndex = xmlXPathSFComputeHash(name) % SF_HASH_SIZE;
+
+        while (xmlXPathSFHash[bucketIndex] != UCHAR_MAX) {
+            int funcIndex = xmlXPathSFHash[bucketIndex];
+
+            if (strcmp(xmlXPathStandardFunctions[funcIndex].name,
+                       (char *) name) == 0)
+                return(xmlXPathStandardFunctions[funcIndex].func);
+
+            bucketIndex += 1;
+            if (bucketIndex >= SF_HASH_SIZE)
+                bucketIndex = 0;
+        }
+    }
+
     if (ctxt->funcLookupFunc != NULL) {
 	xmlXPathFuncLookupFunc f;
 
@@ -13494,61 +13575,6 @@ xmlXPathEscapeUriFunction(xmlXPathParserContextPtr ctxt, int nargs) {
 void
 xmlXPathRegisterAllFunctions(xmlXPathContextPtr ctxt)
 {
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"boolean",
-                         xmlXPathBooleanFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"ceiling",
-                         xmlXPathCeilingFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"count",
-                         xmlXPathCountFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"concat",
-                         xmlXPathConcatFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"contains",
-                         xmlXPathContainsFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"id",
-                         xmlXPathIdFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"false",
-                         xmlXPathFalseFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"floor",
-                         xmlXPathFloorFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"last",
-                         xmlXPathLastFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"lang",
-                         xmlXPathLangFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"local-name",
-                         xmlXPathLocalNameFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"not",
-                         xmlXPathNotFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"name",
-                         xmlXPathNameFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"namespace-uri",
-                         xmlXPathNamespaceURIFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"normalize-space",
-                         xmlXPathNormalizeFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"number",
-                         xmlXPathNumberFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"position",
-                         xmlXPathPositionFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"round",
-                         xmlXPathRoundFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string",
-                         xmlXPathStringFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"string-length",
-                         xmlXPathStringLengthFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"starts-with",
-                         xmlXPathStartsWithFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring",
-                         xmlXPathSubstringFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-before",
-                         xmlXPathSubstringBeforeFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"substring-after",
-                         xmlXPathSubstringAfterFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"sum",
-                         xmlXPathSumFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"true",
-                         xmlXPathTrueFunction);
-    xmlXPathRegisterFunc(ctxt, (const xmlChar *)"translate",
-                         xmlXPathTranslateFunction);
-
     xmlXPathRegisterFuncNS(ctxt, (const xmlChar *)"escape-uri",
 	 (const xmlChar *)"http://www.w3.org/2002/08/xquery-functions",
                          xmlXPathEscapeUriFunction);
-- 
2.47.1

