You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by vm...@apache.org on 2012/12/23 19:34:20 UTC

svn commit: r1425508 [9/17] - in /subversion/branches/javahl-ra: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/hudson/ notes/ notes/api-errata/1.8/ notes/obliterate/ notes/tree-conflicts/ subversion/ subversion/bindings/s...

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/cache-membuffer.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/cache-membuffer.c Sun Dec 23 18:34:14 2012
@@ -33,11 +33,12 @@
 #include "svn_string.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_mutex.h"
+#include "private/svn_pseudo_md5.h"
 
 /*
  * This svn_cache__t implementation actually consists of two parts:
  * a shared (per-process) singleton membuffer cache instance and shallow
- * svn_cache__t frontend instances that each use different key spaces.
+ * svn_cache__t front-end instances that each use different key spaces.
  * For data management, they all forward to the singleton membuffer cache.
  *
  * A membuffer cache consists of two parts:
@@ -65,7 +66,7 @@
  *
  * 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 accomodate the new
+ * 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.
@@ -79,7 +80,7 @@
  * get evicted, it is moved to the begin of that window and the window is
  * moved.
  *
- * Moreover, the entry's hits get halfed to make that entry more likely to
+ * Moreover, the entry's hits get halved to make that entry more likely to
  * be removed the next time the sliding insertion / removal window comes by.
  * As a result, frequently used entries are likely not to be dropped until
  * they get not used for a while. Also, even a cache thrashing situation
@@ -100,33 +101,46 @@
  * on their hash key.
  */
 
-/* A 4-way associative cache seems to be the best compromise between
+/* A 16-way associative cache seems to be a good compromise between
  * performance (worst-case lookups) and efficiency-loss due to collisions.
  *
  * This value may be changed to any positive integer.
  */
-#define GROUP_SIZE 4
-
-/* We use MD5 for digest size and speed (SHA1 is >2x slower, for instance).
- */
-#define KEY_SIZE APR_MD5_DIGESTSIZE
+#define GROUP_SIZE 16
 
 /* For more efficient copy operations, let'a align all data items properly.
  * Must be a power of 2.
  */
 #define ITEM_ALIGNMENT 16
 
-/* Don't create cache segments smaller than this value unless the total
- * cache size itself is smaller.
+/* 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
+
+/* The minimum segment size we will allow for multi-segmented caches
+ */
+#define MIN_SEGMENT_SIZE 0x10000ull
+
+/* The maximum number of segments allowed. Larger numbers reduce the size
+ * of each segment, in turn reducing the max size of a cachable item.
+ * Also, each segment gets its own lock object. The actual number supported
+ * by the OS may therefore be lower and svn_cache__membuffer_cache_create
+ * may return an error.
  */
-#define MIN_SEGMENT_SIZE 0x2000000ull
+#define MAX_SEGMENT_COUNT 0x10000
+
+/* 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
 
 /* 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
  * vector that is likely to fit into the CPU caches even for fairly large
- * caches. For instance, the default of 32 means 8x32 groups per byte, i.e.
- * 8 flags/byte x 32 groups/flag x 4 entries/group x 40 index bytes/entry
- * x 16 cache bytes/index byte = 1kB init vector / 640MB cache.
+ * membuffer caches. For instance, the default of 32 means 8x32 groups per
+ * byte, i.e. 8 flags/byte x 32 groups/flag x 8 entries/group x 40 index
+ * bytes/entry x 8 cache bytes/index byte = 1kB init vector / 640MB cache.
  */
 #define GROUP_INIT_GRANULARITY 32
 
@@ -134,10 +148,6 @@
  */
 #define NO_INDEX APR_UINT32_MAX
 
-/* Invalid buffer offset reference value. Equivalent to APR_UINT32_T(-1)
- */
-#define NO_OFFSET APR_UINT64_MAX
-
 /* To save space in our group structure, we only use 32 bit size values
  * and, therefore, limit the size of each entry to just below 4GB.
  * Supporting larger items is not a good idea as the data transfer
@@ -145,6 +155,12 @@
  */
 #define MAX_ITEM_SIZE ((apr_uint32_t)(0 - ITEM_ALIGNMENT))
 
+/* A 16 byte key type. We use that to identify cache entries.
+ * The notation as just two integer values will cause many compilers
+ * to create better code.
+ */
+typedef apr_uint64_t entry_key_t[2];
+
 /* Debugging / corruption detection support.
  * If you define this macro, the getter functions will performed expensive
  * checks on the item data, requested keys and entry types. If there is
@@ -205,7 +221,7 @@ static void get_prefix_tail(const char *
 /* Initialize all members of TAG except for the content hash.
  */
 static svn_error_t *store_key_part(entry_tag_t *tag,
-                                   unsigned char *prefix_hash,
+                                   entry_key_t prefix_hash,
                                    char *prefix_tail,
                                    const void *key,
                                    apr_size_t key_len,
@@ -270,11 +286,12 @@ static svn_error_t* assert_equal_tags(co
 
 #define DEBUG_CACHE_MEMBUFFER_TAG_ARG entry_tag_t *tag,
 
-#define DEBUG_CACHE_MEMBUFFER_TAG &tag,
+#define DEBUG_CACHE_MEMBUFFER_TAG tag,
 
 #define DEBUG_CACHE_MEMBUFFER_INIT_TAG                         \
-  entry_tag_t tag;                                             \
-  SVN_ERR(store_key_part(&tag,                                 \
+  entry_tag_t _tag;                                            \
+  entry_tag_t *tag = &_tag;                                    \
+  SVN_ERR(store_key_part(tag,                                  \
                          cache->prefix,                        \
                          cache->prefix_tail,                   \
                          key,                                  \
@@ -296,17 +313,15 @@ 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. An entry is unused if and only if its OFFSET
- * member is NO_OFFSET.
+ * list of used entries.
  */
 typedef struct entry_t
 {
   /* Identifying the data item. Only valid for used entries.
    */
-  unsigned char key [KEY_SIZE];
+  entry_key_t key;
 
-  /* If NO_OFFSET, the entry is not in used. Otherwise, it is the offset
-   * of the cached item's serialized data within the data buffer.
+  /* The offset of the cached item's serialized data within the data buffer.
    */
   apr_uint64_t offset;
 
@@ -344,7 +359,14 @@ typedef struct entry_t
 
 /* We group dictionary entries to make this GROUP-SIZE-way assicative.
  */
-typedef entry_t entry_group_t[GROUP_SIZE];
+typedef struct entry_group_t
+{
+  /* number of entries used [0 .. USED-1] */
+  apr_size_t used;
+
+  /* the actual entries */
+  entry_t entries[GROUP_SIZE];
+} entry_group_t;
 
 /* The cache header structure.
  */
@@ -444,6 +466,12 @@ struct svn_membuffer_t
    * thread-safe.
    */
   apr_thread_rwlock_t *lock;
+
+  /* If set, write access will wait until they get exclusive access.
+   * Otherwise, they will become no-ops if the segment is currently
+   * read-locked.
+   */
+  svn_boolean_t allow_blocking_writes;
 #endif
 };
 
@@ -471,18 +499,48 @@ read_lock_cache(svn_membuffer_t *cache)
   return SVN_NO_ERROR;
 }
 
-/* If locking is supported for CACHE, aquire a write lock for it.
+/* If locking is supported for CACHE, acquire a write lock for it.
  */
 static svn_error_t *
-write_lock_cache(svn_membuffer_t *cache)
+write_lock_cache(svn_membuffer_t *cache, svn_boolean_t *success)
 {
 #if APR_HAS_THREADS
   if (cache->lock)
-  {
-    apr_status_t status = apr_thread_rwlock_wrlock(cache->lock);
-    if (status)
-      return svn_error_wrap_apr(status, _("Can't write-lock cache mutex"));
-  }
+    {
+      apr_status_t status;
+      if (cache->allow_blocking_writes)
+        {
+          status = apr_thread_rwlock_wrlock(cache->lock);
+        }
+      else
+        {
+          status = apr_thread_rwlock_trywrlock(cache->lock);
+          if (SVN_LOCK_IS_BUSY(status))
+            {
+              *success = FALSE;
+              status = APR_SUCCESS;
+            }
+        }
+    
+      if (status)
+        return svn_error_wrap_apr(status,
+                                  _("Can't write-lock cache mutex"));
+    }
+#endif
+  return SVN_NO_ERROR;
+}
+
+/* If locking is supported for CACHE, acquire an unconditional write lock
+ * for it.
+ */
+static svn_error_t *
+force_write_lock_cache(svn_membuffer_t *cache)
+{
+#if APR_HAS_THREADS
+  apr_status_t status = apr_thread_rwlock_wrlock(cache->lock);
+  if (status)
+    return svn_error_wrap_apr(status,
+                              _("Can't write-lock cache mutex"));
 #endif
   return SVN_NO_ERROR;
 }
@@ -508,7 +566,7 @@ unlock_cache(svn_membuffer_t *cache, svn
 }
 
 /* If supported, guard the execution of EXPR with a read lock to cache.
- * Macro has been modelled after SVN_MUTEX__WITH_LOCK.
+ * Macro has been modeled after SVN_MUTEX__WITH_LOCK.
  */
 #define WITH_READ_LOCK(cache, expr)         \
 do {                                        \
@@ -517,12 +575,29 @@ do {                                    
 } while (0)
 
 /* If supported, guard the execution of EXPR with a write lock to cache.
- * Macro has been modelled after SVN_MUTEX__WITH_LOCK.
- */
-#define WITH_WRITE_LOCK(cache, expr)        \
-do {                                        \
-  SVN_ERR(write_lock_cache(cache));         \
-  SVN_ERR(unlock_cache(cache, (expr)));     \
+ * Macro has been modeled after SVN_MUTEX__WITH_LOCK.
+ *
+ * The write lock process is complicated if we don't allow to wait for
+ * the lock: If we didn't get the lock, we may still need to remove an
+ * existing entry for the given key because that content is now stale.
+ * Once we discovered such an entry, we unconditionally do a blocking
+ * wait for the write lock.  In case no old content could be found, a
+ * failing lock attempt is simply a no-op and we exit the macro.
+ */
+#define WITH_WRITE_LOCK(cache, expr)                            \
+do {                                                            \
+  svn_boolean_t got_lock = TRUE;                                \
+  SVN_ERR(write_lock_cache(cache, &got_lock));                  \
+  if (!got_lock)                                                \
+    {                                                           \
+      svn_boolean_t exists;                                     \
+      SVN_ERR(entry_exists(cache, group_index, key, &exists));  \
+      if (exists)                                               \
+        SVN_ERR(force_write_lock_cache(cache));                 \
+      else                                                      \
+        break;                                                  \
+    }                                                           \
+  SVN_ERR(unlock_cache(cache, (expr)));                         \
 } while (0)
 
 /* Resolve a dictionary entry reference, i.e. return the entry
@@ -531,7 +606,7 @@ do {                                    
 static APR_INLINE entry_t *
 get_entry(svn_membuffer_t *cache, apr_uint32_t idx)
 {
-  return &cache->directory[idx / GROUP_SIZE][idx % GROUP_SIZE];
+  return &cache->directory[idx / GROUP_SIZE].entries[idx % GROUP_SIZE];
 }
 
 /* Get the entry references for the given ENTRY.
@@ -539,7 +614,11 @@ get_entry(svn_membuffer_t *cache, apr_ui
 static APR_INLINE apr_uint32_t
 get_index(svn_membuffer_t *cache, entry_t *entry)
 {
-  return (apr_uint32_t)(entry - (entry_t *)cache->directory);
+  apr_uint32_t group_index
+    = ((char *)entry - (char *)cache->directory) / sizeof(entry_group_t);
+
+  return group_index * GROUP_SIZE
+       + (apr_uint32_t)(entry - cache->directory[group_index].entries);
 }
 
 /* Remove the used ENTRY from the CACHE, i.e. make it "unused".
@@ -548,11 +627,16 @@ get_index(svn_membuffer_t *cache, entry_
 static void
 drop_entry(svn_membuffer_t *cache, entry_t *entry)
 {
+  /* the group that ENTRY belongs to plus a number of useful index values
+   */
   apr_uint32_t idx = get_index(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;
 
   /* Only valid to be called for used entries.
    */
-  assert(entry->offset != NO_OFFSET);
+  assert(idx <= last_in_group);
 
   /* update global cache usage counters
    */
@@ -574,7 +658,7 @@ drop_entry(svn_membuffer_t *cache, entry
             /* remove the first entry -> insertion may start at pos 0, now */
             cache->current_data = 0;
           }
-          else
+        else
           {
             /* insertion may start right behind the previous entry */
             entry_t *previous = get_entry(cache, entry->previous);
@@ -595,9 +679,35 @@ drop_entry(svn_membuffer_t *cache, entry
   else
     get_entry(cache, entry->next)->previous = entry->previous;
 
-  /* Mark the entry as unused.
+  /* 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.
    */
-  entry->offset = NO_OFFSET;
+  if (idx < last_in_group)
+    {
+      /* copy the last used entry to the removed entry's index
+       */
+      *entry = group->entries[group->used-1];
+
+      /* update foreign links to new index
+       */
+      if (last_in_group == cache->next)
+        cache->next = idx;
+
+      if (entry->previous == NO_INDEX)
+        cache->first = idx;
+      else
+        get_entry(cache, entry->previous)->next = idx;
+      
+      if (entry->next == NO_INDEX)
+        cache->last = idx;
+      else
+        get_entry(cache, entry->next)->previous = idx;
+    }
+
+  /* Update the number of used entries.
+   */
+  group->used--;
 }
 
 /* Insert ENTRY into the chain of used dictionary entries. The entry's
@@ -607,21 +717,28 @@ drop_entry(svn_membuffer_t *cache, entry
 static void
 insert_entry(svn_membuffer_t *cache, entry_t *entry)
 {
+  /* the group that ENTRY belongs to plus a number of useful index values
+   */
   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);
 
   /* 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(idx == group_index * GROUP_SIZE + group->used);
   cache->current_data = ALIGN_VALUE(entry->offset + entry->size);
 
-  /* update global cache usage counters
+  /* update usage counters
    */
   cache->used_entries++;
   cache->data_used += entry->size;
   entry->hit_count = 0;
+  group->used++;
 
   /* update entry chain
    */
@@ -656,6 +773,11 @@ insert_entry(svn_membuffer_t *cache, ent
       else
         cache->first = idx;
     }
+
+  /* The current insertion position must never point outside our
+   * data buffer.
+   */
+  assert(cache->current_data <= cache->data_size);
 }
 
 /* Map a KEY of 16 bytes to the CACHE and group that shall contain the
@@ -663,22 +785,13 @@ insert_entry(svn_membuffer_t *cache, ent
  */
 static apr_uint32_t
 get_group_index(svn_membuffer_t **cache,
-                const apr_uint32_t *key)
+                entry_key_t key)
 {
-  apr_uint32_t hash;
-
-  /* Get the group that *must* contain the entry. Fold the hash value
-   * just to be sure (it should not be necessary for perfect hashes).
-   */
-  hash = key[0];
-  hash = key[1] ^ ((hash >> 19) || (hash << 13));
-  hash = key[2] ^ ((hash >> 19) || (hash << 13));
-  hash = key[3] ^ ((hash >> 19) || (hash << 13));
-
-  /* select the cache segment to use */
-  *cache = &(*cache)[key[0] & ((*cache)->segment_count -1)];
-
-  return hash % (*cache)->group_count;
+  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;
 }
 
 /* Reduce the hit count of ENTRY and update the accumunated hit info
@@ -714,7 +827,7 @@ static void
 initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
 {
   unsigned char bit_mask;
-  apr_uint32_t i, j;
+  apr_uint32_t i;
 
   /* range of groups to initialize due to GROUP_INIT_GRANULARITY */
   apr_uint32_t first_index =
@@ -724,8 +837,7 @@ initialize_group(svn_membuffer_t *cache,
     last_index = cache->group_count;
 
   for (i = first_index; i < last_index; ++i)
-    for (j = 0; j < GROUP_SIZE; j++)
-        cache->directory[i][j].offset = NO_OFFSET;
+    cache->directory[i].used = 0;
 
   /* set the "initialized" bit for these groups */
   bit_mask
@@ -749,16 +861,16 @@ initialize_group(svn_membuffer_t *cache,
 static entry_t *
 find_entry(svn_membuffer_t *cache,
            apr_uint32_t group_index,
-           const unsigned char *to_find,
+           const apr_uint64_t to_find[2],
            svn_boolean_t find_empty)
 {
-  entry_t *group;
+  entry_group_t *group;
   entry_t *entry = NULL;
-  int i;
+  apr_size_t i;
 
   /* get the group that *must* contain the entry
    */
-  group = &cache->directory[group_index][0];
+  group = &cache->directory[group_index];
 
   /* If the entry group has not been initialized, yet, there is no data.
    */
@@ -767,10 +879,11 @@ find_entry(svn_membuffer_t *cache,
       if (find_empty)
         {
           initialize_group(cache, group_index);
-          entry = group;
+          entry = &group->entries[0];
 
           /* initialize entry for the new key */
-          memcpy(entry->key, to_find, KEY_SIZE);
+          entry->key[0] = to_find[0];
+          entry->key[1] = to_find[1];
         }
 
       return entry;
@@ -778,54 +891,53 @@ find_entry(svn_membuffer_t *cache,
 
   /* try to find the matching entry
    */
-  for (i = 0; i < GROUP_SIZE; ++i)
-    if (group[i].offset != NO_OFFSET &&
-        !memcmp(to_find, group[i].key, KEY_SIZE))
+  for (i = 0; i < group->used; ++i)
+    if (   to_find[0] == group->entries[i].key[0]
+        && to_find[1] == group->entries[i].key[1])
       {
         /* found it
          */
-        entry = &group[i];
+        entry = &group->entries[i];
         if (find_empty)
           drop_entry(cache, entry);
-
-        return entry;
+        else
+          return entry;
       }
 
   /* None found. Are we looking for a free entry?
    */
   if (find_empty)
     {
-      /* look for an empty entry and return that ...
-       */
-      for (i = 0; i < GROUP_SIZE; ++i)
-        if (group[i].offset == NO_OFFSET)
-          {
-            entry = &group[i];
-            break;
-          }
-
-      /* ... or, if none is empty, delete the oldest entry
+      /* if there is no empty entry, delete the oldest entry
        */
-      if (entry == NULL)
+      if (group->used == GROUP_SIZE)
         {
-          entry = &group[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
+           * the other entries in this group.
+           */
+          entry = &group->entries[rand() % GROUP_SIZE];
           for (i = 1; i < GROUP_SIZE; ++i)
-            if (entry->hit_count > group[i].hit_count)
-              entry = &group[i];
+            if (entry->hit_count > group->entries[i].hit_count)
+              entry = &group->entries[i];
 
           /* for the entries that don't have been removed,
            * reduce their hitcounts to put them at a relative
            * disadvantage the next time.
            */
           for (i = 0; i < GROUP_SIZE; ++i)
-            if (entry != &group[i])
+            if (entry != &group->entries[i])
               let_entry_age(cache, entry);
 
           drop_entry(cache, entry);
         }
 
-      /* initialize entry for the new key */
-      memcpy(entry->key, to_find, KEY_SIZE);
+      /* initialize entry for the new key
+       */
+      entry = &group->entries[group->used];
+      entry->key[0] = to_find[0];
+      entry->key[1] = to_find[1];
     }
 
   return entry;
@@ -863,6 +975,11 @@ move_entry(svn_membuffer_t *cache, entry
    */
   cache->current_data = entry->offset + size;
   cache->next = entry->next;
+
+  /* The current insertion position must never point outside our
+   * data buffer.
+   */
+  assert(cache->current_data <= cache->data_size);
 }
 
 /* If necessary, enlarge the insertion window until it is at least
@@ -904,7 +1021,7 @@ ensure_data_insertable(svn_membuffer_t *
 
       /* leave function as soon as the insertion window is large enough
        */
-      if (end - cache->current_data >= size)
+      if (end >= size + cache->current_data)
         return TRUE;
 
       /* Don't be too eager to cache data. Smaller items will fit into
@@ -946,22 +1063,38 @@ ensure_data_insertable(svn_membuffer_t *
             }
           else
             {
-              /* 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;
+              svn_boolean_t keep;
 
-              /* 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.
-               */
-              if (entry->hit_count >= threshold)
+              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.
+                   */
+                  average_hit_value = cache->hit_count / cache->used_entries;
+                  threshold = (average_hit_value+1) * (rand() % 4096) / 2048;
+
+                  keep = entry->hit_count >= threshold;
+                }
+              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);
+                }
+
+              /* keepers or destroyers? */
+              if (keep)
                 {
                   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_size += entry->size;
                   drop_entry(cache, entry);
                 }
@@ -995,56 +1128,82 @@ static void* secure_aligned_alloc(apr_po
   return memory;
 }
 
-/* Create a new membuffer cache instance. If the TOTAL_SIZE of the
- * memory i too small to accomodate the DICTIONARY_SIZE, the latte
- * will be resized automatically. Also, a minumum size is assured
- * for the DICTIONARY_SIZE. THREAD_SAFE may be FALSE, if there will
- * be no concurrent acccess to the CACHE returned.
- *
- * All allocations, in particular the data buffer and dictionary will
- * be made from POOL.
- */
 svn_error_t *
 svn_cache__membuffer_cache_create(svn_membuffer_t **cache,
                                   apr_size_t total_size,
                                   apr_size_t directory_size,
+                                  apr_size_t segment_count,
                                   svn_boolean_t thread_safe,
+                                  svn_boolean_t allow_blocking_writes,
                                   apr_pool_t *pool)
 {
   svn_membuffer_t *c;
 
-  apr_uint32_t segment_count_shift = 0;
-  apr_uint32_t segment_count = 1;
-
   apr_uint32_t seg;
   apr_uint32_t group_count;
   apr_uint32_t group_init_size;
   apr_uint64_t data_size;
   apr_uint64_t max_entry_size;
 
-  /* Determine a reasonable number of cache segments. Segmentation is
-   * only useful for multi-threaded / multi-core servers as it reduces
-   * lock contention on these systems.
-   *
-   * But on these systems, we can assume that ample memory has been
-   * allocated to this cache. Smaller caches should not be segmented
-   * as this severely limites the maximum size of cachable items.
-   *
-   * Segments should not be smaller than 32MB and max. cachable item
-   * size should grow as fast as segmentation.
+  /* Limit the total size (only relevant if we can address > 4GB)
    */
-  while (((2 * MIN_SEGMENT_SIZE) << (2 * segment_count_shift)) < total_size)
-    ++segment_count_shift;
+#if APR_SIZEOF_VOIDP > 4
+  if (total_size > MAX_SEGMENT_SIZE * MAX_SEGMENT_COUNT)
+    total_size = MAX_SEGMENT_SIZE * MAX_SEGMENT_COUNT;
+#endif
 
-  segment_count = 1 << segment_count_shift;
+  /* Limit the segment count
+   */
+  if (segment_count > MAX_SEGMENT_COUNT)
+    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)
+    segment_count &= segment_count-1;
+
+  /* if the caller hasn't provided a reasonable segment count or the above
+   * limitations set it to 0, derive one from the absolute cache size
+   */
+  if (segment_count < 1)
+    {
+      /* Determine a reasonable number of cache segments. Segmentation is
+       * only useful for multi-threaded / multi-core servers as it reduces
+       * lock contention on these systems.
+       *
+       * But on these systems, we can assume that ample memory has been
+       * allocated to this cache. Smaller caches should not be segmented
+       * as this severely limits the maximum size of cachable items.
+       *
+       * Segments should not be smaller than 32MB and max. cachable item
+       * size should grow as fast as segmentation.
+       */
+
+      apr_uint32_t segment_count_shift = 0;
+      while (((2 * DEFAULT_MIN_SEGMENT_SIZE) << (2 * segment_count_shift))
+             < total_size)
+        ++segment_count_shift;
+
+      segment_count = 1 << segment_count_shift;
+    }
+
+  /* If we have an extremely large cache (>512 GB), the default segment
+   * size may exceed the amount allocatable as one chunk. In that case,
+   * increase segmentation until we are under the threshold.
+   */
+  while (   total_size / segment_count > MAX_SEGMENT_SIZE
+         && segment_count < MAX_SEGMENT_COUNT)
+    segment_count *= 2;
 
   /* allocate cache as an array of segments / cache objects */
   c = apr_palloc(pool, segment_count * sizeof(*c));
 
   /* Split total cache size into segments of equal size
    */
-  total_size >>= segment_count_shift;
-  directory_size >>= segment_count_shift;
+  total_size /= segment_count;
+  directory_size /= segment_count;
 
   /* prevent pathological conditions: ensure a certain minimum cache size
    */
@@ -1061,8 +1220,10 @@ svn_cache__membuffer_cache_create(svn_me
 
   /* limit the data size to what we can address.
    * Note that this cannot overflow since all values are of size_t.
+   * Also, make it a multiple of the item placement granularity to
+   * prevent subtle overflows.
    */
-  data_size = total_size - directory_size;
+  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
@@ -1137,6 +1298,10 @@ svn_cache__membuffer_cache_create(svn_me
           if (status)
             return svn_error_wrap_apr(status, _("Can't create cache mutex"));
         }
+
+      /* Select the behavior of write operations.
+       */
+      c[seg].allow_blocking_writes = allow_blocking_writes;
 #endif
     }
 
@@ -1146,6 +1311,40 @@ svn_cache__membuffer_cache_create(svn_me
   return SVN_NO_ERROR;
 }
 
+/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
+ * by the hash value TO_FIND and set *FOUND accordingly.
+ *
+ * Note: This function requires the caller to serialize access.
+ * Don't call it directly, call entry_exists instead.
+ */
+static svn_error_t *
+entry_exists_internal(svn_membuffer_t *cache,
+                      apr_uint32_t group_index,
+                      entry_key_t to_find,
+                      svn_boolean_t *found)
+{
+  *found = find_entry(cache, group_index, to_find, FALSE) != NULL;
+  return SVN_NO_ERROR;
+}
+
+/* Look for the cache entry in group GROUP_INDEX of CACHE, identified
+ * by the hash value TO_FIND and set *FOUND accordingly.
+ */
+static svn_error_t *
+entry_exists(svn_membuffer_t *cache,
+             apr_uint32_t group_index,
+             entry_key_t to_find,
+             svn_boolean_t *found)
+{
+  WITH_READ_LOCK(cache,
+                 entry_exists_internal(cache,
+                                       group_index,
+                                       to_find,
+                                       found));
+
+  return SVN_NO_ERROR;
+}
+
 
 /* Try to insert the serialized item given in BUFFER with SIZE into
  * the group GROUP_INDEX of CACHE and uniquely identify it by hash
@@ -1161,13 +1360,39 @@ svn_cache__membuffer_cache_create(svn_me
  */
 static svn_error_t *
 membuffer_cache_set_internal(svn_membuffer_t *cache,
-                             const unsigned char *to_find,
+                             entry_key_t to_find,
                              apr_uint32_t group_index,
                              char *buffer,
                              apr_size_t size,
                              DEBUG_CACHE_MEMBUFFER_TAG_ARG
                              apr_pool_t *scratch_pool)
 {
+  /* first, look for a previous entry for the given key */
+  entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
+
+  /* if there is an old version of that entry and the new data fits into
+   * the old spot, just re-use that space. */
+  if (buffer && entry && entry->size >= size)
+    {
+      cache->data_used += size - entry->size;
+      entry->size = size;
+
+#ifdef SVN_DEBUG_CACHE_MEMBUFFER
+
+      /* Remember original content, type and key (hashes)
+       */
+      SVN_ERR(store_content_part(tag, buffer, size, scratch_pool));
+      memcpy(&entry->tag, tag, sizeof(*tag));
+
+#endif
+
+      if (size)
+        memcpy(cache->data + entry->offset, buffer, size);
+
+      cache->total_writes++;
+      return SVN_NO_ERROR;
+    }
+
   /* if necessary, enlarge the insertion window.
    */
   if (   buffer != NULL
@@ -1176,9 +1401,9 @@ membuffer_cache_set_internal(svn_membuff
     {
       /* Remove old data for this key, if that exists.
        * Get an unused entry for the key and and initialize it with
-       * the serialized item's (future) posion within data buffer.
+       * the serialized item's (future) position within data buffer.
        */
-      entry_t *entry = find_entry(cache, group_index, to_find, TRUE);
+      entry = find_entry(cache, group_index, to_find, TRUE);
       entry->size = size;
       entry->offset = cache->current_data;
 
@@ -1191,22 +1416,25 @@ membuffer_cache_set_internal(svn_membuff
 
 #endif
 
+      /* Link the entry properly.
+       */
+      insert_entry(cache, entry);
+
       /* Copy the serialized item data into the cache.
        */
       if (size)
         memcpy(cache->data + entry->offset, buffer, size);
 
-      /* Link the entry properly.
-       */
-      insert_entry(cache, entry);
       cache->total_writes++;
     }
   else
     {
       /* if there is already an entry for this key, drop it.
        */
-      find_entry(cache, group_index, to_find, TRUE);
+      if (entry)
+        drop_entry(cache, entry);
     }
+
   return SVN_NO_ERROR;
 }
 
@@ -1221,7 +1449,7 @@ membuffer_cache_set_internal(svn_membuff
  */
 static svn_error_t *
 membuffer_cache_set(svn_membuffer_t *cache,
-                    const void *key,
+                    entry_key_t key,
                     void *item,
                     svn_cache__serialize_func_t serializer,
                     DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -1265,7 +1493,7 @@ membuffer_cache_set(svn_membuffer_t *cac
 static svn_error_t *
 membuffer_cache_get_internal(svn_membuffer_t *cache,
                              apr_uint32_t group_index,
-                             const unsigned char *to_find,
+                             entry_key_t to_find,
                              char **buffer,
                              apr_size_t *item_size,
                              DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -1302,7 +1530,7 @@ membuffer_cache_get_internal(svn_membuff
 
   /* Compare original content, type and key (hashes)
    */
-  SVN_ERR(store_content_part(tag, buffer, entry->size, result_pool));
+  SVN_ERR(store_content_part(tag, *buffer, entry->size, result_pool));
   SVN_ERR(assert_equal_tags(&entry->tag, tag));
 
 #endif
@@ -1325,7 +1553,7 @@ membuffer_cache_get_internal(svn_membuff
  */
 static svn_error_t *
 membuffer_cache_get(svn_membuffer_t *cache,
-                    const void *key,
+                    entry_key_t key,
                     void **item,
                     svn_cache__deserialize_func_t deserializer,
                     DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -1372,7 +1600,7 @@ membuffer_cache_get(svn_membuffer_t *cac
 static svn_error_t *
 membuffer_cache_get_partial_internal(svn_membuffer_t *cache,
                                      apr_uint32_t group_index,
-                                     const unsigned char *to_find,
+                                     entry_key_t to_find,
                                      void **item,
                                      svn_boolean_t *found,
                                      svn_cache__partial_getter_func_t deserializer,
@@ -1431,7 +1659,7 @@ membuffer_cache_get_partial_internal(svn
  */
 static svn_error_t *
 membuffer_cache_get_partial(svn_membuffer_t *cache,
-                            const void *key,
+                            entry_key_t key,
                             void **item,
                             svn_boolean_t *found,
                             svn_cache__partial_getter_func_t deserializer,
@@ -1463,7 +1691,7 @@ membuffer_cache_get_partial(svn_membuffe
 static svn_error_t *
 membuffer_cache_set_partial_internal(svn_membuffer_t *cache,
                                      apr_uint32_t group_index,
-                                     const unsigned char *to_find,
+                                     entry_key_t to_find,
                                      svn_cache__partial_setter_func_t func,
                                      void *baton,
                                      DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -1504,7 +1732,7 @@ membuffer_cache_set_partial_internal(svn
 
 #endif
 
-      /* modify it, preferrably in-situ.
+      /* modify it, preferably in-situ.
        */
       err = func((void **)&data, &size, baton, scratch_pool);
 
@@ -1525,7 +1753,7 @@ membuffer_cache_set_partial_internal(svn
             {
               /* Remove the old entry and try to make space for the new one.
                */
-              drop_entry(cache, entry);
+              entry = find_entry(cache, group_index, to_find, TRUE);
               if (   (cache->max_entry_size >= size)
                   && ensure_data_insertable(cache, size))
                 {
@@ -1539,17 +1767,17 @@ membuffer_cache_set_partial_internal(svn
                   /* Link the entry properly.
                    */
                   insert_entry(cache, entry);
+                }
+            }
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
-                  /* Remember original content, type and key (hashes)
-                   */
-                  SVN_ERR(store_content_part(tag, data, size, scratch_pool));
-                  memcpy(&entry->tag, tag, sizeof(*tag));
+          /* Remember original content, type and key (hashes)
+           */
+          SVN_ERR(store_content_part(tag, data, size, scratch_pool));
+          memcpy(&entry->tag, tag, sizeof(*tag));
 
 #endif
-                }
-            }
         }
     }
 
@@ -1563,7 +1791,7 @@ membuffer_cache_set_partial_internal(svn
  */
 static svn_error_t *
 membuffer_cache_set_partial(svn_membuffer_t *cache,
-                            const void *key,
+                            entry_key_t key,
                             svn_cache__partial_setter_func_t func,
                             void *baton,
                             DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -1575,7 +1803,7 @@ membuffer_cache_set_partial(svn_membuffe
   WITH_WRITE_LOCK(cache,
                   membuffer_cache_set_partial_internal
                      (cache, group_index, key, func, baton,
-                      DEBUG_CACHE_MEMBUFFER_TAG_ARG
+                      DEBUG_CACHE_MEMBUFFER_TAG
                       scratch_pool));
 
   /* done here -> unlock the cache
@@ -1619,7 +1847,7 @@ typedef struct svn_membuffer_cache_t
    * This makes (very likely) our keys different from all keys used
    * by other svn_membuffer_cache_t instances.
    */
-  apr_uint64_t prefix [APR_MD5_DIGESTSIZE / sizeof(apr_uint64_t)];
+  entry_key_t prefix;
 
   /* A copy of the unmodified prefix. It is being used as a user-visible
    * ID for this cache instance.
@@ -1633,7 +1861,7 @@ typedef struct svn_membuffer_cache_t
 
   /* Temporary buffer containing the hash key for the current access
    */
-  apr_uint64_t combined_key [APR_MD5_DIGESTSIZE / sizeof(apr_uint64_t)];
+  entry_key_t combined_key;
 
   /* a pool for temporary allocations during get() and set()
    */
@@ -1673,7 +1901,31 @@ combine_key(svn_membuffer_cache_t *cache
   if (key_len == APR_HASH_KEY_STRING)
     key_len = strlen((const char *) key);
 
-  apr_md5((unsigned char*)cache->combined_key, key, key_len);
+  if (key_len < 16)
+    {
+      apr_uint32_t data[4] = { 0 };
+      memcpy(data, key, key_len);
+
+      svn__pseudo_md5_15((apr_uint32_t *)cache->combined_key, data);
+    }
+  else if (key_len < 32)
+    {
+      apr_uint32_t data[8] = { 0 };
+      memcpy(data, key, key_len);
+
+      svn__pseudo_md5_31((apr_uint32_t *)cache->combined_key, data);
+    }
+  else if (key_len < 64)
+    {
+      apr_uint32_t data[16] = { 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);
+    }
 
   cache->combined_key[0] ^= cache->prefix[0];
   cache->combined_key[1] ^= cache->prefix[1];

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/cache-memcache.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/cache-memcache.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/cache-memcache.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/cache-memcache.c Sun Dec 23 18:34:14 2012
@@ -470,7 +470,8 @@ add_memcache_server(const char *name,
                                        0,  /* min connections */
                                        5,  /* soft max connections */
                                        10, /* hard max connections */
-                                       50, /* connection time to live (secs) */
+                                       /*  time to live (in microseconds) */
+                                       apr_time_from_sec(50),
                                        &server);
   if (apr_err != APR_SUCCESS)
     {

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/cache_config.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/cache_config.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/cache_config.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/cache_config.c Sun Dec 23 18:34:14 2012
@@ -52,7 +52,7 @@ static svn_cache_config_t cache_settings
                   * has little impact on performance and a more modest
                   * value (< 100) may be more suitable.
                   */
-#ifdef APR_HAS_THREADS
+#if APR_HAS_THREADS
     FALSE        /* assume multi-threaded operation.
                   * Because this simply activates proper synchronization
                   * between threads, it is a safe default.
@@ -118,8 +118,10 @@ svn_cache__get_global_membuffer_cache(vo
       err = svn_cache__membuffer_cache_create(
           &new_cache,
           (apr_size_t)cache_size,
-          (apr_size_t)(cache_size / 16),
+          (apr_size_t)(cache_size / 10),
+          0,
           ! svn_cache_config_get()->single_threaded,
+          FALSE,
           pool);
 
       /* Some error occured. Most likely it's an OOM error but we don't

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/cmdline.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/cmdline.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/cmdline.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/cmdline.c Sun Dec 23 18:34:14 2012
@@ -526,6 +526,13 @@ svn_cmdline_create_auth_baton(svn_auth_b
 
   if (non_interactive == FALSE)
     {
+      svn_boolean_t ssl_client_cert_file_prompt;
+
+      SVN_ERR(svn_config_get_bool(cfg, &ssl_client_cert_file_prompt,
+                                  SVN_CONFIG_SECTION_AUTH,
+                                  SVN_CONFIG_OPTION_SSL_CLIENT_CERT_FILE_PROMPT,
+                                  FALSE));
+
       /* Two basic prompt providers: username/password, and just username. */
       svn_auth_get_simple_prompt_provider(&provider,
                                           svn_cmdline_auth_simple_prompt,
@@ -539,19 +546,23 @@ svn_cmdline_create_auth_baton(svn_auth_b
          2, /* retry limit */ pool);
       APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
 
-      /* Three ssl prompt providers, for server-certs, client-certs,
-         and client-cert-passphrases.  */
+      /* SSL prompt providers: server-certs and client-cert-passphrases.  */
       svn_auth_get_ssl_server_trust_prompt_provider
         (&provider, svn_cmdline_auth_ssl_server_trust_prompt, pb, pool);
       APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
 
-      svn_auth_get_ssl_client_cert_prompt_provider
-        (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool);
-      APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
-
       svn_auth_get_ssl_client_cert_pw_prompt_provider
         (&provider, svn_cmdline_auth_ssl_client_cert_pw_prompt, pb, 2, pool);
       APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+
+      /* If configuration allows, add a provider for client-cert path
+         prompting, too. */
+      if (ssl_client_cert_file_prompt)
+        {
+          svn_auth_get_ssl_client_cert_prompt_provider
+            (&provider, svn_cmdline_auth_ssl_client_cert_prompt, pb, 2, pool);
+          APR_ARRAY_PUSH(providers, svn_auth_provider_object_t *) = provider;
+        }
     }
   else if (trust_server_cert)
     {
@@ -631,6 +642,7 @@ void
 svn_cmdline__print_xml_prop(svn_stringbuf_t **outstr,
                             const char* propname,
                             svn_string_t *propval,
+                            svn_boolean_t inherited_prop,
                             apr_pool_t *pool)
 {
   const char *xml_safe;
@@ -654,16 +666,22 @@ svn_cmdline__print_xml_prop(svn_stringbu
     }
 
   if (encoding)
-    svn_xml_make_open_tag(outstr, pool, svn_xml_protect_pcdata,
-                          "property", "name", propname,
-                          "encoding", encoding, NULL);
+    svn_xml_make_open_tag(
+      outstr, pool, svn_xml_protect_pcdata,
+      inherited_prop ? "inherited_property" : "property",
+      "name", propname,
+      "encoding", encoding, NULL);
   else
-    svn_xml_make_open_tag(outstr, pool, svn_xml_protect_pcdata,
-                          "property", "name", propname, NULL);
+    svn_xml_make_open_tag(
+      outstr, pool, svn_xml_protect_pcdata,
+      inherited_prop ? "inherited_property" : "property",
+      "name", propname, NULL);
 
   svn_stringbuf_appendcstr(*outstr, xml_safe);
 
-  svn_xml_make_close_tag(outstr, pool, "property");
+  svn_xml_make_close_tag(
+    outstr, pool,
+    inherited_prop ? "inherited_property" : "property");
 
   return;
 }

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/config.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/config.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/config.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/config.c Sun Dec 23 18:34:14 2012
@@ -555,6 +555,43 @@ expand_option_value(svn_config_t *cfg, c
     *opt_x_valuep = NULL;
 }
 
+static void
+svn_config_addsection(svn_config_t *cfg,
+                      const char *section,
+                      cfg_section_t **sec)
+{  
+  cfg_section_t *s;
+
+  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;
+  else
+    s->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;
+}
+
+static void
+svn_config_create_option(cfg_option_t **opt,
+                         const char *option,
+                         const char *value,
+                         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));
+
+  o->value = apr_pstrdup(pool, value);
+  o->x_value = NULL;
+  o->expanded = FALSE;
+  
+  *opt = o;
+}
 
 
 void
@@ -612,25 +649,12 @@ svn_config_set(svn_config_t *cfg,
     }
 
   /* Create a new option */
-  opt = apr_palloc(cfg->pool, sizeof(*opt));
-  opt->name = apr_pstrdup(cfg->pool, option);
-  opt->hash_key = make_hash_key(apr_pstrdup(cfg->pool, option));
-
-  opt->value = apr_pstrdup(cfg->pool, value);
-  opt->x_value = NULL;
-  opt->expanded = FALSE;
+  svn_config_create_option(&opt, option, value, cfg->pool);
 
   if (sec == NULL)
     {
       /* Even the section doesn't exist. Create it. */
-      sec = apr_palloc(cfg->pool, sizeof(*sec));
-      sec->name = apr_pstrdup(cfg->pool, section);
-      if(cfg->section_names_case_sensitive)
-        sec->hash_key = sec->name;
-      else
-        sec->hash_key = make_hash_key(apr_pstrdup(cfg->pool, section));
-      sec->options = apr_hash_make(cfg->pool);
-      apr_hash_set(cfg->sections, sec->hash_key, APR_HASH_KEY_STRING, sec);
+      svn_config_addsection(cfg, section, &sec);
     }
 
   apr_hash_set(sec->options, opt->hash_key, APR_HASH_KEY_STRING, opt);
@@ -951,6 +975,94 @@ svn_config_get_server_setting(svn_config
   return retval;
 }
 
+
+svn_error_t *
+svn_config_dup(svn_config_t **cfgp,
+               svn_config_t *src,
+               apr_pool_t *pool)
+{
+  apr_hash_index_t *sectidx;
+  apr_hash_index_t *optidx;
+
+  *cfgp = 0;
+  SVN_ERR(svn_config_create(cfgp, FALSE, pool));
+
+  (*cfgp)->x_values = src->x_values;
+  (*cfgp)->section_names_case_sensitive = src->section_names_case_sensitive;
+
+  for (sectidx = apr_hash_first(pool, src->sections);
+       sectidx != NULL;
+       sectidx = apr_hash_next(sectidx))
+  {
+    const void *sectkey;
+    void *sectval;
+    apr_ssize_t sectkeyLength;
+    cfg_section_t * srcsect;
+    cfg_section_t * destsec;
+
+    apr_hash_this(sectidx, &sectkey, &sectkeyLength, &sectval);
+    srcsect = sectval;
+
+    svn_config_addsection(*cfgp, srcsect->name, &destsec);
+
+    for (optidx = apr_hash_first(pool, srcsect->options);
+         optidx != NULL;
+         optidx = apr_hash_next(optidx))
+    {
+      const void *optkey;
+      void *optval;
+      apr_ssize_t optkeyLength;
+      cfg_option_t *srcopt;
+      cfg_option_t *destopt;
+
+      apr_hash_this(optidx, &optkey, &optkeyLength, &optval);
+      srcopt = optval;
+
+      svn_config_create_option(&destopt, srcopt->name, srcopt->value, pool);
+
+      destopt->value = apr_pstrdup(pool, srcopt->value);
+      destopt->x_value = apr_pstrdup(pool, srcopt->x_value);
+      destopt->expanded = srcopt->expanded;
+      apr_hash_set(destsec->options,
+                   apr_pstrdup(pool, (const char*)optkey),
+                   optkeyLength, destopt);
+    }
+  }
+  
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_config_copy_config(apr_hash_t **cfg_hash,
+                       apr_hash_t *src_hash,
+                       apr_pool_t *pool)
+{
+  apr_hash_index_t *cidx;
+
+  *cfg_hash = apr_hash_make(pool);
+  for (cidx = apr_hash_first(pool, src_hash);
+       cidx != NULL;
+       cidx = apr_hash_next(cidx))
+  {
+    const void *ckey;
+    void *cval;
+    apr_ssize_t ckeyLength;
+    svn_config_t * srcconfig;
+    svn_config_t * destconfig;
+
+    apr_hash_this(cidx, &ckey, &ckeyLength, &cval);
+    srcconfig = cval;
+
+    SVN_ERR(svn_config_dup(&destconfig, srcconfig, pool));
+
+    apr_hash_set(*cfg_hash, 
+                 apr_pstrdup(pool, (const char*)ckey),
+                 ckeyLength, destconfig);
+  }
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t*
 svn_config_get_server_setting_int(svn_config_t *cfg,
                                   const char *server_group,

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/config_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/config_file.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/config_file.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/config_file.c Sun Dec 23 18:34:14 2012
@@ -1034,6 +1034,13 @@ svn_config_ensure(const char *config_dir
         "# kwallet-svn-application-name-with-pid = yes"                      NL
 #endif
         "###"                                                                NL
+        "### Set ssl-client-cert-file-prompt to 'yes' to cause the client"   NL
+        "### to prompt for a path to a client cert file when the server"     NL
+        "### requests a client cert but no client cert file is found in the" NL
+        "### expected place (see the 'ssl-client-cert-file' option in the"   NL
+        "### 'servers' configuration file). Defaults to 'no'."               NL
+        "# ssl-client-cert-file-prompt = no"                                 NL
+        "###"                                                                NL
         "### The rest of the [auth] section in this file has been deprecated."
                                                                              NL
         "### Both 'store-passwords' and 'store-auth-creds' can now be"       NL
@@ -1153,7 +1160,12 @@ svn_config_ensure(const char *config_dir
         "# *.png = svn:mime-type=image/png"                                  NL
         "# *.jpg = svn:mime-type=image/jpeg"                                 NL
         "# Makefile = svn:eol-style=native"                                  NL
-        ""                                                                   NL;
+        ""                                                                   NL
+        "### Section for configuring working copies."                        NL
+        "[working-copy]"                                                     NL
+        "### Set to true to enable exclusive SQLite locking.  Some clients"  NL
+        "### may not support exclusive locking."                             NL
+        "# exclusive-locking = false"                                        NL;
 
       err = svn_io_file_open(&f, path,
                              (APR_WRITE | APR_CREATE | APR_EXCL),

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/deprecated.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/deprecated.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/deprecated.c Sun Dec 23 18:34:14 2012
@@ -36,6 +36,7 @@
 #include "svn_path.h"
 #include "svn_opt.h"
 #include "svn_cmdline.h"
+#include "svn_version.h"
 #include "svn_pools.h"
 #include "svn_dso.h"
 #include "svn_mergeinfo.h"
@@ -604,8 +605,11 @@ svn_opt_print_help(apr_getopt_t *os,
         }
     }
   else if (print_version)   /* just --version */
-    SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer,
-                                        quiet, FALSE, pool));
+    {
+      SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer,
+                                          svn_version_extended(FALSE, pool),
+                                          quiet, FALSE, pool));
+    }
   else if (os && !targets->nelts)            /* `-h', `--help', or `help' */
     svn_opt_print_generic_help(header,
                                cmd_table,

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/eol.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/eol.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/eol.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/eol.c Sun Dec 23 18:34:14 2012
@@ -32,18 +32,6 @@
 
 /* Machine-word-sized masks used in svn_eol__find_eol_start.
  */
-#if APR_SIZEOF_VOIDP == 8
-#  define LOWER_7BITS_SET 0x7f7f7f7f7f7f7f7f
-#  define BIT_7_SET       0x8080808080808080
-#  define R_MASK          0x0a0a0a0a0a0a0a0a
-#  define N_MASK          0x0d0d0d0d0d0d0d0d
-#else
-#  define LOWER_7BITS_SET 0x7f7f7f7f
-#  define BIT_7_SET       0x80808080
-#  define R_MASK          0x0a0a0a0a
-#  define N_MASK          0x0d0d0d0d
-#endif
-
 char *
 svn_eol__find_eol_start(char *buf, apr_size_t len)
 {
@@ -69,19 +57,19 @@ svn_eol__find_eol_start(char *buf, apr_s
     /* This is a variant of the well-known strlen test: */
     apr_uintptr_t chunk = *(const apr_uintptr_t *)buf;
 
-    /* A byte in R_TEST is \0, iff it was \r in *BUF.
-     * Similarly, N_TEST is an indicator for \n. */
-    apr_uintptr_t r_test = chunk ^ R_MASK;
-    apr_uintptr_t n_test = chunk ^ N_MASK;
-
-    /* A byte in R_TEST can by < 0x80, iff it has been \0 before
-     * (i.e. \r in *BUF). Dito for N_TEST. */
-    r_test |= (r_test & LOWER_7BITS_SET) + LOWER_7BITS_SET;
-    n_test |= (n_test & LOWER_7BITS_SET) + LOWER_7BITS_SET;
+    /* A byte in SVN__R_TEST is \0, iff it was \r in *BUF.
+     * Similarly, SVN__N_TEST is an indicator for \n. */
+    apr_uintptr_t r_test = chunk ^ SVN__R_MASK;
+    apr_uintptr_t n_test = chunk ^ SVN__N_MASK;
+
+    /* A byte in SVN__R_TEST can by < 0x80, iff it has been \0 before
+     * (i.e. \r in *BUF). Dito for SVN__N_TEST. */
+    r_test |= (r_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET;
+    n_test |= (n_test & SVN__LOWER_7BITS_SET) + SVN__LOWER_7BITS_SET;
 
     /* Check whether at least one of the words contains a byte <0x80
      * (if one is detected, there was a \r or \n in CHUNK). */
-    if ((r_test & n_test & BIT_7_SET) != BIT_7_SET)
+    if ((r_test & n_test & SVN__BIT_7_SET) != SVN__BIT_7_SET)
       break;
   }
 

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/error.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/error.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/error.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/error.c Sun Dec 23 18:34:14 2012
@@ -196,9 +196,14 @@ svn_error_wrap_apr(apr_status_t status,
       va_start(ap, fmt);
       msg = apr_pvsprintf(err->pool, fmt, ap);
       va_end(ap);
-      err->message = apr_psprintf(err->pool, "%s%s%s", msg,
-                                  (msg_apr) ? ": " : "",
-                                  (msg_apr) ? msg_apr : "");
+      if (msg_apr)
+        {
+          err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL);
+        }
+      else
+        {
+          err->message = msg;
+        }
     }
 
   return err;

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/gpg_agent.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/gpg_agent.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/gpg_agent.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/gpg_agent.c Sun Dec 23 18:34:14 2012
@@ -107,10 +107,10 @@ static svn_boolean_t
 receive_from_gpg_agent(int sd, char *buf, size_t n)
 {
   int i = 0;
-  int recvd;
+  size_t recvd;
   char c;
 
-  /* Clear existing buffer concent before reading response. */
+  /* Clear existing buffer content before reading response. */
   if (n > 0)
     *buf = '\0';
 
@@ -326,7 +326,6 @@ password_get_gpg_agent(svn_boolean_t *do
   display = getenv("DISPLAY");
   if (display != NULL)
     {
-      request = apr_psprintf(pool, "OPTION display=%s\n", display);
       if (!send_option(sd, buffer, BUFFER_SIZE, "display", display, pool))
         {
           close(sd);

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/io.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/io.c Sun Dec 23 18:34:14 2012
@@ -2058,8 +2058,12 @@ svn_io_file_lock2(const char *lock_file,
   if (locktype == APR_FLOCK_EXCLUSIVE)
     flags |= APR_WRITE;
 
+  /* locktype is never read after this block, so we don't need to bother
+     setting it.  If that were to ever change, uncomment the following
+     block. 
   if (nonblocking)
     locktype |= APR_FLOCK_NONBLOCK;
+  */
 
   SVN_ERR(svn_io_file_open(&lockfile_handle, lock_file, flags,
                            APR_OS_DEFAULT,
@@ -3427,30 +3431,60 @@ svn_error_t *
 svn_io_read_length_line(apr_file_t *file, char *buf, apr_size_t *limit,
                         apr_pool_t *pool)
 {
+  /* variables */
+  apr_size_t total_read = 0;
+  svn_boolean_t eof = FALSE;
   const char *name;
   svn_error_t *err;
-  apr_size_t i;
-  char c;
+  apr_size_t buf_size = *limit;
 
-  for (i = 0; i < *limit; i++)
+  while (buf_size > 0)
     {
-      SVN_ERR(svn_io_file_getc(&c, file, pool));
-      /* Note: this error could be APR_EOF, which
-         is totally fine.  The caller should be aware of
-         this. */
+      /* read a fair chunk of data at once. But don't get too ambitious
+       * as that would result in too much waste. Also make sure we can
+       * put a NUL after the last byte read.
+       */
+      apr_size_t to_read = buf_size < 129 ? buf_size - 1 : 128;
+      apr_size_t bytes_read = 0;
+      char *eol;
+
+      /* read data block (or just a part of it) */
+      SVN_ERR(svn_io_file_read_full2(file, buf, to_read,
+                                     &bytes_read, &eof, pool));
+
+      /* look or a newline char */
+      buf[bytes_read] = 0;
+      eol = strchr(buf, '\n');
+      if (eol)
+        {
+          apr_off_t offset = (eol + 1 - buf) - (apr_off_t)bytes_read;
+          
+          *eol = 0;
+          *limit = total_read + (eol - buf);
+
+          /* correct the file pointer:
+           * appear as though we just had read the newline char
+           */
+          SVN_ERR(svn_io_file_seek(file, APR_CUR, &offset, pool));
 
-      if (c == '\n')
-        {
-          buf[i] = '\0';
-          *limit = i;
           return SVN_NO_ERROR;
         }
-      else
+      else if (eof)
         {
-          buf[i] = c;
+          /* no EOL found but we hit the end of the file.
+           * Generate a nice EOF error object and return it.
+           */
+          char dummy;
+          SVN_ERR(svn_io_file_getc(&dummy, file, pool));
         }
+
+      /* next data chunk */
+      buf_size -= bytes_read;
+      buf += bytes_read;
+      total_read += bytes_read;
     }
 
+  /* buffer limit has been exceeded without finding the EOL */
   err = svn_io_file_name_get(&name, file, pool);
   if (err)
     name = NULL;

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/log.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/log.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/log.c Sun Dec 23 18:34:14 2012
@@ -380,3 +380,17 @@ svn_log__replay(const char *path, svn_re
     log_path = "/";
   return apr_psprintf(pool, "replay %s r%ld", log_path, rev);
 }
+
+const char *
+svn_log__get_inherited_props(const char *path,
+                             svn_revnum_t rev,
+                             apr_pool_t *pool)
+{
+  const char *log_path;
+
+  if (path && path[0] != '\0')
+    log_path = svn_path_uri_encode(path, pool);
+  else
+    log_path = "/";
+  return apr_psprintf(pool, "get-inherited-props %s r%ld", log_path, rev);
+}

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/mergeinfo.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/mergeinfo.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/mergeinfo.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/mergeinfo.c Sun Dec 23 18:34:14 2012
@@ -35,6 +35,7 @@
 #include "private/svn_fspath.h"
 #include "private/svn_mergeinfo_private.h"
 #include "private/svn_string_private.h"
+#include "private/svn_subr_private.h"
 #include "svn_private_config.h"
 #include "svn_hash.h"
 #include "private/svn_dep_compat.h"
@@ -616,6 +617,7 @@ parse_revision_line(const char **input, 
                     apr_pool_t *scratch_pool)
 {
   const char *pathname = "";
+  apr_ssize_t klen;
   svn_rangelist_t *existing_rangelist;
   svn_rangelist_t *rangelist = apr_array_make(scratch_pool, 1,
                                               sizeof(svn_merge_range_t *));
@@ -700,14 +702,14 @@ parse_revision_line(const char **input, 
      leading slash, e.g. "trunk:4033\n/trunk:4039-4995".  In the event
      we encounter this we merge the rangelists together under a single
      absolute path key. */
-  existing_rangelist = apr_hash_get(hash, pathname, APR_HASH_KEY_STRING);
+  klen = strlen(pathname);
+  existing_rangelist = apr_hash_get(hash, pathname, klen);
   if (existing_rangelist)
     SVN_ERR(svn_rangelist_merge2(rangelist, existing_rangelist,
                                  scratch_pool, scratch_pool));
 
-  apr_hash_set(hash, apr_pstrdup(apr_hash_pool_get(hash), pathname),
-               APR_HASH_KEY_STRING,
-               svn_rangelist_dup(rangelist, apr_hash_pool_get(hash)));
+  apr_hash_set(hash, apr_pstrmemdup(apr_hash_pool_get(hash), pathname, klen),
+               klen, svn_rangelist_dup(rangelist, apr_hash_pool_get(hash)));
 
   return SVN_NO_ERROR;
 }
@@ -736,7 +738,7 @@ svn_mergeinfo_parse(svn_mergeinfo_t *mer
 {
   svn_error_t *err;
 
-  *mergeinfo = apr_hash_make(pool);
+  *mergeinfo = svn_hash__make(pool);
   err = parse_top(&input, input + strlen(input), *mergeinfo, pool);
 
   /* Always return SVN_ERR_MERGEINFO_PARSE_ERROR as the topmost error. */
@@ -1559,29 +1561,28 @@ mergeinfo_hash_diff_cb(const void *key, 
     {
       /* Record any deltas (additions or deletions). */
       svn_rangelist_t *deleted_rangelist, *added_rangelist;
-      from_rangelist = apr_hash_get(cb->from, path, APR_HASH_KEY_STRING);
-      to_rangelist = apr_hash_get(cb->to, path, APR_HASH_KEY_STRING);
+      from_rangelist = apr_hash_get(cb->from, path, klen);
+      to_rangelist = apr_hash_get(cb->to, path, klen);
       SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
                                  from_rangelist, to_rangelist,
                                  cb->consider_inheritance, cb->pool));
       if (cb->deleted && deleted_rangelist->nelts > 0)
-        apr_hash_set(cb->deleted, apr_pstrdup(cb->pool, path),
-                     APR_HASH_KEY_STRING, deleted_rangelist);
+        apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen),
+                     klen, deleted_rangelist);
       if (cb->added && added_rangelist->nelts > 0)
-        apr_hash_set(cb->added, apr_pstrdup(cb->pool, path),
-                     APR_HASH_KEY_STRING, added_rangelist);
+        apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen),
+                     klen, added_rangelist);
     }
   else if ((status == svn_hash_diff_key_a) && cb->deleted)
     {
-      from_rangelist = apr_hash_get(cb->from, path, APR_HASH_KEY_STRING);
-      apr_hash_set(cb->deleted, apr_pstrdup(cb->pool, path),
-                   APR_HASH_KEY_STRING,
+      from_rangelist = apr_hash_get(cb->from, path, klen);
+      apr_hash_set(cb->deleted, apr_pstrmemdup(cb->pool, path, klen), klen,
                    svn_rangelist_dup(from_rangelist, cb->pool));
     }
   else if ((status == svn_hash_diff_key_b) && cb->added)
     {
-      to_rangelist = apr_hash_get(cb->to, path, APR_HASH_KEY_STRING);
-      apr_hash_set(cb->added, apr_pstrdup(cb->pool, path), APR_HASH_KEY_STRING,
+      to_rangelist = apr_hash_get(cb->to, path, klen);
+      apr_hash_set(cb->added, apr_pstrmemdup(cb->pool, path, klen), klen,
                    svn_rangelist_dup(to_rangelist, cb->pool));
     }
   return SVN_NO_ERROR;
@@ -1618,17 +1619,17 @@ svn_mergeinfo_diff2(svn_mergeinfo_t *del
   if (from && to == NULL)
     {
       *deleted = svn_mergeinfo_dup(from, result_pool);
-      *added = apr_hash_make(result_pool);
+      *added = svn_hash__make(result_pool);
     }
   else if (from == NULL && to)
     {
-      *deleted = apr_hash_make(result_pool);
+      *deleted = svn_hash__make(result_pool);
       *added = svn_mergeinfo_dup(to, result_pool);
     }
   else
     {
-      *deleted = apr_hash_make(result_pool);
-      *added = apr_hash_make(result_pool);
+      *deleted = svn_hash__make(result_pool);
+      *added = svn_hash__make(result_pool);
 
       if (from && to)
         {
@@ -1648,17 +1649,77 @@ svn_mergeinfo__equals(svn_boolean_t *is_
                       svn_boolean_t consider_inheritance,
                       apr_pool_t *pool)
 {
-  if (apr_hash_count(info1) == apr_hash_count(info2))
+  apr_hash_index_t *hi;
+
+  *is_equal = FALSE;
+
+  /* special cases: at least one side has no merge info */
+  if (info1 == NULL && info2 == NULL)
     {
-      svn_mergeinfo_t deleted, added;
-      SVN_ERR(svn_mergeinfo_diff2(&deleted, &added, info1, info2,
-                                  consider_inheritance, pool, pool));
-      *is_equal = apr_hash_count(deleted) == 0 && apr_hash_count(added) == 0;
+      *is_equal = TRUE;
+      return SVN_NO_ERROR;
     }
-  else
+
+  if (info1 == NULL ||  info2 == NULL)
+    return SVN_NO_ERROR;
+
+  /* trivial case: different number of paths -> unequal */
+  if (apr_hash_count(info1) != apr_hash_count(info2))
+    return SVN_NO_ERROR;
+
+  /* compare range lists for all paths */
+  for (hi = apr_hash_first(pool, info1); hi; hi = apr_hash_next(hi))
     {
-      *is_equal = FALSE;
+      const char *key;
+      apr_ssize_t key_length;
+      svn_rangelist_t *lhs, *rhs;
+      int i;
+      svn_rangelist_t *deleted, *added;
+
+      /* get both path lists */
+      apr_hash_this(hi, (const void**)&key, &key_length, (void **)&lhs);
+      rhs = apr_hash_get(info2, key, key_length);
+
+      /* missing on one side? */
+      if (rhs == NULL)
+        return SVN_NO_ERROR;
+
+      /* quick compare: the range lists will often be a perfect match */
+      if (lhs->nelts == rhs->nelts)
+        {
+          for (i = 0; i < lhs->nelts; ++i)
+            {
+              svn_merge_range_t *lrange
+                = APR_ARRAY_IDX(lhs, i, svn_merge_range_t *);
+              svn_merge_range_t *rrange
+                = APR_ARRAY_IDX(rhs, i, svn_merge_range_t *);
+
+              /* range mismatch? -> needs detailed comparison */
+              if (   lrange->start != rrange->start
+                  || lrange->end != rrange->end)
+                break;
+
+              /* inheritance mismatch? -> merge info differs */
+              if (   consider_inheritance
+                  && lrange->inheritable != rrange->inheritable)
+                return SVN_NO_ERROR;
+            }
+
+          /* all ranges found to match -> next path */
+          if (i == lhs->nelts)
+            continue;
+        }
+
+      /* range lists differ but there are many ways to sort and aggregate
+         revisions into ranges. Do a full diff on them. */
+      SVN_ERR(svn_rangelist_diff(&deleted, &added, lhs, rhs,
+                                  consider_inheritance, pool));
+      if (deleted->nelts || added->nelts)
+        return SVN_NO_ERROR;
     }
+
+  /* no mismatch found */
+  *is_equal = TRUE;
   return SVN_NO_ERROR;
 }
 
@@ -1668,62 +1729,37 @@ svn_mergeinfo_merge2(svn_mergeinfo_t mer
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 {
-  apr_array_header_t *sorted1, *sorted2;
-  int i, j;
+  apr_hash_index_t *hi;
   apr_pool_t *iterpool;
 
   if (!apr_hash_count(changes))
     return SVN_NO_ERROR;
 
-  sorted1 = svn_sort__hash(mergeinfo, svn_sort_compare_items_as_paths,
-                           scratch_pool);
-  sorted2 = svn_sort__hash(changes, svn_sort_compare_items_as_paths,
-                           scratch_pool);
-
-  i = 0;
-  j = 0;
   iterpool = svn_pool_create(scratch_pool);
-  while (i < sorted1->nelts && j < sorted2->nelts)
+  for (hi = apr_hash_first(scratch_pool, changes); hi; hi = apr_hash_next(hi))
     {
-      svn_sort__item_t elt1, elt2;
-      int res;
-
-      svn_pool_clear(iterpool);
-
-      elt1 = APR_ARRAY_IDX(sorted1, i, svn_sort__item_t);
-      elt2 = APR_ARRAY_IDX(sorted2, j, svn_sort__item_t);
-      res = svn_sort_compare_items_as_paths(&elt1, &elt2);
-
-      if (res == 0)
-        {
-          svn_rangelist_t *rl1, *rl2;
-
-          rl1 = elt1.value;
-          rl2 = elt2.value;
-
-          SVN_ERR(svn_rangelist_merge2(rl1, rl2, result_pool, iterpool));
-          apr_hash_set(mergeinfo, elt1.key, elt1.klen, rl1);
-          i++;
-          j++;
-        }
-      else if (res < 0)
-        {
-          i++;
+      const char *key;
+      apr_ssize_t klen;
+      svn_rangelist_t *to_insert;
+      svn_rangelist_t *target;
+
+      /* get ranges to insert and the target ranges list of that insertion */
+      apr_hash_this(hi, (const void**)&key, &klen, (void*)&to_insert);
+      target = apr_hash_get(mergeinfo, key, klen);
+
+      /* if range list exists, just expand on it.
+       * Otherwise, add new hash entry. */
+      if (target)
+        {
+          SVN_ERR(svn_rangelist_merge2(target, to_insert, result_pool,
+                                       iterpool));
+          apr_pool_clear(iterpool);
         }
       else
-        {
-          apr_hash_set(mergeinfo, elt2.key, elt2.klen, elt2.value);
-          j++;
-        }
+        apr_hash_set(mergeinfo, key, klen, to_insert);
     }
-  svn_pool_destroy(iterpool);
 
-  /* Copy back any remaining elements from the second hash. */
-  for (; j < sorted2->nelts; j++)
-    {
-      svn_sort__item_t elt = APR_ARRAY_IDX(sorted2, j, svn_sort__item_t);
-      apr_hash_set(mergeinfo, elt.key, elt.klen, elt.value);
-    }
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
@@ -1970,7 +2006,7 @@ svn_mergeinfo_catalog_dup(svn_mergeinfo_
 svn_mergeinfo_t
 svn_mergeinfo_dup(svn_mergeinfo_t mergeinfo, apr_pool_t *pool)
 {
-  svn_mergeinfo_t new_mergeinfo = apr_hash_make(pool);
+  svn_mergeinfo_t new_mergeinfo = svn_hash__make(pool);
   apr_hash_index_t *hi;
 
   for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
@@ -2161,36 +2197,6 @@ svn_mergeinfo__add_prefix_to_catalog(svn
 }
 
 svn_error_t *
-svn_mergeinfo__relpaths_to_urls(apr_hash_t **out_mergeinfo,
-                                svn_mergeinfo_t mergeinfo,
-                                const char *repos_root_url,
-                                apr_pool_t *result_pool,
-                                apr_pool_t *scratch_pool)
-{
-  *out_mergeinfo = NULL;
-  if (mergeinfo)
-    {
-      apr_hash_index_t *hi;
-      apr_hash_t *full_path_mergeinfo = apr_hash_make(result_pool);
-
-      for (hi = apr_hash_first(scratch_pool, mergeinfo);
-           hi; hi = apr_hash_next(hi))
-        {
-          const char *key = svn__apr_hash_index_key(hi);
-          void *val = svn__apr_hash_index_val(hi);
-
-          apr_hash_set(full_path_mergeinfo,
-                       svn_path_url_add_component2(repos_root_url, key + 1,
-                                                   result_pool),
-                       APR_HASH_KEY_STRING, val);
-        }
-      *out_mergeinfo = full_path_mergeinfo;
-    }
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
 svn_mergeinfo__add_suffix_to_mergeinfo(svn_mergeinfo_t *out_mergeinfo,
                                        svn_mergeinfo_t mergeinfo,
                                        const char *suffix_relpath,
@@ -2224,13 +2230,18 @@ svn_rangelist_dup(const svn_rangelist_t 
 {
   svn_rangelist_t *new_rl = apr_array_make(pool, rangelist->nelts,
                                            sizeof(svn_merge_range_t *));
+
+  /* allocate target range buffer with a single operation */
+  svn_merge_range_t *copy = apr_palloc(pool, sizeof(*copy) * rangelist->nelts);
   int i;
 
+  /* fill it iteratively and link it into the range list */
   for (i = 0; i < rangelist->nelts; i++)
     {
-      APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) =
-        svn_merge_range_dup(APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *),
-                            pool);
+      memcpy(copy + i,
+             APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *),
+             sizeof(*copy));
+      APR_ARRAY_PUSH(new_rl, svn_merge_range_t *) = copy + i;
     }
 
   return new_rl;
@@ -2318,38 +2329,6 @@ svn_mergeinfo__catalog_to_formatted_stri
 }
 
 svn_error_t *
-svn_mergeinfo__to_formatted_string(svn_string_t **output,
-                                   svn_mergeinfo_t mergeinfo,
-                                   const char *prefix,
-                                   apr_pool_t *pool)
-{
-  svn_stringbuf_t *output_buf = NULL;
-
-  if (mergeinfo && apr_hash_count(mergeinfo))
-    {
-      SVN_ERR(mergeinfo_to_stringbuf(&output_buf, mergeinfo,
-                                     prefix ? prefix : "", pool));
-      svn_stringbuf_appendcstr(output_buf, "\n");
-    }
-#if SVN_DEBUG
-  else if (!mergeinfo)
-    {
-      output_buf = svn_stringbuf_create(prefix ? prefix : "", pool);
-      svn_stringbuf_appendcstr(output_buf, _("NULL mergeinfo\n"));
-    }
-  else if (apr_hash_count(mergeinfo) == 0)
-    {
-      output_buf = svn_stringbuf_create(prefix ? prefix : "", pool);
-      svn_stringbuf_appendcstr(output_buf, _("empty mergeinfo\n"));
-    }
-#endif
-
-  *output = output_buf ? svn_stringbuf__morph_into_string(output_buf)
-                       : svn_string_create_empty(pool);
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
 svn_mergeinfo__get_range_endpoints(svn_revnum_t *youngest_rev,
                                    svn_revnum_t *oldest_rev,
                                    svn_mergeinfo_t mergeinfo,

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/nls.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/nls.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/nls.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/nls.c Sun Dec 23 18:34:14 2012
@@ -121,10 +121,12 @@ svn_nls_init(void)
 #else /* ! WIN32 */
       bindtextdomain(PACKAGE_NAME, SVN_LOCALE_DIR);
     }
+#endif /* WIN32 */
+
 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
   bind_textdomain_codeset(PACKAGE_NAME, "UTF-8");
 #endif /* HAVE_BIND_TEXTDOMAIN_CODESET */
-#endif /* WIN32 */
+
 #endif /* ENABLE_NLS */
 
   return err;

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/opt.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/opt.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/opt.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/opt.c Sun Dec 23 18:34:14 2012
@@ -49,7 +49,6 @@
 #include "private/svn_opt_private.h"
 
 #include "opt.h"
-#include "sysinfo.h"
 #include "svn_private_config.h"
 
 
@@ -1102,9 +1101,11 @@ svn_opt__arg_canonicalize_path(const cha
   return SVN_NO_ERROR;
 }
 
+
 svn_error_t *
 svn_opt__print_version_info(const char *pgm_name,
                             const char *footer,
+                            const svn_version_extended_t *info,
                             svn_boolean_t quiet,
                             svn_boolean_t verbose,
                             apr_pool_t *pool)
@@ -1114,16 +1115,11 @@ svn_opt__print_version_info(const char *
 
   SVN_ERR(svn_cmdline_printf(pool, _("%s, version %s\n"
                                      "   compiled %s, %s on %s\n\n"),
-                             pgm_name, SVN_VERSION, __DATE__, __TIME__,
-                             SVN_BUILD_HOST));
-  SVN_ERR(svn_cmdline_fputs(
-             _("Copyright (C) 2012 The Apache Software Foundation.\n"
-               "This software consists of contributions made by many "
-               "people; see the NOTICE\n"
-               "file for more information.\n"
-               "Subversion is open source software, see "
-               "http://subversion.apache.org/\n\n"),
-             stdout, pool));
+                             pgm_name, SVN_VERSION,
+                             svn_version_ext_build_date(info),
+                             svn_version_ext_build_time(info),
+                             svn_version_ext_build_host(info)));
+  SVN_ERR(svn_cmdline_printf(pool, "%s\n", svn_version_ext_copyright(info)));
 
   if (footer)
     {
@@ -1132,30 +1128,66 @@ svn_opt__print_version_info(const char *
 
   if (verbose)
     {
-      const char *const host = svn_sysinfo__canonical_host(pool);
-      const char *const relname = svn_sysinfo__release_name(pool);
-      const char *const dlibs = svn_sysinfo__loaded_libs(pool);
+      const apr_array_header_t *libs;
 
       SVN_ERR(svn_cmdline_fputs(_("System information:\n\n"), stdout, pool));
-      if (relname)
-        SVN_ERR(svn_cmdline_printf(pool, _("* running on %s\n"
-                                           "  - %s\n"),
-                                   host, relname));
-      else
-        SVN_ERR(svn_cmdline_printf(pool, _("* running on %s\n"), host));
+      SVN_ERR(svn_cmdline_printf(pool, _("* running on %s\n"),
+                                 svn_version_ext_runtime_host(info)));
+      if (svn_version_ext_runtime_osname(info))
+        {
+          SVN_ERR(svn_cmdline_printf(pool, _("  - %s\n"),
+                                     svn_version_ext_runtime_osname(info)));
+        }
 
-      if (dlibs)
+      libs = svn_version_ext_linked_libs(info);
+      if (libs && libs->nelts)
         {
+          const svn_version_ext_linked_lib_t *lib;
+          int i;
+
+          SVN_ERR(svn_cmdline_fputs(_("* linked dependencies:\n"),
+                                    stdout, pool));
+          for (i = 0; i < libs->nelts; ++i)
+            {
+              lib = &APR_ARRAY_IDX(libs, i, svn_version_ext_linked_lib_t);
+              if (lib->runtime_version)
+                SVN_ERR(svn_cmdline_printf(pool,
+                                           "  - %s %s (compiled with %s)\n",
+                                           lib->name,
+                                           lib->runtime_version,
+                                           lib->compiled_version));
+              else
+                SVN_ERR(svn_cmdline_printf(pool,
+                                           "  - %s %s (static)\n",
+                                           lib->name,
+                                           lib->compiled_version));
+            }
+        }
+
+      libs = svn_version_ext_loaded_libs(info);
+      if (libs && libs->nelts)
+        {
+          const svn_version_ext_loaded_lib_t *lib;
+          int i;
+
           SVN_ERR(svn_cmdline_fputs(_("* loaded shared libraries:\n"),
                                     stdout, pool));
-          SVN_ERR(svn_cmdline_fputs(dlibs, stdout, pool));
+          for (i = 0; i < libs->nelts; ++i)
+            {
+              lib = &APR_ARRAY_IDX(libs, i, svn_version_ext_loaded_lib_t);
+              if (lib->version)
+                SVN_ERR(svn_cmdline_printf(pool,
+                                           "  - %s   (%s)\n",
+                                           lib->name, lib->version));
+              else
+                SVN_ERR(svn_cmdline_printf(pool, "  - %s\n", lib->name));
+            }
         }
     }
 
   return SVN_NO_ERROR;
 }
 
-
 svn_error_t *
 svn_opt_print_help4(apr_getopt_t *os,
                     const char *pgm_name,
@@ -1187,8 +1219,11 @@ svn_opt_print_help4(apr_getopt_t *os,
         }
     }
   else if (print_version)   /* just --version */
-    SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer,
-                                        quiet, verbose, pool));
+    {
+      SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer,
+                                          svn_version_extended(verbose, pool),
+                                          quiet, verbose, pool));
+    }
   else if (os && !targets->nelts)            /* `-h', `--help', or `help' */
     svn_opt_print_generic_help2(header,
                                 cmd_table,

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/opt.h
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/opt.h?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/opt.h (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/opt.h Sun Dec 23 18:34:14 2012
@@ -24,15 +24,17 @@
 #ifndef SVN_LIBSVN_SUBR_OPT_H
 #define SVN_LIBSVN_SUBR_OPT_H
 
+#include "svn_version.h"
 #include "svn_opt.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
 
-/* Print version info for PGM_NAME.  If QUIET is  true, print in
- * brief.  Else if QUIET is not true, print the version more
- * verbosely, and if FOOTER is non-null, print it following the
+
+/* Print version version info for PGM_NAME to the console.  If QUIET is
+ * true, print in brief.  Else if QUIET is not true, print the version
+ * more verbosely, and if FOOTER is non-null, print it following the
  * version information. If VERBOSE is true, print running system info.
  *
  * Use POOL for temporary allocations.
@@ -40,6 +42,7 @@ extern "C" {
 svn_error_t *
 svn_opt__print_version_info(const char *pgm_name,
                             const char *footer,
+                            const svn_version_extended_t *info,
                             svn_boolean_t quiet,
                             svn_boolean_t verbose,
                             apr_pool_t *pool);

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/path.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/path.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/path.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/path.c Sun Dec 23 18:34:14 2012
@@ -1100,7 +1100,7 @@ svn_path_get_absolute(const char **pabso
 }
 
 
-
+#if !defined(WIN32) && !defined(DARWIN)
 /** Get APR's internal path encoding. */
 static svn_error_t *
 get_path_encoding(svn_boolean_t *path_is_utf8, apr_pool_t *pool)
@@ -1119,6 +1119,7 @@ get_path_encoding(svn_boolean_t *path_is
   *path_is_utf8 = (encoding_style == APR_FILEPATH_ENCODING_UTF8);
   return SVN_NO_ERROR;
 }
+#endif
 
 
 svn_error_t *

Modified: subversion/branches/javahl-ra/subversion/libsvn_subr/simple_providers.c
URL: http://svn.apache.org/viewvc/subversion/branches/javahl-ra/subversion/libsvn_subr/simple_providers.c?rev=1425508&r1=1425507&r2=1425508&view=diff
==============================================================================
--- subversion/branches/javahl-ra/subversion/libsvn_subr/simple_providers.c (original)
+++ subversion/branches/javahl-ra/subversion/libsvn_subr/simple_providers.c Sun Dec 23 18:34:14 2012
@@ -475,6 +475,10 @@ svn_auth__simple_creds_cache_set(svn_boo
   /* Save credentials to disk. */
   err = svn_config_write_auth_data(creds_hash, SVN_AUTH_CRED_SIMPLE,
                                    realmstring, config_dir, pool);
+  if (err)
+    *saved = FALSE;
+
+  /* ### return error? */
   svn_error_clear(err);
 
   return SVN_NO_ERROR;