Index: subversion/libsvn_repos/authz.c
===================================================================
--- subversion/libsvn_repos/authz.c	(revision 1508093)
+++ subversion/libsvn_repos/authz.c	(working copy)
@@ -37,6 +37,9 @@
 #include "private/svn_fspath.h"
 #include "repos.h"
 
+/* we need just svn_config_t.pool */
+#include "../libsvn_subr/config_impl.h"
+
 
 /*** Structures. ***/
 
@@ -44,7 +47,7 @@
    lookup. */
 struct authz_lookup_baton {
   /* The authz configuration. */
-  svn_config_t *config;
+  svn_authz_t *authz;
 
   /* The user to authorize. */
   const char *user;
@@ -76,14 +79,24 @@
                            enumerator, if any. */
 };
 
+svn_boolean_t authz_use_cache = TRUE;       /* FIXME devel only */
+
 /* Currently this structure is just a wrapper around a
    svn_config_t. */
 struct svn_authz_t
 {
   svn_config_t *cfg;
+  const char *cached_user;
+  apr_hash_t *cache;
 };
 
+struct access_cache_t
+{
+  svn_repos_authz_access_t allow;
+  svn_repos_authz_access_t deny;
+};
 
+
 
 /*** Checking access. ***/
 
@@ -241,10 +254,10 @@
    */
   if (rule_match_string[0] == '@')
     return authz_group_contains_user(
-      b->config, &rule_match_string[1], b->user, pool);
+      b->authz->cfg, &rule_match_string[1], b->user, pool);
   else if (rule_match_string[0] == '&')
     return authz_alias_is_user(
-      b->config, &rule_match_string[1], b->user, pool);
+      b->authz->cfg, &rule_match_string[1], b->user, pool);
   else
     return (strcmp(b->user, rule_match_string) == 0);
 }
@@ -295,6 +308,62 @@
 }
 
 
+/* Wrapper for svn_config_enumerate2(..., authz_parse_line, ...)
+ * that manages the allow/deny per-section cache.
+ */
+static void
+authz_lookup_cached(const char *path, 
+                    struct authz_lookup_baton *baton,
+                    apr_pool_t *pool)
+{
+  struct svn_config_t *cfg = baton->authz->cfg;
+  struct apr_hash_t *cache = baton->authz->cache;
+  struct access_cache_t *access;
+  svn_repos_authz_access_t saved_allow, saved_deny;
+
+  if (!cache) 
+    {
+      svn_config_enumerate2(cfg, path,
+                            authz_parse_line, baton, pool);
+      return;
+    }
+
+  /* if section [path] doesn't exist, then we're done */
+  if (!svn_config_has_section(cfg, path))
+    return; 
+
+  /* if we already know allow/deny for section [path] ... */
+  access = apr_hash_get(cache, path, APR_HASH_KEY_STRING);
+  if (access)
+    {
+      /* ... combine baton with cached allow/deny and we're done */
+      baton->allow |= access->allow;
+      baton->deny |= access->deny;
+      return;
+    }
+
+  /* reuse baton: save baton's allow/deny information and reset it */
+  saved_allow = baton->allow;
+  saved_deny = baton->deny;
+  baton->allow = baton->deny = svn_authz_none;
+
+  svn_config_enumerate2(cfg, path, authz_parse_line, baton, pool);
+
+  /* cache the result 
+   * Note: Use baton->config->pool instead of pool as the cache has
+   * the same lifetime as baton->config.
+   */
+  access = apr_palloc(cfg->pool, sizeof(*access));
+  access->allow = baton->allow;
+  access->deny = baton->deny;
+  apr_hash_set(cache, apr_pstrdup(cfg->pool, path), APR_HASH_KEY_STRING, access);
+
+  /* combine the result with saved values */
+  baton->allow |= saved_allow;
+  baton->deny |= saved_deny;
+}
+
+
 /* Callback to parse a section and update the authz_baton if the
  * section denies access to the subtree the baton describes.
  */
@@ -310,9 +379,8 @@
     return TRUE;
 
   /* Work out what this section grants. */
-  b->allow = b->deny = 0;
-  svn_config_enumerate2(b->config, section_name,
-                        authz_parse_line, b, pool);
+  b->allow = b->deny = svn_authz_none;
+  authz_lookup_cached(section_name, b, pool);
 
   /* Has the section explicitly determined an access? */
   conclusive = authz_access_is_determined(b->allow, b->deny,
@@ -338,7 +406,7 @@
  * successfully determined.
  */
 static svn_boolean_t
-authz_get_path_access(svn_config_t *cfg, const char *repos_name,
+authz_get_path_access(svn_authz_t *authz, const char *repos_name,
                       const char *path, const char *user,
                       svn_repos_authz_access_t required_access,
                       svn_boolean_t *access_granted,
@@ -346,14 +414,13 @@
 {
   const char *qualified_path;
   struct authz_lookup_baton baton = { 0 };
-
-  baton.config = cfg;
+ 
+  baton.authz = authz;
   baton.user = user;
 
   /* Try to locate a repository-specific block first. */
   qualified_path = apr_pstrcat(pool, repos_name, ":", path, (char *)NULL);
-  svn_config_enumerate2(cfg, qualified_path,
-                        authz_parse_line, &baton, pool);
+  authz_lookup_cached(qualified_path, &baton, pool);
 
   *access_granted = authz_access_is_granted(baton.allow, baton.deny,
                                             required_access);
@@ -364,7 +431,7 @@
     return TRUE;
 
   /* No repository specific rule, try pan-repository rules. */
-  svn_config_enumerate2(cfg, path, authz_parse_line, &baton, pool);
+  authz_lookup_cached(path, &baton, pool);
 
   *access_granted = authz_access_is_granted(baton.allow, baton.deny,
                                             required_access);
@@ -382,14 +449,14 @@
  * searched, return the updated authorization status.
  */
 static svn_boolean_t
-authz_get_tree_access(svn_config_t *cfg, const char *repos_name,
+authz_get_tree_access(svn_authz_t *authz, const char *repos_name,
                       const char *path, const char *user,
                       svn_repos_authz_access_t required_access,
                       apr_pool_t *pool)
 {
   struct authz_lookup_baton baton = { 0 };
 
-  baton.config = cfg;
+  baton.authz = authz;
   baton.user = user;
   baton.required_access = required_access;
   baton.repos_path = path;
@@ -398,7 +465,7 @@
   /* Default to access granted if no rules say otherwise. */
   baton.access = TRUE;
 
-  svn_config_enumerate_sections2(cfg, authz_parse_section,
+  svn_config_enumerate_sections2(authz->cfg, authz_parse_section,
                                  &baton, pool);
 
   return baton.access;
@@ -420,9 +487,8 @@
                  strlen(b->qualified_repos_path)) == 0)
     {
       b->allow = b->deny = svn_authz_none;
-
-      svn_config_enumerate2(b->config, section_name,
-                            authz_parse_line, baton, pool);
+  
+      authz_lookup_cached(section_name, baton, pool);
       b->access = authz_access_is_granted(b->allow, b->deny,
                                           b->required_access);
 
@@ -440,14 +506,14 @@
  * to any path within the REPOSITORY.  Return TRUE if so.  Use POOL
  * for temporary allocations. */
 static svn_boolean_t
-authz_get_any_access(svn_config_t *cfg, const char *repos_name,
+authz_get_any_access(svn_authz_t *authz, const char *repos_name,
                      const char *user,
                      svn_repos_authz_access_t required_access,
                      apr_pool_t *pool)
 {
   struct authz_lookup_baton baton = { 0 };
 
-  baton.config = cfg;
+  baton.authz = authz;
   baton.user = user;
   baton.required_access = required_access;
   baton.access = FALSE; /* Deny access by default. */
@@ -460,7 +526,7 @@
    * may not always have). So we end up enumerating the sections in
    * the authz CFG and stop on the first match with some access for
    * this user. */
-  svn_config_enumerate_sections2(cfg, authz_get_any_access_parser_cb,
+  svn_config_enumerate_sections2(authz->cfg, authz_get_any_access_parser_cb,
                                  &baton, pool);
 
   /* If walking the configuration was inconclusive, deny access. */
@@ -754,6 +820,9 @@
 {
   struct authz_validate_baton baton = { 0 };
 
+  authz->cached_user = NULL;    /* FIXME does apr_palloc zero the alloced stuff? */
+  authz->cache = NULL;          /* FIXME does apr_palloc zero the alloced stuff? */
+
   baton.err = SVN_NO_ERROR;
   baton.config = authz->cfg;
 
@@ -1027,13 +1096,40 @@
 {
   const char *current_path;
 
+  /* Note: Below we use authz->cfg->pool instead of pool as the cache must
+   * have the same lifetime as authz->cfg.
+   */
+  if (authz_use_cache)         /* FIXME devel only */
+    {
+      /* Check whether user is still the same as authz->cached_user;
+       * otherwise blow away authz->cache and remember the new user.
+       */
+      if (!( user && authz->cached_user 
+             && strcmp(user, authz->cached_user) == 0 ))
+        {
+          if (user)
+            {
+              authz->cache = NULL;
+              authz->cached_user = apr_pstrdup(authz->cfg->pool, user);
+            }
+          else if (authz->cached_user)
+            {
+              authz->cache = NULL;
+              authz->cached_user = NULL;
+            }
+        }
+              
+      if (!authz->cache)
+          authz->cache = apr_hash_make(authz->cfg->pool);
+    }
+
   if (!repos_name)
     repos_name = "";
 
   /* If PATH is NULL, check if the user has *any* access. */
   if (!path)
     {
-      *access_granted = authz_get_any_access(authz->cfg, repos_name,
+      *access_granted = authz_get_any_access(authz, repos_name,
                                              user, required_access, pool);
       return SVN_NO_ERROR;
     }
@@ -1045,11 +1141,9 @@
   path = svn_fspath__canonicalize(path, pool);
   current_path = path;
 
-  while (!authz_get_path_access(authz->cfg, repos_name,
+  while (!authz_get_path_access(authz, repos_name,
                                 current_path, user,
-                                required_access,
-                                access_granted,
-                                pool))
+                                required_access, access_granted, pool))
     {
       /* Stop if the loop hits the repository root with no
          results. */
@@ -1068,7 +1162,7 @@
      the entire authz config to see whether any child paths are denied
      to the requested user. */
   if (*access_granted && (required_access & svn_authz_recursive))
-    *access_granted = authz_get_tree_access(authz->cfg, repos_name, path,
+    *access_granted = authz_get_tree_access(authz, repos_name, path,
                                             user, required_access, pool);
 
   return SVN_NO_ERROR;
