You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by cm...@apache.org on 2013/05/10 16:58:56 UTC

svn commit: r1481041 [19/38] - in /subversion/branches/master-passphrase: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/client-side/svncopy/ contrib/hook-scripts/ contrib/server-side/fsfsfixer/ contrib/server-side/fsfsf...

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/auth.c Fri May 10 14:58:47 2013
@@ -26,6 +26,7 @@
 #include <apr_tables.h>
 #include <apr_strings.h>
 
+#include "svn_hash.h"
 #include "svn_types.h"
 #include "svn_string.h"
 #include "svn_error.h"
@@ -35,6 +36,7 @@
 #include "svn_dso.h"
 #include "svn_version.h"
 #include "private/svn_auth_private.h"
+#include "private/svn_dep_compat.h"
 
 #include "auth.h"
 
@@ -152,17 +154,14 @@ svn_auth_open(svn_auth_baton_t **auth_ba
       provider = APR_ARRAY_IDX(providers, i, svn_auth_provider_object_t *);
 
       /* Add it to the appropriate table in the auth_baton */
-      table = apr_hash_get(ab->tables,
-                           provider->vtable->cred_kind, APR_HASH_KEY_STRING);
+      table = svn_hash_gets(ab->tables, provider->vtable->cred_kind);
       if (! table)
         {
           table = apr_pcalloc(pool, sizeof(*table));
           table->providers
             = apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
 
-          apr_hash_set(ab->tables,
-                       provider->vtable->cred_kind, APR_HASH_KEY_STRING,
-                       table);
+          svn_hash_sets(ab->tables, provider->vtable->cred_kind, table);
         }
       APR_ARRAY_PUSH(table->providers, svn_auth_provider_object_t *)
         = provider;
@@ -178,17 +177,26 @@ svn_auth_set_parameter(svn_auth_baton_t 
                        const char *name,
                        const void *value)
 {
-  apr_hash_set(auth_baton->parameters, name, APR_HASH_KEY_STRING, value);
+  svn_hash_sets(auth_baton->parameters, name, value);
 }
 
 const void *
 svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
                        const char *name)
 {
-  return apr_hash_get(auth_baton->parameters, name, APR_HASH_KEY_STRING);
+  return svn_hash_gets(auth_baton->parameters, name);
 }
 
 
+/* Return the key used to address the in-memory cache of auth
+   credentials of type CRED_KIND and associated with REALMSTRING. */
+static const char *
+make_cache_key(const char *cred_kind,
+               const char *realmstring,
+               apr_pool_t *pool)
+{
+  return apr_pstrcat(pool, cred_kind, ":", realmstring, (char *)NULL);
+}
 
 svn_error_t *
 svn_auth_first_credentials(void **credentials,
@@ -208,16 +216,15 @@ svn_auth_first_credentials(void **creden
   const char *cache_key;
 
   /* Get the appropriate table of providers for CRED_KIND. */
-  table = apr_hash_get(auth_baton->tables, cred_kind, APR_HASH_KEY_STRING);
+  table = svn_hash_gets(auth_baton->tables, cred_kind);
   if (! table)
     return svn_error_createf(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
                              _("No provider registered for '%s' credentials"),
                              cred_kind);
 
   /* First, see if we have cached creds in the auth_baton. */
-  cache_key = apr_pstrcat(pool, cred_kind, ":", realmstring, (char *)NULL);
-  creds = apr_hash_get(auth_baton->creds_cache,
-                       cache_key, APR_HASH_KEY_STRING);
+  cache_key = make_cache_key(cred_kind, realmstring, pool);
+  creds = svn_hash_gets(auth_baton->creds_cache, cache_key);
   if (creds)
     {
        got_first = FALSE;
@@ -230,9 +237,11 @@ svn_auth_first_credentials(void **creden
         {
           provider = APR_ARRAY_IDX(table->providers, i,
                                    svn_auth_provider_object_t *);
-          SVN_ERR(provider->vtable->first_credentials
-                  (&creds, &iter_baton, provider->provider_baton,
-                   auth_baton->parameters, realmstring, auth_baton->pool));
+          SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,
+                                                      provider->provider_baton,
+                                                      auth_baton->parameters,
+                                                      realmstring,
+                                                      auth_baton->pool));
 
           if (creds != NULL)
             {
@@ -258,10 +267,9 @@ svn_auth_first_credentials(void **creden
       *state = iterstate;
 
       /* Put the creds in the cache */
-      apr_hash_set(auth_baton->creds_cache,
-                   apr_pstrdup(auth_baton->pool, cache_key),
-                   APR_HASH_KEY_STRING,
-                   creds);
+      svn_hash_sets(auth_baton->creds_cache,
+                    apr_pstrdup(auth_baton->pool, cache_key),
+                    creds);
     }
 
   *credentials = creds;
@@ -290,27 +298,24 @@ svn_auth_next_credentials(void **credent
                                svn_auth_provider_object_t *);
       if (! state->got_first)
         {
-          SVN_ERR(provider->vtable->first_credentials
-                  (&creds, &(state->provider_iter_baton),
-                   provider->provider_baton, auth_baton->parameters,
-                   state->realmstring, auth_baton->pool));
+          SVN_ERR(provider->vtable->first_credentials(
+                      &creds, &(state->provider_iter_baton),
+                      provider->provider_baton, auth_baton->parameters,
+                      state->realmstring, auth_baton->pool));
           state->got_first = TRUE;
         }
-      else
+      else if (provider->vtable->next_credentials)
         {
-          if (provider->vtable->next_credentials)
-            SVN_ERR(provider->vtable->next_credentials
-                    (&creds, state->provider_iter_baton,
-                     provider->provider_baton, auth_baton->parameters,
-                     state->realmstring, auth_baton->pool));
+          SVN_ERR(provider->vtable->next_credentials(
+                      &creds, state->provider_iter_baton,
+                      provider->provider_baton, auth_baton->parameters,
+                      state->realmstring, auth_baton->pool));
         }
 
       if (creds != NULL)
         {
           /* Put the creds in the cache */
-          apr_hash_set(auth_baton->creds_cache,
-                       state->cache_key, APR_HASH_KEY_STRING,
-                       creds);
+          svn_hash_sets(auth_baton->creds_cache, state->cache_key, creds);
           break;
         }
 
@@ -338,15 +343,13 @@ svn_auth_save_credentials(svn_auth_iters
     return SVN_NO_ERROR;
 
   auth_baton = state->auth_baton;
-  creds = apr_hash_get(state->auth_baton->creds_cache,
-                       state->cache_key, APR_HASH_KEY_STRING);
+  creds = svn_hash_gets(state->auth_baton->creds_cache, state->cache_key);
   if (! creds)
     return SVN_NO_ERROR;
 
   /* Do not save the creds if SVN_AUTH_PARAM_NO_AUTH_CACHE is set */
-  no_auth_cache = apr_hash_get(auth_baton->parameters,
-                               SVN_AUTH_PARAM_NO_AUTH_CACHE,
-                               APR_HASH_KEY_STRING);
+  no_auth_cache = svn_hash_gets(auth_baton->parameters,
+                                SVN_AUTH_PARAM_NO_AUTH_CACHE);
   if (no_auth_cache)
     return SVN_NO_ERROR;
 
@@ -389,6 +392,32 @@ svn_auth_save_credentials(svn_auth_iters
   return SVN_NO_ERROR;
 }
 
+
+svn_error_t *
+svn_auth_forget_credentials(svn_auth_baton_t *auth_baton,
+                            const char *cred_kind,
+                            const char *realmstring,
+                            apr_pool_t *scratch_pool)
+{
+  SVN_ERR_ASSERT((cred_kind && realmstring) || (!cred_kind && !realmstring));
+
+  /* If we have a CRED_KIND and REALMSTRING, we clear out just the
+     cached item (if any).  Otherwise, empty the whole hash. */
+  if (cred_kind)
+    {
+      svn_hash_sets(auth_baton->creds_cache,
+                    make_cache_key(cred_kind, realmstring, scratch_pool),
+                    NULL);
+    }
+  else
+    {
+      apr_hash_clear(auth_baton->creds_cache);
+    }
+
+  return SVN_NO_ERROR;
+}
+
+
 svn_auth_ssl_server_cert_info_t *
 svn_auth_ssl_server_cert_info_dup
   (const svn_auth_ssl_server_cert_info_t *info, apr_pool_t *pool)
@@ -619,20 +648,3 @@ svn_auth_get_platform_specific_client_pr
 
   return SVN_NO_ERROR;
 }
-
-svn_error_t *
-svn_auth_cleanup_walk(svn_auth_baton_t *baton,
-                      svn_auth_cleanup_callback cleanup,
-                      void *cleanup_baton,
-                      apr_pool_t *scratch_pool)
-{
-
-  if (apr_hash_get(baton->tables, SVN_AUTH_CRED_SIMPLE, APR_HASH_KEY_STRING))
-    {
-      SVN_ERR(svn_auth__simple_cleanup_walk(baton, cleanup, cleanup_baton,
-                                            baton->creds_cache, scratch_pool));
-    }
-  /* ### Maybe add support for other providers? */
-
-  return SVN_NO_ERROR;
-}

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/auth.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/auth.h?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/auth.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/auth.h Fri May 10 14:58:47 2013
@@ -21,8 +21,12 @@
  * ====================================================================
  */
 
-#ifndef SVN_SUBR_AUTH_H
-#define SVN_SUBR_AUTH_H
+#ifndef SVN_LIBSVN_SUBR_AUTH_H
+#define SVN_LIBSVN_SUBR_AUTH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
 
 #include "svn_auth.h"
 
@@ -37,13 +41,9 @@ svn_auth__file_path(const char **path,
                     const char *config_dir,
                     apr_pool_t *pool);
 
-/* Implementation of svn_auth_cleanup_walk() for the "simple" provider */
-svn_error_t *
-svn_auth__simple_cleanup_walk(svn_auth_baton_t *baton,
-                              svn_auth_cleanup_callback cleanup,
-                              void *cleanup_baton,
-                              apr_hash_t *creds_cache,
-                              apr_pool_t *scratch_pool);
 
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
 
-#endif
+#endif /* SVN_LIBSVN_SUBR_AUTH_H */

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/cache-inprocess.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/cache-inprocess.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/cache-inprocess.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/cache-inprocess.c Fri May 10 14:58:47 2013
@@ -214,7 +214,7 @@ inprocess_cache_get(void **value_p,
 {
   inprocess_cache_t *cache = cache_void;
   char* buffer = NULL;
-  apr_size_t size;
+  apr_size_t size = 0;
 
   if (key)
     SVN_MUTEX__WITH_LOCK(cache->mutex,
@@ -642,6 +642,7 @@ svn_cache__create_inprocess(svn_cache__t
 
   wrapper->vtable = &inprocess_cache_vtable;
   wrapper->cache_internal = cache;
+  wrapper->pretend_empty = !!getenv("SVN_X_DOES_NOT_MARK_THE_SPOT");
 
   *cache_p = wrapper;
   return SVN_NO_ERROR;

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/cache-membuffer.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/cache-membuffer.c Fri May 10 14:58:47 2013
@@ -45,6 +45,8 @@
  *
  * 1. A linear data buffer containing cached items in a serialized
  *    representation. There may be arbitrary gaps between entries.
+ *    This buffer is sub-devided into (currently two) cache levels.
+ *
  * 2. A directory of cache entries. This is organized similar to CPU
  *    data caches: for every possible key, there is exactly one group
  *    of entries that may contain the header info for an item with
@@ -56,23 +58,30 @@
  * between different processes and / or to persist them on disk. These
  * out-of-process features have not been implemented, yet.
  *
+ * Superficially, cache levels are being used as usual: insertion happens
+ * into L1 and evictions will promote items to L2.  But their whole point
+ * is a different one.  L1 uses a circular buffer, i.e. we have perfect
+ * caching for the last N bytes where N is the size of L1.  L2 uses a more
+ * elaborate scheme based on priorities and hit counts as described below.
+ *
  * The data buffer usage information is implicitly given by the directory
  * entries. Every USED entry has a reference to the previous and the next
  * used dictionary entry and this double-linked list is ordered by the
  * offsets of their item data within the data buffer. So removing data,
  * for instance, is done simply by unlinking it from the chain, implicitly
  * marking the entry as well as the data buffer section previously
- * associated to it as unused.
+ * associated to it as unused.  First and last element of that chain are
+ * being referenced from the respective cache level.
  *
- * Insertion can occur at only one, sliding position. It is marked by its
- * offset in the data buffer plus the index of the first used entry at or
- * behind that position. If this gap is too small to accommodate the new
- * item, the insertion window is extended as described below. The new entry
- * will always be inserted at the bottom end of the window and since the
- * next used entry is known, properly sorted insertion is possible.
+ * Insertion can occur at only one, sliding position per cache level.  It is
+ * marked by its offset in the data buffer and the index of the first used
+ * entry at or behind that position.  If this gap is too small to accommodate
+ * the new item, the insertion window is extended as described below. The new
+ * entry will always be inserted at the bottom end of the window and since
+ * the next used entry is known, properly sorted insertion is possible.
  *
  * To make the cache perform robustly in a wide range of usage scenarios,
- * a randomized variant of LFU is used (see ensure_data_insertable for
+ * L2 uses a randomized variant of LFU (see ensure_data_insertable_l2 for
  * details). Every item holds a read hit counter and there is a global read
  * hit counter. The more hits an entry has in relation to the average, the
  * more it is likely to be kept using a rand()-based condition. The test is
@@ -86,10 +95,10 @@
  * they get not used for a while. Also, even a cache thrashing situation
  * about 50% of the content survives every 50% of the cache being re-written
  * with new entries. For details on the fine-tuning involved, see the
- * comments in ensure_data_insertable().
+ * comments in ensure_data_insertable_l2().
  *
  * To limit the entry size and management overhead, not the actual item keys
- * but only their MD5 checksums will not be stored. This is reasonably safe
+ * but only their MD5-based hashes will be stored. This is reasonably safe
  * to do since users have only limited control over the full keys, even if
  * these contain folder paths. So, it is very hard to deliberately construct
  * colliding keys. Random checksum collisions can be shown to be extremely
@@ -116,11 +125,11 @@
 /* By default, don't create cache segments smaller than this value unless
  * the total cache size itself is smaller.
  */
-#define DEFAULT_MIN_SEGMENT_SIZE 0x2000000ull
+#define DEFAULT_MIN_SEGMENT_SIZE APR_UINT64_C(0x2000000)
 
 /* The minimum segment size we will allow for multi-segmented caches
  */
-#define MIN_SEGMENT_SIZE 0x10000ull
+#define MIN_SEGMENT_SIZE APR_UINT64_C(0x10000)
 
 /* The maximum number of segments allowed. Larger numbers reduce the size
  * of each segment, in turn reducing the max size of a cachable item.
@@ -133,7 +142,7 @@
 /* As of today, APR won't allocate chunks of 4GB or more. So, limit the
  * segment size to slightly below that.
  */
-#define MAX_SEGMENT_SIZE 0xffff0000ull
+#define MAX_SEGMENT_SIZE APR_UINT64_C(0xffff0000)
 
 /* We don't mark the initialization status for every group but initialize
  * a number of groups at once. That will allow for a very small init flags
@@ -313,7 +322,7 @@ static svn_error_t* assert_equal_tags(co
 /* A single dictionary entry. Since all entries will be allocated once
  * during cache creation, those entries might be either used or unused.
  * An entry is used if and only if it is contained in the doubly-linked
- * list of used entries.
+ * list of used entries per cache level.
  */
 typedef struct entry_t
 {
@@ -321,7 +330,8 @@ typedef struct entry_t
    */
   entry_key_t key;
 
-  /* The offset of the cached item's serialized data within the data buffer.
+  /* The offset of the cached item's serialized data within the caches
+   * DATA buffer.
    */
   apr_uint64_t offset;
 
@@ -337,15 +347,15 @@ typedef struct entry_t
 
   /* Reference to the next used entry in the order defined by offset.
    * NO_INDEX indicates the end of the list; this entry must be referenced
-   * by the caches membuffer_cache_t.last member. NO_INDEX also implies
-   * that the data buffer is not used beyond offset+size.
+   * by the caches cache_level_t.last member.  NO_INDEX also implies that
+   * the data buffer is not used beyond offset+size.
    * Only valid for used entries.
    */
   apr_uint32_t next;
 
   /* Reference to the previous used entry in the order defined by offset.
    * NO_INDEX indicates the end of the list; this entry must be referenced
-   * by the caches membuffer_cache_t.first member.
+   * by the caches cache_level_t.first member.
    * Only valid for used entries.
    */
   apr_uint32_t previous;
@@ -368,28 +378,12 @@ typedef struct entry_group_t
   entry_t entries[GROUP_SIZE];
 } entry_group_t;
 
-/* The cache header structure.
+/* Per-cache level header structure.  Instances of this are members of
+ * svn_membuffer_t and will use non-overlapping sections of its DATA buffer.
+ * All offset values are global / absolute to that whole buffer.
  */
-struct svn_membuffer_t
+typedef struct cache_level_t
 {
-  /* Number of cache segments. Must be a power of 2.
-     Please note that this structure represents only one such segment
-     and that all segments must / will report the same values here. */
-  apr_uint32_t segment_count;
-
-  /* The dictionary, GROUP_SIZE * group_count entries long. Never NULL.
-   */
-  entry_group_t *directory;
-
-  /* Flag array with group_count / GROUP_INIT_GRANULARITY _bit_ elements.
-   * Allows for efficiently marking groups as "not initialized".
-   */
-  unsigned char *group_initialized;
-
-  /* Size of dictionary in groups. Must be > 0.
-   */
-  apr_uint32_t group_count;
-
   /* Reference to the first (defined by the order content in the data
    * buffer) dictionary entry used by any data item.
    * NO_INDEX for an empty cache.
@@ -410,18 +404,46 @@ struct svn_membuffer_t
   apr_uint32_t next;
 
 
-  /* Pointer to the data buffer, data_size bytes long. Never NULL.
+  /* First offset in the caches DATA buffer that belongs to this level.
    */
-  unsigned char *data;
+  apr_uint64_t start_offset;
 
-  /* Size of data buffer in bytes. Must be > 0.
+  /* Size of data buffer allocated to this level in bytes. Must be > 0.
    */
-  apr_uint64_t data_size;
+  apr_uint64_t size;
 
   /* Offset in the data buffer where the next insertion shall occur.
    */
   apr_uint64_t current_data;
 
+} cache_level_t;
+
+/* The cache header structure.
+ */
+struct svn_membuffer_t
+{
+  /* Number of cache segments. Must be a power of 2.
+     Please note that this structure represents only one such segment
+     and that all segments must / will report the same values here. */
+  apr_uint32_t segment_count;
+
+  /* The dictionary, GROUP_SIZE * group_count entries long. Never NULL.
+   */
+  entry_group_t *directory;
+
+  /* Flag array with group_count / GROUP_INIT_GRANULARITY _bit_ elements.
+   * Allows for efficiently marking groups as "not initialized".
+   */
+  unsigned char *group_initialized;
+
+  /* Size of dictionary in groups. Must be > 0.
+   */
+  apr_uint32_t group_count;
+
+  /* Pointer to the data buffer, data_size bytes long. Never NULL.
+   */
+  unsigned char *data;
+
   /* Total number of data buffer bytes in use. This is for statistics only.
    */
   apr_uint64_t data_used;
@@ -431,6 +453,24 @@ struct svn_membuffer_t
    */
   apr_uint64_t max_entry_size;
 
+  /* The cache levels, organized as sub-buffers.  Since entries in the
+   * DIRECTORY use offsets in DATA for addressing, a cache lookup does
+   * not need to know the cache level of a specific item.  Cache levels
+   * are only used to implement a hybrid insertion / eviction strategy.
+   */
+
+  /* First cache level, i.e. most insertions happen here.  Very large
+   * items might get inserted directly into L2.  L1 is a strict FIFO
+   * ring buffer that does not care about item priorities.  All evicted
+   * items get a chance to be promoted to L2.
+   */
+  cache_level_t l1;
+
+  /* Second cache level, i.e. data evicted from L1 will be added here
+   * if the item is "important" enough or the L2 insertion window is large
+   * enough.
+   */
+  cache_level_t l2;
 
   /* Number of used dictionary entries, i.e. number of cached items.
    * In conjunction with hit_count, this is used calculate the average
@@ -521,7 +561,7 @@ write_lock_cache(svn_membuffer_t *cache,
               status = APR_SUCCESS;
             }
         }
-    
+
       if (status)
         return svn_error_wrap_apr(status,
                                   _("Can't write-lock cache mutex"));
@@ -621,6 +661,96 @@ get_index(svn_membuffer_t *cache, entry_
        + (apr_uint32_t)(entry - cache->directory[group_index].entries);
 }
 
+/* Return the cache level of ENTRY in CACHE.
+ */
+static cache_level_t *
+get_cache_level(svn_membuffer_t *cache, entry_t *entry)
+{
+  return entry->offset < cache->l1.size ? &cache->l1
+                                        : &cache->l2;
+}
+
+/* Insert ENTRY to the chain of items that belong to LEVEL in CACHE.  IDX
+ * is ENTRY's item index and is only given for efficiency.  The insertion
+ * takes place just before LEVEL->NEXT.  *CACHE will not be modified.
+ */
+static void
+chain_entry(svn_membuffer_t *cache,
+            cache_level_t *level,
+            entry_t *entry,
+            apr_uint32_t idx)
+{
+  /* insert ENTRY before this item */
+  entry_t *next = level->next == NO_INDEX
+                ? NULL
+                : get_entry(cache, level->next);
+  assert(idx == get_index(cache, entry));
+
+  /* update entry chain
+   */
+  entry->next = level->next;
+  if (level->first == NO_INDEX)
+    {
+      /* insert as the first entry and only in the chain
+       */
+      entry->previous = NO_INDEX;
+      level->last = idx;
+      level->first = idx;
+    }
+  else if (next == NULL)
+    {
+      /* insert as the last entry in the chain.
+       * Note that it cannot also be at the beginning of the chain.
+       */
+      entry->previous = level->last;
+      get_entry(cache, level->last)->next = idx;
+      level->last = idx;
+    }
+  else
+    {
+      /* insert either at the start of a non-empty list or
+       * somewhere in the middle
+       */
+      entry->previous = next->previous;
+      next->previous = idx;
+
+      if (entry->previous != NO_INDEX)
+        get_entry(cache, entry->previous)->next = idx;
+      else
+        level->first = idx;
+    }
+}
+
+/* Remove ENTRY from the chain of items that belong to LEVEL in CACHE. IDX
+ * is ENTRY's item index and is only given for efficiency.  Please note
+ * that neither *CACHE nor *ENTRY will not be modified.
+ */
+static void
+unchain_entry(svn_membuffer_t *cache,
+              cache_level_t *level,
+              entry_t *entry,
+              apr_uint32_t idx)
+{
+  assert(idx == get_index(cache, entry));
+
+  /* update 
+   */
+  if (level->next == idx)
+    level->next = entry->next;
+  
+  /* unlink it from the chain of used entries
+   */
+  if (entry->previous == NO_INDEX)
+    level->first = entry->next;
+  else
+    get_entry(cache, entry->previous)->next = entry->next;
+
+  if (entry->next == NO_INDEX)
+    level->last = entry->previous;
+  else
+    get_entry(cache, entry->next)->previous = entry->previous;
+}
+
 /* Remove the used ENTRY from the CACHE, i.e. make it "unused".
  * In contrast to insertion, removal is possible for any entry.
  */
@@ -633,6 +763,7 @@ drop_entry(svn_membuffer_t *cache, entry
   apr_uint32_t group_index = idx / GROUP_SIZE;
   entry_group_t *group = &cache->directory[group_index];
   apr_uint32_t last_in_group = group_index * GROUP_SIZE + group->used - 1;
+  cache_level_t *level = get_cache_level(cache, entry);
 
   /* Only valid to be called for used entries.
    */
@@ -646,39 +777,31 @@ drop_entry(svn_membuffer_t *cache, entry
 
   /* extend the insertion window, if the entry happens to border it
    */
-  if (idx == cache->next)
-    cache->next = entry->next;
+  if (idx == level->next)
+    level->next = entry->next;
   else
-    if (entry->next == cache->next)
+    if (entry->next == level->next)
       {
         /* insertion window starts right behind the entry to remove
          */
         if (entry->previous == NO_INDEX)
           {
             /* remove the first entry -> insertion may start at pos 0, now */
-            cache->current_data = 0;
+            level->current_data = level->start_offset;
           }
         else
           {
             /* insertion may start right behind the previous entry */
             entry_t *previous = get_entry(cache, entry->previous);
-            cache->current_data = ALIGN_VALUE(  previous->offset
+            level->current_data = ALIGN_VALUE(  previous->offset
                                               + previous->size);
           }
       }
 
   /* unlink it from the chain of used entries
    */
-  if (entry->previous == NO_INDEX)
-    cache->first = entry->next;
-  else
-    get_entry(cache, entry->previous)->next = entry->next;
-
-  if (entry->next == NO_INDEX)
-    cache->last = entry->previous;
-  else
-    get_entry(cache, entry->next)->previous = entry->previous;
-
+  unchain_entry(cache, level, entry, idx);
+  
   /* Move last entry into hole (if the removed one is not the last used).
    * We need to do this since all used entries are at the beginning of
    * the group's entries array.
@@ -689,18 +812,22 @@ drop_entry(svn_membuffer_t *cache, entry
        */
       *entry = group->entries[group->used-1];
 
+      /* this ENTRY may belong to a different cache level than the entry
+       * we have just removed */
+      level = get_cache_level(cache, entry);
+
       /* update foreign links to new index
        */
-      if (last_in_group == cache->next)
-        cache->next = idx;
+      if (last_in_group == level->next)
+        level->next = idx;
 
       if (entry->previous == NO_INDEX)
-        cache->first = idx;
+        level->first = idx;
       else
         get_entry(cache, entry->previous)->next = idx;
-      
+
       if (entry->next == NO_INDEX)
-        cache->last = idx;
+        level->last = idx;
       else
         get_entry(cache, entry->next)->previous = idx;
     }
@@ -722,16 +849,14 @@ insert_entry(svn_membuffer_t *cache, ent
   apr_uint32_t idx = get_index(cache, entry);
   apr_uint32_t group_index = idx / GROUP_SIZE;
   entry_group_t *group = &cache->directory[group_index];
-  entry_t *next = cache->next == NO_INDEX
-                ? NULL
-                : get_entry(cache, cache->next);
+  cache_level_t *level = get_cache_level(cache, entry);
 
   /* The entry must start at the beginning of the insertion window.
    * It must also be the first unused entry in the group.
    */
-  assert(entry->offset == cache->current_data);
+  assert(entry->offset == level->current_data);
   assert(idx == group_index * GROUP_SIZE + group->used);
-  cache->current_data = ALIGN_VALUE(entry->offset + entry->size);
+  level->current_data = ALIGN_VALUE(entry->offset + entry->size);
 
   /* update usage counters
    */
@@ -742,42 +867,12 @@ insert_entry(svn_membuffer_t *cache, ent
 
   /* update entry chain
    */
-  entry->next = cache->next;
-  if (cache->first == NO_INDEX)
-    {
-      /* insert as the first entry and only in the chain
-       */
-      entry->previous = NO_INDEX;
-      cache->last = idx;
-      cache->first = idx;
-    }
-  else if (next == NULL)
-    {
-      /* insert as the last entry in the chain.
-       * Note that it cannot also be at the beginning of the chain.
-       */
-      entry->previous = cache->last;
-      get_entry(cache, cache->last)->next = idx;
-      cache->last = idx;
-    }
-  else
-    {
-      /* insert either at the start of a non-empty list or
-       * somewhere in the middle
-       */
-      entry->previous = next->previous;
-      next->previous = idx;
-
-      if (entry->previous != NO_INDEX)
-        get_entry(cache, entry->previous)->next = idx;
-      else
-        cache->first = idx;
-    }
+  chain_entry(cache, level, entry, idx);
 
   /* The current insertion position must never point outside our
    * data buffer.
    */
-  assert(cache->current_data <= cache->data_size);
+  assert(level->current_data <= level->start_offset + level->size);
 }
 
 /* Map a KEY of 16 bytes to the CACHE and group that shall contain the
@@ -788,10 +883,13 @@ get_group_index(svn_membuffer_t **cache,
                 entry_key_t key)
 {
   svn_membuffer_t *segment0 = *cache;
-  
-  /* select the cache segment to use. they have all the same group_count */
-  *cache = &segment0[key[0] & (segment0->segment_count -1)];
-  return key[1] % segment0->group_count;
+
+  /* select the cache segment to use. they have all the same group_count.
+   * Since key may not be well-distributed, pre-fold it to a smaller but
+   * "denser" ranger.  The divisors are primes larger than the largest
+   * counts. */
+  *cache = &segment0[(key[1] % 2809637ull) & (segment0->segment_count - 1)];
+  return (key[0] % 5030895599ull) % segment0->group_count;
 }
 
 /* Reduce the hit count of ENTRY and update the accumulated hit info
@@ -912,6 +1010,8 @@ find_entry(svn_membuffer_t *cache,
        */
       if (group->used == GROUP_SIZE)
         {
+          static int count = 0;
+          
           /* every entry gets the same chance of being removed.
            * Otherwise, we free the first entry, fill it and
            * remove it again on the next occasion without considering
@@ -931,6 +1031,7 @@ find_entry(svn_membuffer_t *cache,
               let_entry_age(cache, entry);
 
           drop_entry(cache, entry);
+          printf("%d\n", ++count);
         }
 
       /* initialize entry for the new key
@@ -950,6 +1051,7 @@ static void
 move_entry(svn_membuffer_t *cache, entry_t *entry)
 {
   apr_size_t size = ALIGN_VALUE(entry->size);
+  cache_level_t *level = get_cache_level(cache, entry);
 
   /* This entry survived this cleansing run. Reset half of its
    * hit count so that its removal gets more likely in the next
@@ -963,41 +1065,75 @@ move_entry(svn_membuffer_t *cache, entry
    * Size-aligned moves tend to be faster than non-aligned ones
    * because no "odd" bytes at the end need to special treatment.
    */
-  if (entry->offset != cache->current_data)
+  if (entry->offset != level->current_data)
     {
-      memmove(cache->data + cache->current_data,
+      memmove(cache->data + level->current_data,
               cache->data + entry->offset,
               size);
-      entry->offset = cache->current_data;
+      entry->offset = level->current_data;
     }
 
   /* The insertion position is now directly behind this entry.
    */
-  cache->current_data = entry->offset + size;
-  cache->next = entry->next;
+  level->current_data = entry->offset + size;
+  level->next = entry->next;
 
   /* The current insertion position must never point outside our
    * data buffer.
    */
-  assert(cache->current_data <= cache->data_size);
+  assert(level->current_data <= level->start_offset + level->size);
 }
 
-/* If necessary, enlarge the insertion window until it is at least
- * SIZE bytes long. SIZE must not exceed the data buffer size.
- * Return TRUE if enough room could be found or made. A FALSE result
+/* Move ENTRY in CACHE from L1 to L2.
+ */
+static void
+promote_entry(svn_membuffer_t *cache, entry_t *entry)
+{
+  apr_uint32_t idx = get_index(cache, entry);
+  apr_size_t size = ALIGN_VALUE(entry->size);
+  assert(get_cache_level(cache, entry) == &cache->l1);
+
+  /* copy item from the current location in L1 to the start of L2's
+   * insertion window */
+  memmove(cache->data + cache->l2.current_data,
+          cache->data + entry->offset,
+          size);
+  entry->offset = cache->l2.current_data;
+
+  /* The insertion position is now directly behind this entry.
+   */
+  cache->l2.current_data += size;
+
+  /* remove ENTRY from chain of L1 entries and put it into L2
+   */
+  unchain_entry(cache, &cache->l1, entry, idx);
+  chain_entry(cache, &cache->l2, entry, idx);
+}
+
+/* This function implements the cache insertion / eviction strategy for L2.
+ * 
+ * If necessary, enlarge the insertion window of CACHE->L2 until it is at
+ * least TO_FIT_IN->SIZE bytes long. TO_FIT_IN->SIZE must not exceed the
+ * data buffer size allocated to CACHE->L2.  IDX is the item index of
+ * TO_FIT_IN and is given for performance reasons.
+ * 
+ * Return TRUE if enough room could be found or made.  A FALSE result
  * indicates that the respective item shall not be added.
  */
 static svn_boolean_t
-ensure_data_insertable(svn_membuffer_t *cache, apr_size_t size)
+ensure_data_insertable_l2(svn_membuffer_t *cache,
+                          entry_t *to_fit_in,
+                          apr_uint32_t idx)
 {
   entry_t *entry;
   apr_uint64_t average_hit_value;
   apr_uint64_t threshold;
 
-  /* accumulated size of the entries that have been removed to make
-   * room for the new one.
-   */
-  apr_size_t drop_size = 0;
+  /* accumulated "worth" of items dropped so far */
+  apr_size_t drop_hits = 0;
+
+  /* verify parameters */
+  assert(idx == get_index(cache, to_fit_in));
 
   /* This loop will eventually terminate because every cache entry
    * would get dropped eventually:
@@ -1015,41 +1151,37 @@ ensure_data_insertable(svn_membuffer_t *
     {
       /* first offset behind the insertion window
        */
-      apr_uint64_t end = cache->next == NO_INDEX
-                       ? cache->data_size
-                       : get_entry(cache, cache->next)->offset;
+      apr_uint64_t end = cache->l2.next == NO_INDEX
+                       ? cache->l2.start_offset + cache->l2.size
+                       : get_entry(cache, cache->l2.next)->offset;
 
       /* leave function as soon as the insertion window is large enough
        */
-      if (end >= size + cache->current_data)
+      if (end >= to_fit_in->size + cache->l2.current_data)
         return TRUE;
 
-      /* Don't be too eager to cache data. Smaller items will fit into
-       * the cache after dropping a single item. Of the larger ones, we
-       * will only accept about 50%. They are also likely to get evicted
-       * soon due to their notoriously low hit counts.
-       *
-       * As long as enough similarly or even larger sized entries already
-       * exist in the cache, much less insert requests will be rejected.
+      /* if the net worth (in hits) of items removed is already larger
+       * than what we want to insert, reject TO_FIT_IN because it still
+       * does not fit in.
        */
-      if (2 * drop_size > size)
+      if (drop_hits > to_fit_in->hit_count)
         return FALSE;
 
       /* try to enlarge the insertion window
        */
-      if (cache->next == NO_INDEX)
+      if (cache->l2.next == NO_INDEX)
         {
           /* We reached the end of the data buffer; restart at the beginning.
            * Due to the randomized nature of our LFU implementation, very
            * large data items may require multiple passes. Therefore, SIZE
            * should be restricted to significantly less than data_size.
            */
-          cache->current_data = 0;
-          cache->next = cache->first;
+          cache->l2.current_data = cache->l2.start_offset;
+          cache->l2.next = cache->l2.first;
         }
       else
         {
-          entry = get_entry(cache, cache->next);
+          entry = get_entry(cache, cache->l2.next);
 
           /* Keep entries that are very small. Those are likely to be data
            * headers or similar management structures. So, they are probably
@@ -1061,14 +1193,24 @@ ensure_data_insertable(svn_membuffer_t *
             {
               move_entry(cache, entry);
             }
+          else if (cache->l2.next / GROUP_SIZE == idx / GROUP_SIZE)
+            {
+              /* Special case: we cannot drop entries that are in the same
+              * group as TO_FIT_IN because that might the latter to become
+              * invalidated it it happens to be the highest used entry in
+              * the group.  So, we must keep ENTRY unconditionally.
+              * (this is a very rare condition)
+              */
+              move_entry(cache, entry);
+            }
           else
             {
               svn_boolean_t keep;
 
               if (cache->hit_count > cache->used_entries)
                 {
-                  /* Roll the dice and determine a threshold somewhere from 0 up
-                   * to 2 times the average hit count.
+                  /* Roll the dice and determine a threshold somewhere from
+                   * 0 up to 2 times the average hit count.
                    */
                   average_hit_value = cache->hit_count / cache->used_entries;
                   threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
@@ -1077,9 +1219,9 @@ ensure_data_insertable(svn_membuffer_t *
                 }
               else
                 {
-                  /* general hit count is low. Keep everything that got hit
-                   * at all and assign some 50% survival chance to everything
-                   * else.
+                  /* general hit count is low. Keep everything that got
+                   * hit at all and assign some 50% survival chance to
+                   * everything else.
                    */
                   keep = (entry->hit_count > 0) || (rand() & 1);
                 }
@@ -1087,15 +1229,16 @@ ensure_data_insertable(svn_membuffer_t *
               /* keepers or destroyers? */
               if (keep)
                 {
+                 /* Keep ENTRY and move the insertion window.
+                  */
                   move_entry(cache, entry);
                 }
               else
                 {
-                 /* Drop the entry from the end of the insertion window, if it
-                  * has been hit less than the threshold. Otherwise, keep it and
-                  * move the insertion window one entry further.
+                 /* Drop the entry from the end of the insertion window,
+                  * because it had been hit less than the threshold.
                   */
-                  drop_size += entry->size;
+                  drop_hits += entry->hit_count;
                   drop_entry(cache, entry);
                 }
             }
@@ -1106,6 +1249,70 @@ ensure_data_insertable(svn_membuffer_t *
    * right answer. */
 }
 
+/* This function implements the cache insertion / eviction strategy for L1.
+ *
+ * If necessary, enlarge the insertion window of CACHE->L1 by promoting
+ * entries to L2 until it is at least SIZE bytes long.
+ *
+ * Return TRUE if enough room could be found or made.  A FALSE result
+ * indicates that the respective item shall not be added because it is
+ * too large.
+ */
+static svn_boolean_t
+ensure_data_insertable_l1(svn_membuffer_t *cache, apr_size_t size)
+{
+  entry_t *entry;
+
+  /* Guarantees that the while loop will terminate. */
+  if (size > cache->l1.size)
+    return FALSE;
+
+  /* This loop will eventually terminate because every cache entry
+   * would get dropped eventually.
+   */
+  while (1)
+    {
+      /* first offset behind the insertion window
+       */
+      apr_uint64_t end = cache->l1.next == NO_INDEX
+                       ? cache->l1.start_offset + cache->l1.size
+                       : get_entry(cache, cache->l1.next)->offset;
+
+      /* leave function as soon as the insertion window is large enough
+       */
+      if (end >= size + cache->l1.current_data)
+        return TRUE;
+
+      /* Enlarge the insertion window
+       */
+      if (cache->l1.next == NO_INDEX)
+        {
+          /* We reached the end of the data buffer; restart at the beginning.
+           * Due to the randomized nature of our LFU implementation, very
+           * large data items may require multiple passes. Therefore, SIZE
+           * should be restricted to significantly less than data_size.
+           */
+          cache->l1.current_data = cache->l1.start_offset;
+          cache->l1.next = cache->l1.first;
+        }
+      else
+        {
+          /* Remove the entry from the end of insertion window and promote
+           * it to L2, if it is important enough.
+           */
+          entry = get_entry(cache, cache->l1.next);
+
+          if (ensure_data_insertable_l2(cache, entry, cache->l1.next))
+            promote_entry(cache, entry);
+          else
+            drop_entry(cache, entry);
+        }
+    }
+
+  /* This will never be reached. But if it was, "can't insert" was the
+   * right answer. */
+}
+
 /* Mimic apr_pcalloc in APR_POOL_DEBUG mode, i.e. handle failed allocations
  * (e.g. OOM) properly: Allocate at least SIZE bytes from POOL and zero
  * the content of the allocated memory if ZERO has been set. Return NULL
@@ -1158,7 +1365,7 @@ svn_cache__membuffer_cache_create(svn_me
     segment_count = MAX_SEGMENT_COUNT;
   if (segment_count * MIN_SEGMENT_SIZE > total_size)
     segment_count = total_size / MIN_SEGMENT_SIZE;
-    
+
   /* The segment count must be a power of two. Round it down as necessary.
    */
   while ((segment_count & (segment_count-1)) != 0)
@@ -1225,13 +1432,13 @@ svn_cache__membuffer_cache_create(svn_me
    */
   data_size = ALIGN_VALUE(total_size - directory_size + 1) - ITEM_ALIGNMENT;
 
-  /* For cache sizes > 4TB, individual cache segments will be larger
-   * than 16GB allowing for >4GB entries.  But caching chunks larger
-   * than 4GB is simply not supported.
+  /* For cache sizes > 16TB, individual cache segments will be larger
+   * than 32GB allowing for >4GB entries.  But caching chunks larger
+   * than 4GB are simply not supported.
    */
-  max_entry_size = data_size / 4 > MAX_ITEM_SIZE
+  max_entry_size = data_size / 8 > MAX_ITEM_SIZE
                  ? MAX_ITEM_SIZE
-                 : data_size / 4;
+                 : data_size / 8;
 
   /* to keep the entries small, we use 32 bit indexes only
    * -> we need to ensure that no more then 4G entries exist.
@@ -1259,13 +1466,25 @@ svn_cache__membuffer_cache_create(svn_me
          hence "unused" */
       c[seg].group_initialized = apr_pcalloc(pool, group_init_size);
 
-      c[seg].first = NO_INDEX;
-      c[seg].last = NO_INDEX;
-      c[seg].next = NO_INDEX;
+      /* Allocate 1/4th of the data buffer to L1
+       */
+      c[seg].l1.first = NO_INDEX;
+      c[seg].l1.last = NO_INDEX;
+      c[seg].l1.next = NO_INDEX;
+      c[seg].l1.start_offset = 0;
+      c[seg].l1.size = ALIGN_VALUE(data_size / 4);
+      c[seg].l1.current_data = 0;
+
+      /* The remaining 3/4th will be used as L2
+       */
+      c[seg].l2.first = NO_INDEX;
+      c[seg].l2.last = NO_INDEX;
+      c[seg].l2.next = NO_INDEX;
+      c[seg].l2.start_offset = c[seg].l1.size;
+      c[seg].l2.size = data_size - c[seg].l1.size;
+      c[seg].l2.current_data = c[seg].l2.start_offset;
 
-      c[seg].data_size = data_size;
       c[seg].data = secure_aligned_alloc(pool, (apr_size_t)data_size, FALSE);
-      c[seg].current_data = 0;
       c[seg].data_used = 0;
       c[seg].max_entry_size = max_entry_size;
 
@@ -1282,7 +1501,7 @@ svn_cache__membuffer_cache_create(svn_me
         {
           /* We are OOM. There is no need to proceed with "half a cache".
            */
-          return svn_error_wrap_apr(APR_ENOMEM, _("OOM"));
+          return svn_error_wrap_apr(APR_ENOMEM, "OOM");
         }
 
 #if APR_HAS_THREADS
@@ -1397,7 +1616,7 @@ membuffer_cache_set_internal(svn_membuff
    */
   if (   buffer != NULL
       && cache->max_entry_size >= size
-      && ensure_data_insertable(cache, size))
+      && ensure_data_insertable_l1(cache, size))
     {
       /* Remove old data for this key, if that exists.
        * Get an unused entry for the key and and initialize it with
@@ -1405,7 +1624,7 @@ membuffer_cache_set_internal(svn_membuff
        */
       entry = find_entry(cache, group_index, to_find, TRUE);
       entry->size = size;
-      entry->offset = cache->current_data;
+      entry->offset = cache->l1.current_data;
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
@@ -1758,13 +1977,13 @@ membuffer_cache_set_partial_internal(svn
                */
               drop_entry(cache, entry);
               if (   (cache->max_entry_size >= size)
-                  && ensure_data_insertable(cache, size))
+                  && ensure_data_insertable_l1(cache, size))
                 {
                   /* Write the new entry.
                    */
                   entry = find_entry(cache, group_index, to_find, TRUE);
                   entry->size = size;
-                  entry->offset = cache->current_data;
+                  entry->offset = cache->l1.current_data;
                   if (size)
                     memcpy(cache->data + entry->offset, data, size);
 
@@ -1829,6 +2048,22 @@ membuffer_cache_set_partial(svn_membuffe
  * svn_cache__t instance.
  */
 
+/* Stores the combined key value for the given key.  It will be used by
+ * combine_key() to short-circuit expensive hash calculations.
+ */
+typedef struct last_access_key_t
+{
+  /* result of key combining */
+  entry_key_t combined_key;
+
+  /* length of the key (or APR_HASH_KEY_STRING if not used) */
+  apr_size_t key_len;
+
+  /* the original key.  Only KEY_LEN bytes are valid.  We use uint32 for
+   * better compatibility with pseudo-md5 functions. */
+  apr_uint32_t key[64];
+} last_access_key_t;
+
 /* Internal cache structure (used in svn_cache__t.cache_internal) basically
  * holding the additional parameters needed to call the respective membuffer
  * functions.
@@ -1876,6 +2111,11 @@ typedef struct svn_membuffer_cache_t
    */
   int alloc_counter;
 
+  /* cache for the last key used.
+   * Will be NULL for caches with short fix-sized keys.
+   */
+  last_access_key_t *last_access;
+  
   /* if enabled, this will serialize the access to this instance.
    */
   svn_mutex__t *mutex;
@@ -1893,46 +2133,127 @@ typedef struct svn_membuffer_cache_t
  */
 #define ALLOCATIONS_PER_POOL_CLEAR 10
 
-
 /* Basically calculate a hash value for KEY of length KEY_LEN, combine it
  * with the CACHE->PREFIX and write the result in CACHE->COMBINED_KEY.
+ * This could replace combine_key() entirely but we actually use it only
+ * when the quick path failed.
  */
 static void
-combine_key(svn_membuffer_cache_t *cache,
-            const void *key,
-            apr_ssize_t key_len)
+combine_long_key(svn_membuffer_cache_t *cache,
+                 const void *key,
+                 apr_ssize_t key_len)
 {
+  assert(cache->last_access);
+
+  /* handle variable-length keys */
   if (key_len == APR_HASH_KEY_STRING)
     key_len = strlen((const char *) key);
 
-  if (key_len < 16)
+  /* same key as the last time? -> short-circuit */
+  if (   key_len == cache->last_access->key_len
+      && memcmp(key, cache->last_access->key, key_len) == 0)
     {
-      apr_uint32_t data[4] = { 0 };
-      memcpy(data, key, key_len);
+      memcpy(cache->combined_key, cache->last_access->combined_key,
+             sizeof(cache->combined_key));
+    }
+  else if (key_len >= 64)
+    {
+      /* relatively long key.  Use the generic, slow hash code for it */
+      apr_md5((unsigned char*)cache->combined_key, key, key_len);
+      cache->combined_key[0] ^= cache->prefix[0];
+      cache->combined_key[1] ^= cache->prefix[1];
 
-      svn__pseudo_md5_15((apr_uint32_t *)cache->combined_key, data);
+      /* is the key short enough to cache the result? */
+      if (key_len <= sizeof(cache->last_access->key))
+        {
+          memcpy(cache->last_access->combined_key, cache->combined_key,
+                 sizeof(cache->combined_key));
+          cache->last_access->key_len = key_len;
+          memcpy(cache->last_access->key, key, key_len);
+        }
     }
-  else if (key_len < 32)
+  else
     {
-      apr_uint32_t data[8] = { 0 };
-      memcpy(data, key, key_len);
+      /* shorter keys use efficient hash code and *do* cache the results */
+      cache->last_access->key_len = key_len;
+      if (key_len < 16)
+        {
+          memset(cache->last_access->key, 0, 16);
+          memcpy(cache->last_access->key, key, key_len);
 
-      svn__pseudo_md5_31((apr_uint32_t *)cache->combined_key, data);
+          svn__pseudo_md5_15((apr_uint32_t *)cache->combined_key,
+                             cache->last_access->key);
+        }
+      else if (key_len < 32)
+        {
+          memset(cache->last_access->key, 0, 32);
+          memcpy(cache->last_access->key, key, key_len);
+
+          svn__pseudo_md5_31((apr_uint32_t *)cache->combined_key,
+                             cache->last_access->key);
+        }
+      else
+        {
+          memset(cache->last_access->key, 0, 64);
+          memcpy(cache->last_access->key, key, key_len);
+
+          svn__pseudo_md5_63((apr_uint32_t *)cache->combined_key,
+                             cache->last_access->key);
+        }
+
+      cache->combined_key[0] ^= cache->prefix[0];
+      cache->combined_key[1] ^= cache->prefix[1];
+
+      memcpy(cache->last_access->combined_key, cache->combined_key,
+             sizeof(cache->combined_key));
     }
-  else if (key_len < 64)
+}
+
+/* Basically calculate a hash value for KEY of length KEY_LEN, combine it
+ * with the CACHE->PREFIX and write the result in CACHE->COMBINED_KEY.
+ */
+static void
+combine_key(svn_membuffer_cache_t *cache,
+            const void *key,
+            apr_ssize_t key_len)
+{
+  /* copy of *key, padded with 0 */
+  apr_uint64_t data[2];
+
+  /* short, fixed-size keys are the most common case */
+  if (key_len == 16)
+    {
+      data[0] = ((const apr_uint64_t *)key)[0];
+      data[1] = ((const apr_uint64_t *)key)[1];
+    }
+  else if (key_len == 8)
+    {
+      data[0] = ((const apr_uint64_t *)key)[0];
+      data[1] = 0;
+    }
+  else if (key_len != APR_HASH_KEY_STRING && key_len < 16)
     {
-      apr_uint32_t data[16] = { 0 };
+      data[0] = 0;
+      data[1] = 0;
       memcpy(data, key, key_len);
-
-      svn__pseudo_md5_63((apr_uint32_t *)cache->combined_key, data);
     }
   else
     {
-      apr_md5((unsigned char*)cache->combined_key, key, key_len);
+      /* longer or variably sized keys */
+      combine_long_key(cache, key, key_len);
+      return;
     }
 
-  cache->combined_key[0] ^= cache->prefix[0];
-  cache->combined_key[1] ^= cache->prefix[1];
+  /* scramble key DATA.  All of this must be reversible to prevent key
+   * collisions.  So, we limit ourselves to xor and permutations. */
+  data[1] = (data[1] << 27) | (data[1] >> 37);
+  data[1] ^= data[0] & 0xffff;
+  data[0] ^= data[1] & 0xffffffffffff0000ull;
+  data[0] = (data[0] << 43) | (data[0] >> 21);
+
+  /* combine with this cache's namespace */
+  cache->combined_key[0] = data[0] ^ cache->prefix[0];
+  cache->combined_key[1] = data[1] ^ cache->prefix[1];
 }
 
 /* Implement svn_cache__vtable_t.get (not thread-safe)
@@ -2112,9 +2433,9 @@ static svn_error_t *
 svn_membuffer_get_segment_info(svn_membuffer_t *segment,
                                svn_cache__info_t *info)
 {
-  info->data_size += segment->data_size;
+  info->data_size += segment->l1.size + segment->l2.size;
   info->used_size += segment->data_used;
-  info->total_size += segment->data_size +
+  info->total_size += segment->l1.size + segment->l2.size +
       segment->group_count * GROUP_SIZE * sizeof(entry_t);
 
   info->used_entries += segment->used_entries;
@@ -2347,6 +2668,18 @@ svn_cache__create_membuffer_cache(svn_ca
                        pool));
   memcpy(cache->prefix, checksum->digest, sizeof(cache->prefix));
 
+  /* fix-length keys of 16 bytes or under don't need a buffer because we
+   * can use a very fast key combining algorithm. */
+  if ((klen == APR_HASH_KEY_STRING) ||  klen > sizeof(entry_key_t))
+    {
+      cache->last_access = apr_pcalloc(pool, sizeof(*cache->last_access));
+      cache->last_access->key_len = APR_HASH_KEY_STRING;
+    }
+  else
+    {
+      cache->last_access = NULL;
+    }
+
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
   /* Initialize cache debugging support.
@@ -2362,6 +2695,7 @@ svn_cache__create_membuffer_cache(svn_ca
   wrapper->cache_internal = cache;
   wrapper->error_handler = 0;
   wrapper->error_baton = 0;
+  wrapper->pretend_empty = !!getenv("SVN_X_DOES_NOT_MARK_THE_SPOT");
 
   *cache_p = wrapper;
   return SVN_NO_ERROR;

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/cache-memcache.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/cache-memcache.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/cache-memcache.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/cache-memcache.c Fri May 10 14:58:47 2013
@@ -407,6 +407,7 @@ svn_cache__create_memcache(svn_cache__t 
   wrapper->cache_internal = cache;
   wrapper->error_handler = 0;
   wrapper->error_baton = 0;
+  wrapper->pretend_empty = !!getenv("SVN_X_DOES_NOT_MARK_THE_SPOT");
 
   *cache_p = wrapper;
   return SVN_NO_ERROR;

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/cache.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/cache.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/cache.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/cache.c Fri May 10 14:58:47 2013
@@ -75,6 +75,10 @@ svn_cache__get(void **value_p,
   /* In case any errors happen and are quelched, make sure we start
      out with FOUND set to false. */
   *found = FALSE;
+#ifdef SVN_DEBUG
+  if (cache->pretend_empty)
+    return SVN_NO_ERROR;
+#endif
 
   cache->reads++;
   err = handle_error(cache,
@@ -114,6 +118,12 @@ svn_cache__iter(svn_boolean_t *completed
                 void *user_baton,
                 apr_pool_t *scratch_pool)
 {
+#ifdef SVN_DEBUG
+  if (cache->pretend_empty)
+    /* Pretend CACHE is empty. */
+    return SVN_NO_ERROR;
+#endif
+
   return (cache->vtable->iter)(completed,
                                cache->cache_internal,
                                user_cb,
@@ -135,6 +145,10 @@ svn_cache__get_partial(void **value,
   /* In case any errors happen and are quelched, make sure we start
   out with FOUND set to false. */
   *found = FALSE;
+#ifdef SVN_DEBUG
+  if (cache->pretend_empty)
+    return SVN_NO_ERROR;
+#endif
 
   cache->reads++;
   err = handle_error(cache,

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/cache.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/cache.h?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/cache.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/cache.h Fri May 10 14:58:47 2013
@@ -99,6 +99,10 @@ struct svn_cache__t {
 
   /* Total number of function calls that returned an error. */
   apr_uint64_t failures;
+
+  /* Cause all getters to act as though the cache contains no data.
+     (Currently this never becomes set except in maintainer builds.) */
+  svn_boolean_t pretend_empty;
 };
 
 

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/cmdline.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/cmdline.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/cmdline.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/cmdline.c Fri May 10 14:58:47 2013
@@ -46,6 +46,7 @@
 #include "svn_ctype.h"
 #include "svn_dso.h"
 #include "svn_dirent_uri.h"
+#include "svn_hash.h"
 #include "svn_path.h"
 #include "svn_pools.h"
 #include "svn_error.h"
@@ -147,7 +148,9 @@ svn_cmdline_init(const char *progname, F
       _set_error_mode(_OUT_TO_STDERR);
 
       /* In _DEBUG mode: Redirect all debug output (E.g. assert() to stderr.
-         (Ignored in releas builds) */
+         (Ignored in release builds) */
+      _CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDERR);
+      _CrtSetReportFile( _CRT_ERROR, _CRTDBG_FILE_STDERR);
       _CrtSetReportFile( _CRT_ASSERT, _CRTDBG_FILE_STDERR);
       _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
       _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
@@ -855,7 +858,7 @@ svn_cmdline__apply_config_options(apr_ha
                           APR_ARRAY_IDX(config_options, i,
                                         svn_cmdline__config_argument_t *);
 
-     cfg = apr_hash_get(config, arg->file, APR_HASH_KEY_STRING);
+     cfg = svn_hash_gets(config, arg->file);
 
      if (cfg)
        {
@@ -1051,7 +1054,7 @@ svn_cmdline__be_interactive(svn_boolean_
       return (isatty(STDIN_FILENO) != 0);
 #endif
     }
-  else if (force_interactive) 
+  else if (force_interactive)
     return TRUE;
 
   return !non_interactive;
@@ -1081,8 +1084,7 @@ find_editor_binary(const char **editor,
   /* If not found then fall back on the config file. */
   if (! e)
     {
-      cfg = config ? apr_hash_get(config, SVN_CONFIG_CATEGORY_CONFIG,
-                                  APR_HASH_KEY_STRING) : NULL;
+      cfg = config ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) : NULL;
       svn_config_get(cfg, &e, SVN_CONFIG_SECTION_HELPERS,
                      SVN_CONFIG_OPTION_EDITOR_CMD, NULL);
     }

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/compat.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/compat.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/compat.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/compat.c Fri May 10 14:58:47 2013
@@ -24,6 +24,7 @@
 #include <apr_pools.h>
 #include <apr_strings.h>
 
+#include "svn_hash.h"
 #include "svn_types.h"
 #include "svn_error.h"
 #include "svn_compat.h"
@@ -75,12 +76,9 @@ svn_compat_log_revprops_clear(apr_hash_t
 {
   if (revprops)
     {
-      apr_hash_set(revprops, SVN_PROP_REVISION_AUTHOR,
-                   APR_HASH_KEY_STRING, NULL);
-      apr_hash_set(revprops, SVN_PROP_REVISION_DATE,
-                   APR_HASH_KEY_STRING, NULL);
-      apr_hash_set(revprops, SVN_PROP_REVISION_LOG,
-                   APR_HASH_KEY_STRING, NULL);
+      svn_hash_sets(revprops, SVN_PROP_REVISION_AUTHOR, NULL);
+      svn_hash_sets(revprops, SVN_PROP_REVISION_DATE, NULL);
+      svn_hash_sets(revprops, SVN_PROP_REVISION_LOG, NULL);
     }
 }
 
@@ -105,14 +103,11 @@ svn_compat_log_revprops_out(const char *
   *author = *date = *message = NULL;
   if (revprops)
     {
-      if ((author_s = apr_hash_get(revprops, SVN_PROP_REVISION_AUTHOR,
-                                   APR_HASH_KEY_STRING)))
+      if ((author_s = svn_hash_gets(revprops, SVN_PROP_REVISION_AUTHOR)))
         *author = author_s->data;
-      if ((date_s = apr_hash_get(revprops, SVN_PROP_REVISION_DATE,
-                                 APR_HASH_KEY_STRING)))
+      if ((date_s = svn_hash_gets(revprops, SVN_PROP_REVISION_DATE)))
         *date = date_s->data;
-      if ((message_s = apr_hash_get(revprops, SVN_PROP_REVISION_LOG,
-                                    APR_HASH_KEY_STRING)))
+      if ((message_s = svn_hash_gets(revprops, SVN_PROP_REVISION_LOG)))
         *message = message_s->data;
     }
 }

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/config.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/config.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/config.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/config.c Fri May 10 14:58:47 2013
@@ -29,6 +29,7 @@
 
 #include <apr_general.h>
 #include <apr_lib.h>
+#include "svn_hash.h"
 #include "svn_error.h"
 #include "svn_pools.h"
 #include "config_impl.h"
@@ -46,9 +47,6 @@ struct cfg_section_t
   /* The section name. */
   const char *name;
 
-  /* The section name, converted into a hash key. */
-  const char *hash_key;
-
   /* Table of cfg_option_t's. */
   apr_hash_t *options;
 };
@@ -79,9 +77,10 @@ struct cfg_option_t
 
 
 svn_error_t *
-svn_config_create(svn_config_t **cfgp,
-                  svn_boolean_t section_names_case_sensitive,
-                  apr_pool_t *result_pool)
+svn_config_create2(svn_config_t **cfgp,
+                   svn_boolean_t section_names_case_sensitive,
+                   svn_boolean_t option_names_case_sensitive,
+                   apr_pool_t *result_pool)
 {
   svn_config_t *cfg = apr_palloc(result_pool, sizeof(*cfg));
 
@@ -92,21 +91,26 @@ svn_config_create(svn_config_t **cfgp,
   cfg->tmp_key = svn_stringbuf_create_empty(result_pool);
   cfg->tmp_value = svn_stringbuf_create_empty(result_pool);
   cfg->section_names_case_sensitive = section_names_case_sensitive;
+  cfg->option_names_case_sensitive = option_names_case_sensitive;
 
   *cfgp = cfg;
   return SVN_NO_ERROR;
 }
 
 svn_error_t *
-svn_config_read2(svn_config_t **cfgp, const char *file,
+svn_config_read3(svn_config_t **cfgp, const char *file,
                  svn_boolean_t must_exist,
                  svn_boolean_t section_names_case_sensitive,
-                 apr_pool_t *pool)
+                 svn_boolean_t option_names_case_sensitive,
+                 apr_pool_t *result_pool)
 {
   svn_config_t *cfg;
   svn_error_t *err;
 
-  SVN_ERR(svn_config_create(&cfg, section_names_case_sensitive, pool));
+  SVN_ERR(svn_config_create2(&cfg,
+                             section_names_case_sensitive,
+                             option_names_case_sensitive,
+                             result_pool));
 
   /* Yes, this is platform-specific code in Subversion, but there's no
      practical way to migrate it into APR, as it's simultaneously
@@ -116,10 +120,10 @@ svn_config_read2(svn_config_t **cfgp, co
 #ifdef WIN32
   if (0 == strncmp(file, SVN_REGISTRY_PREFIX, SVN_REGISTRY_PREFIX_LEN))
     err = svn_config__parse_registry(cfg, file + SVN_REGISTRY_PREFIX_LEN,
-                                     must_exist, pool);
+                                     must_exist, result_pool);
   else
 #endif /* WIN32 */
-    err = svn_config__parse_file(cfg, file, must_exist, pool);
+    err = svn_config__parse_file(cfg, file, must_exist, result_pool);
 
   if (err != SVN_NO_ERROR)
     return err;
@@ -132,13 +136,17 @@ svn_config_read2(svn_config_t **cfgp, co
 svn_error_t *
 svn_config_parse(svn_config_t **cfgp, svn_stream_t *stream,
                  svn_boolean_t section_names_case_sensitive,
+                 svn_boolean_t option_names_case_sensitive,
                  apr_pool_t *result_pool)
 {
   svn_config_t *cfg;
   svn_error_t *err;
   apr_pool_t *scratch_pool = svn_pool_create(result_pool);
 
-  err = svn_config_create(&cfg, section_names_case_sensitive, result_pool);
+  err = svn_config_create2(&cfg,
+                           section_names_case_sensitive,
+                           option_names_case_sensitive,
+                           result_pool);
 
   if (err == SVN_NO_ERROR)
     err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
@@ -180,7 +188,8 @@ read_all(svn_config_t **cfgp,
 #ifdef WIN32
   if (sys_registry_path)
     {
-      SVN_ERR(svn_config_read2(cfgp, sys_registry_path, FALSE, FALSE, pool));
+      SVN_ERR(svn_config_read3(cfgp, sys_registry_path, FALSE, FALSE, FALSE,
+                               pool));
       red_config = TRUE;
     }
 #endif /* WIN32 */
@@ -191,7 +200,8 @@ read_all(svn_config_t **cfgp,
         SVN_ERR(svn_config_merge(*cfgp, sys_file_path, FALSE));
       else
         {
-          SVN_ERR(svn_config_read2(cfgp, sys_file_path, FALSE, FALSE, pool));
+          SVN_ERR(svn_config_read3(cfgp, sys_file_path,
+                                   FALSE, FALSE, FALSE, pool));
           red_config = TRUE;
         }
     }
@@ -205,8 +215,8 @@ read_all(svn_config_t **cfgp,
         SVN_ERR(svn_config_merge(*cfgp, usr_registry_path, FALSE));
       else
         {
-          SVN_ERR(svn_config_read2(cfgp, usr_registry_path,
-                                   FALSE, FALSE, pool));
+          SVN_ERR(svn_config_read3(cfgp, usr_registry_path,
+                                   FALSE, FALSE, FALSE, pool));
           red_config = TRUE;
         }
     }
@@ -218,13 +228,14 @@ read_all(svn_config_t **cfgp,
         SVN_ERR(svn_config_merge(*cfgp, usr_file_path, FALSE));
       else
         {
-          SVN_ERR(svn_config_read2(cfgp, usr_file_path, FALSE, FALSE, pool));
+          SVN_ERR(svn_config_read3(cfgp, usr_file_path,
+                                   FALSE, FALSE, FALSE, pool));
           red_config = TRUE;
         }
     }
 
   if (! red_config)
-    SVN_ERR(svn_config_create(cfgp, FALSE, pool));
+    SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
 
   return SVN_NO_ERROR;
 }
@@ -354,7 +365,10 @@ svn_config_merge(svn_config_t *cfg, cons
      ### We could use a tmp subpool for this, since merge_cfg is going
      to be tossed afterwards.  Premature optimization, though? */
   svn_config_t *merge_cfg;
-  SVN_ERR(svn_config_read2(&merge_cfg, file, must_exist, FALSE, cfg->pool));
+  SVN_ERR(svn_config_read3(&merge_cfg, file, must_exist,
+                           cfg->section_names_case_sensitive,
+                           cfg->option_names_case_sensitive,
+                           cfg->pool));
 
   /* Now copy the new options into the original table. */
   for_each_option(merge_cfg, cfg, merge_cfg->pool, merge_callback);
@@ -429,7 +443,8 @@ find_option(svn_config_t *cfg, const cha
 
       /* Canonicalize the option key */
       svn_stringbuf_set(cfg->tmp_key, option);
-      make_hash_key(cfg->tmp_key->data);
+      if (! cfg->option_names_case_sensitive)
+        make_hash_key(cfg->tmp_key->data);
 
       opt = apr_hash_get(sec->options, cfg->tmp_key->data,
                          cfg->tmp_key->len);
@@ -463,19 +478,29 @@ make_string_from_option(const char **val
   /* Expand the option value if necessary. */
   if (!opt->expanded)
     {
-      apr_pool_t *tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
+      /* before attempting to expand an option, check for the placeholder.
+       * If none is there, there is no point in calling expand_option_value.
+       */
+      if (opt->value && strchr(opt->value, '%'))
+        {
+          apr_pool_t *tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
 
-      expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool);
-      opt->expanded = TRUE;
+          expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool);
+          opt->expanded = TRUE;
 
-      if (!x_pool)
+          if (!x_pool)
+            {
+              /* Grab the fully expanded value from tmp_pool before its
+                 disappearing act. */
+              if (opt->x_value)
+                opt->x_value = apr_pstrmemdup(cfg->x_pool, opt->x_value,
+                                              strlen(opt->x_value));
+              svn_pool_destroy(tmp_pool);
+            }
+        }
+      else
         {
-          /* Grab the fully expanded value from tmp_pool before its
-             disappearing act. */
-          if (opt->x_value)
-            opt->x_value = apr_pstrmemdup(cfg->x_pool, opt->x_value,
-                                          strlen(opt->x_value));
-          svn_pool_destroy(tmp_pool);
+          opt->expanded = TRUE;
         }
     }
 
@@ -575,41 +600,45 @@ expand_option_value(svn_config_t *cfg, c
     *opt_x_valuep = NULL;
 }
 
-static void
+static cfg_section_t *
 svn_config_addsection(svn_config_t *cfg,
-                      const char *section,
-                      cfg_section_t **sec)
-{  
+                      const char *section)
+{
   cfg_section_t *s;
+  const char *hash_key;
 
   s = apr_palloc(cfg->pool, sizeof(cfg_section_t));
   s->name = apr_pstrdup(cfg->pool, section);
   if(cfg->section_names_case_sensitive)
-    s->hash_key = s->name;
+    hash_key = s->name;
   else
-    s->hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
+    hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
   s->options = apr_hash_make(cfg->pool);
-  apr_hash_set(cfg->sections, s->hash_key, APR_HASH_KEY_STRING, s);
-  
-  *sec = s;
+  svn_hash_sets(cfg->sections, hash_key, s);
+
+  return s;
 }
 
 static void
 svn_config_create_option(cfg_option_t **opt,
                          const char *option,
                          const char *value,
+                         svn_boolean_t option_names_case_sensitive,
                          apr_pool_t *pool)
 {
   cfg_option_t *o;
 
   o = apr_palloc(pool, sizeof(cfg_option_t));
   o->name = apr_pstrdup(pool, option);
-  o->hash_key = make_hash_key(apr_pstrdup(pool, option));
+  if(option_names_case_sensitive)
+    o->hash_key = o->name;
+  else
+    o->hash_key = make_hash_key(apr_pstrdup(pool, option));
 
   o->value = apr_pstrdup(pool, value);
   o->x_value = NULL;
   o->expanded = FALSE;
-  
+
   *opt = o;
 }
 
@@ -669,15 +698,17 @@ svn_config_set(svn_config_t *cfg,
     }
 
   /* Create a new option */
-  svn_config_create_option(&opt, option, value, cfg->pool);
+  svn_config_create_option(&opt, option, value,
+                           cfg->option_names_case_sensitive,
+                           cfg->pool);
 
   if (sec == NULL)
     {
       /* Even the section doesn't exist. Create it. */
-      svn_config_addsection(cfg, section, &sec);
+      sec = svn_config_addsection(cfg, section);
     }
 
-  apr_hash_set(sec->options, opt->hash_key, APR_HASH_KEY_STRING, opt);
+  svn_hash_sets(sec->options, opt->hash_key, opt);
 }
 
 
@@ -1004,7 +1035,7 @@ const char *svn_config_find_group(svn_co
   gb.key = key;
   gb.match = NULL;
   gb.pool = pool;
-  svn_config_enumerate2(cfg, master_section, search_groups, &gb, pool);
+  (void) svn_config_enumerate2(cfg, master_section, search_groups, &gb, pool);
   return gb.match;
 }
 
@@ -1035,10 +1066,11 @@ svn_config_dup(svn_config_t **cfgp,
   apr_hash_index_t *optidx;
 
   *cfgp = 0;
-  SVN_ERR(svn_config_create(cfgp, FALSE, pool));
+  SVN_ERR(svn_config_create2(cfgp, FALSE, FALSE, pool));
 
   (*cfgp)->x_values = src->x_values;
   (*cfgp)->section_names_case_sensitive = src->section_names_case_sensitive;
+  (*cfgp)->option_names_case_sensitive = src->option_names_case_sensitive;
 
   for (sectidx = apr_hash_first(pool, src->sections);
        sectidx != NULL;
@@ -1053,7 +1085,7 @@ svn_config_dup(svn_config_t **cfgp,
     apr_hash_this(sectidx, &sectkey, &sectkeyLength, &sectval);
     srcsect = sectval;
 
-    svn_config_addsection(*cfgp, srcsect->name, &destsec);
+    destsec = svn_config_addsection(*cfgp, srcsect->name);
 
     for (optidx = apr_hash_first(pool, srcsect->options);
          optidx != NULL;
@@ -1068,7 +1100,9 @@ svn_config_dup(svn_config_t **cfgp,
       apr_hash_this(optidx, &optkey, &optkeyLength, &optval);
       srcopt = optval;
 
-      svn_config_create_option(&destopt, srcopt->name, srcopt->value, pool);
+      svn_config_create_option(&destopt, srcopt->name, srcopt->value,
+                               (*cfgp)->option_names_case_sensitive,
+                               pool);
 
       destopt->value = apr_pstrdup(pool, srcopt->value);
       destopt->x_value = apr_pstrdup(pool, srcopt->x_value);
@@ -1078,7 +1112,7 @@ svn_config_dup(svn_config_t **cfgp,
                    optkeyLength, destopt);
     }
   }
-  
+
   return SVN_NO_ERROR;
 }
 
@@ -1105,7 +1139,7 @@ svn_config_copy_config(apr_hash_t **cfg_
 
     SVN_ERR(svn_config_dup(&destconfig, srcconfig, pool));
 
-    apr_hash_set(*cfg_hash, 
+    apr_hash_set(*cfg_hash,
                  apr_pstrdup(pool, (const char*)ckey),
                  ckeyLength, destconfig);
   }
@@ -1170,6 +1204,6 @@ svn_config_has_section(svn_config_t *cfg
   if (! cfg->section_names_case_sensitive)
     make_hash_key(cfg->tmp_key->data);
 
-  sec = apr_hash_get(cfg->sections, cfg->tmp_key->data, APR_HASH_KEY_STRING);
+  sec = svn_hash_gets(cfg->sections, cfg->tmp_key->data);
   return sec != NULL;
 }

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/config_auth.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/config_auth.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/config_auth.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/config_auth.c Fri May 10 14:58:47 2013
@@ -26,13 +26,15 @@
 #include "svn_dirent_uri.h"
 #include "svn_hash.h"
 #include "svn_io.h"
-
+#include "svn_pools.h"
 #include "config_impl.h"
 
 #include "auth.h"
 
 #include "svn_private_config.h"
 
+#include "private/svn_auth_private.h"
+
 /* Helper for svn_config_{read|write}_auth_data.  Return a path to a
    file within ~/.subversion/auth/ that holds CRED_KIND credentials
    within REALMSTRING.  If no path is available *PATH will be set to
@@ -128,8 +130,8 @@ svn_config_write_auth_data(apr_hash_t *h
 
   /* Add the realmstring to the hash, so programs (or users) can
      verify exactly which set of credentials this file holds.  */
-  apr_hash_set(hash, SVN_CONFIG_REALMSTRING_KEY, APR_HASH_KEY_STRING,
-               svn_string_create(realmstring, pool));
+  svn_hash_sets(hash, SVN_CONFIG_REALMSTRING_KEY,
+                svn_string_create(realmstring, pool));
 
   SVN_ERR_W(svn_io_file_open(&authfile, auth_path,
                              (APR_WRITE | APR_CREATE | APR_TRUNCATE
@@ -146,7 +148,130 @@ svn_config_write_auth_data(apr_hash_t *h
 
   /* To be nice, remove the realmstring from the hash again, just in
      case the caller wants their hash unchanged. */
-  apr_hash_set(hash, SVN_CONFIG_REALMSTRING_KEY, APR_HASH_KEY_STRING, NULL);
+  svn_hash_sets(hash, SVN_CONFIG_REALMSTRING_KEY, NULL);
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_config_walk_auth_data(const char *config_dir,
+                          svn_config_auth_walk_func_t walk_func,
+                          void *walk_baton,
+                          apr_pool_t *scratch_pool)
+{
+  int i;
+  apr_pool_t *iterpool;
+  svn_boolean_t finished = FALSE;
+  const char *cred_kinds[] =
+    {
+      SVN_AUTH_CRED_SIMPLE,
+      SVN_AUTH_CRED_USERNAME,
+      SVN_AUTH_CRED_SSL_CLIENT_CERT,
+      SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
+      SVN_AUTH_CRED_SSL_SERVER_TRUST,
+      NULL
+    };
+
+  if (! config_dir)
+    {
+      /* Can't locate the cache to clear */
+      return SVN_NO_ERROR;
+    }
+
+  iterpool = svn_pool_create(scratch_pool);
+  for (i = 0; cred_kinds[i]; i++)
+    {
+      const char *item_path;
+      const char *dir_path;
+      apr_hash_t *nodes;
+      svn_error_t *err;
+      apr_pool_t *itempool;
+      apr_hash_index_t *hi;
+
+      svn_pool_clear(iterpool);
+
+      if (finished)
+        break;
+
+      SVN_ERR(svn_auth__file_path(&item_path, cred_kinds[i], "!", config_dir,
+                                  iterpool));
+
+      dir_path = svn_dirent_dirname(item_path, iterpool);
+
+      err = svn_io_get_dirents3(&nodes, dir_path, TRUE, iterpool, iterpool);
+      if (err)
+        {
+          if (!APR_STATUS_IS_ENOENT(err->apr_err)
+              && !SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))
+            return svn_error_trace(err);
+
+          svn_error_clear(err);
+          continue;
+        }
+
+      itempool = svn_pool_create(iterpool);
+      for (hi = apr_hash_first(iterpool, nodes); hi; hi = apr_hash_next(hi))
+        {
+          svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
+          svn_stream_t *stream;
+          apr_hash_t *creds_hash;
+          const svn_string_t *realm;
+          svn_boolean_t delete_file = FALSE;
+
+          if (finished)
+            break;
+
+          if (dirent->kind != svn_node_file)
+            continue;
+
+          svn_pool_clear(itempool);
+
+          item_path = svn_dirent_join(dir_path, svn__apr_hash_index_key(hi),
+                                      itempool);
+
+          err = svn_stream_open_readonly(&stream, item_path,
+                                         itempool, itempool);
+          if (err)
+            {
+              /* Ignore this file. There are no credentials in it anyway */
+              svn_error_clear(err);
+              continue;
+            }
+
+          creds_hash = apr_hash_make(itempool);
+          err = svn_hash_read2(creds_hash, stream,
+                               SVN_HASH_TERMINATOR, itempool);
+          err = svn_error_compose_create(err, svn_stream_close(stream));
+          if (err)
+            {
+              /* Ignore this file. There are no credentials in it anyway */
+              svn_error_clear(err);
+              continue;
+            }
+
+          realm = svn_hash_gets(creds_hash, SVN_CONFIG_REALMSTRING_KEY);
+          if (! realm)
+            continue; /* Not an auth file */
+
+          err = walk_func(&delete_file, walk_baton, cred_kinds[i],
+                          realm->data, creds_hash, itempool);
+          if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
+            {
+              svn_error_clear(err);
+              err = SVN_NO_ERROR;
+              finished = TRUE;
+            }
+          SVN_ERR(err);
+
+          if (delete_file)
+            {
+              /* Delete the file on disk */
+              SVN_ERR(svn_io_remove_file2(item_path, TRUE, itempool));
+            }
+        }
+    }
 
+  svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/config_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/config_file.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/config_file.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/config_file.c Fri May 10 14:58:47 2013
@@ -417,7 +417,7 @@ svn_config__parse_file(svn_config_t *cfg
 
   err = svn_config__parse_stream(cfg, stream, result_pool, scratch_pool);
 
-  if (err != SVN_NO_ERROR) 
+  if (err != SVN_NO_ERROR)
     {
       /* Add the filename to the error stack. */
       err = svn_error_createf(err->apr_err, err,
@@ -1155,6 +1155,16 @@ svn_config_ensure(const char *config_dir
         "### ra_local (the file:// scheme). The value represents the number" NL
         "### of MB used by the cache."                                       NL
         "# memory-cache-size = 16"                                           NL
+        "### Set diff-ignore-content-type to 'yes' to cause 'svn diff' to"   NL
+        "### attempt to show differences of all modified files regardless"   NL
+        "### of their MIME content type.  By default, Subversion will only"  NL
+        "### attempt to show differences for files believed to have human-"  NL
+        "### readable (non-binary) content.  This option is especially"      NL
+        "### useful when Subversion is configured (via the 'diff-cmd'"       NL
+        "### option) to employ an external differencing tool which is able"  NL
+        "### to show meaningful differences for binary file formats.  [New"  NL
+        "### in 1.9]"                                                        NL
+        "# diff-ignore-content-type = no"                                    NL
         ""                                                                   NL
         "### Section for configuring automatic properties."                  NL
         "[auto-props]"                                                       NL

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/config_impl.h
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/config_impl.h?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/config_impl.h (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/config_impl.h Fri May 10 14:58:47 2013
@@ -67,6 +67,9 @@ struct svn_config_t
 
   /* Specifies whether section names are populated case sensitively. */
   svn_boolean_t section_names_case_sensitive;
+
+  /* Specifies whether option names are populated case sensitively. */
+  svn_boolean_t option_names_case_sensitive;
 };
 
 

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/config_win.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/config_win.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/config_win.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/config_win.c Fri May 10 14:58:47 2013
@@ -215,18 +215,17 @@ svn_config__parse_registry(svn_config_t 
   for (index = 0; ; ++index)
     {
       DWORD section_len = (DWORD)section->blocksize;
-      FILETIME last_write_time;
       HKEY sub_hkey;
 
       err = RegEnumKeyEx(hkey, index, section->data, &section_len,
-                         NULL, NULL, NULL, &last_write_time);
+                         NULL, NULL, NULL, NULL);
       if (err == ERROR_NO_MORE_ITEMS)
           break;
       if (err == ERROR_MORE_DATA)
         {
           svn_stringbuf_ensure(section, section_len);
           err = RegEnumKeyEx(hkey, index, section->data, &section_len,
-                             NULL, NULL, NULL, &last_write_time);
+                             NULL, NULL, NULL, NULL);
         }
       if (err != ERROR_SUCCESS)
         {

Modified: subversion/branches/master-passphrase/subversion/libsvn_subr/debug.c
URL: http://svn.apache.org/viewvc/subversion/branches/master-passphrase/subversion/libsvn_subr/debug.c?rev=1481041&r1=1481040&r2=1481041&view=diff
==============================================================================
--- subversion/branches/master-passphrase/subversion/libsvn_subr/debug.c (original)
+++ subversion/branches/master-passphrase/subversion/libsvn_subr/debug.c Fri May 10 14:58:47 2013
@@ -32,6 +32,9 @@
 #include "svn_types.h"
 #include "svn_string.h"
 
+#ifndef SVN_DBG__PROTOTYPES
+#define SVN_DBG__PROTOTYPES
+#endif
 #include "private/svn_debug.h"