You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2015/06/23 14:55:47 UTC

svn commit: r1687045 [11/31] - in /subversion/branches/svn-mergeinfo-normalizer: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ doc/ subversion/bindings/javahl/ subversion/bindings/javahl/native/ subversion/bindings/javahl/nativ...

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/dump.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/dump.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/dump.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/dump.c Tue Jun 23 12:55:43 2015
@@ -2334,7 +2334,7 @@ verify_one_revision(svn_fs_t *fs,
 }
 
 /* Baton type used for forwarding notifications from FS API to REPOS API. */
-struct verify_fs2_notify_func_baton_t
+struct verify_fs_notify_func_baton_t
 {
    /* notification function to call (must not be NULL) */
    svn_repos_notify_func_t notify_func;
@@ -2348,11 +2348,11 @@ struct verify_fs2_notify_func_baton_t
 
 /* Forward the notification to BATON. */
 static void
-verify_fs2_notify_func(svn_revnum_t revision,
+verify_fs_notify_func(svn_revnum_t revision,
                        void *baton,
                        apr_pool_t *pool)
 {
-  struct verify_fs2_notify_func_baton_t *notify_baton = baton;
+  struct verify_fs_notify_func_baton_t *notify_baton = baton;
 
   notify_baton->notify->revision = revision;
   notify_baton->notify_func(notify_baton->notify_baton,
@@ -2378,9 +2378,10 @@ svn_repos_verify_fs3(svn_repos_t *repos,
   apr_pool_t *iterpool = svn_pool_create(pool);
   svn_repos_notify_t *notify;
   svn_fs_progress_notify_func_t verify_notify = NULL;
-  struct verify_fs2_notify_func_baton_t *verify_notify_baton = NULL;
+  struct verify_fs_notify_func_baton_t *verify_notify_baton = NULL;
   svn_error_t *err;
-  svn_boolean_t found_corruption = FALSE;
+  svn_boolean_t failed_metadata = FALSE;
+  svn_revnum_t failed_revisions = 0;
 
   /* Determine the current youngest revision of the filesystem. */
   SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
@@ -2409,7 +2410,7 @@ svn_repos_verify_fs3(svn_repos_t *repos,
     {
       notify = svn_repos_notify_create(svn_repos_notify_verify_rev_end, pool);
 
-      verify_notify = verify_fs2_notify_func;
+      verify_notify = verify_fs_notify_func;
       verify_notify_baton = apr_palloc(pool, sizeof(*verify_notify_baton));
       verify_notify_baton->notify_func = notify_func;
       verify_notify_baton->notify_baton = notify_baton;
@@ -2423,34 +2424,26 @@ svn_repos_verify_fs3(svn_repos_t *repos,
                       verify_notify, verify_notify_baton,
                       cancel_func, cancel_baton, pool);
 
-  if (err)
+  if (err && err->apr_err == SVN_ERR_CANCELLED)
+    {
+      return svn_error_trace(err);
+    }
+  else if (err)
     {
-      if (err->apr_err == SVN_ERR_CANCELLED)
-        return svn_error_trace(err);
-
-      found_corruption = TRUE;
       notify_verification_error(SVN_INVALID_REVNUM, err, notify_func,
                                 notify_baton, iterpool);
 
-      /* If we already reported the error, reset it. */
-      if (notify_func)
+      if (!keep_going)
+        {
+          /* Return the error, the caller doesn't want us to continue. */
+          return svn_error_trace(err);
+        }
+      else
         {
+          /* Clear the error and keep going. */
+          failed_metadata = TRUE;
           svn_error_clear(err);
-          err = NULL;
         }
-
-      /* If we abort the verification now, combine yet unreported error
-         info with the generic one we return. */
-      if (!keep_going)
-        /* ### Jump to "We're done" and so send the final notification,
-               for consistency? */
-        return svn_error_createf(SVN_ERR_REPOS_CORRUPTED, err,
-                                _("Repository '%s' failed to verify"),
-                                svn_dirent_local_style(svn_repos_path(repos,
-                                                                      pool),
-                                                        pool));
-
-      svn_error_clear(err);
     }
 
   if (!metadata_only)
@@ -2464,24 +2457,30 @@ svn_repos_verify_fs3(svn_repos_t *repos,
                                   cancel_func, cancel_baton,
                                   iterpool);
 
-        if (err)
+        if (err && err->apr_err == SVN_ERR_CANCELLED)
+          {
+            return svn_error_trace(err);
+          }
+        else if (err)
           {
-            if (err->apr_err == SVN_ERR_CANCELLED)
-              return svn_error_trace(err);
-
-            found_corruption = TRUE;
             notify_verification_error(rev, err, notify_func, notify_baton,
                                       iterpool);
-            svn_error_clear(err);
 
-            if (keep_going)
-              continue;
+            if (!keep_going)
+              {
+                /* Return the error, the caller doesn't want us to continue. */
+                return svn_error_trace(err);
+              }
             else
-              break;
+              {
+                /* Clear the error and keep going. */
+                ++failed_revisions;
+                svn_error_clear(err);
+              }
           }
-
-        if (notify_func)
+        else if (notify_func)
           {
+            /* Tell the caller that we're done with this revision. */
             notify->revision = rev;
             notify_func(notify_baton, notify, iterpool);
           }
@@ -2496,12 +2495,40 @@ svn_repos_verify_fs3(svn_repos_t *repos,
 
   svn_pool_destroy(iterpool);
 
-  if (found_corruption)
-    return svn_error_createf(SVN_ERR_REPOS_CORRUPTED, NULL,
-                             _("Repository '%s' failed to verify"),
-                             svn_dirent_local_style(svn_repos_path(repos,
-                                                                   pool),
-                                                    pool));
+  /* Summarize the results. */
+  if (failed_metadata || 0 != failed_revisions)
+    {
+      const char *const repos_path =
+        svn_dirent_local_style(svn_repos_path(repos, pool), pool);
+
+      if (0 == failed_revisions)
+        {
+          return svn_error_createf(
+              SVN_ERR_REPOS_VERIFY_FAILED, NULL,
+              _("Metadata verification failed on repository '%s'"),
+              repos_path);
+        }
+      else
+        {
+          const char* format_string;
+
+          if (failed_metadata)
+            format_string = apr_psprintf(
+                pool, _("Verification of metadata and"
+                        " %%%s out of %%%s revisions"
+                        " failed on repository '%%s'"),
+                SVN_REVNUM_T_FMT, SVN_REVNUM_T_FMT);
+          else
+            format_string = apr_psprintf(
+                pool, _("Verification of %%%s out of %%%s revisions"
+                        " failed on repository '%%s'"),
+                SVN_REVNUM_T_FMT, SVN_REVNUM_T_FMT);
+
+          return svn_error_createf(
+              SVN_ERR_REPOS_VERIFY_FAILED, NULL, format_string,
+              failed_revisions, end_rev - start_rev + 1, repos_path);
+        }
+    }
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/replay.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/replay.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/replay.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/replay.c Tue Jun 23 12:55:43 2015
@@ -555,10 +555,6 @@ path_driver_cb_func(void **dir_baton,
         return svn_error_create(SVN_ERR_FS_ALREADY_EXISTS, NULL,
                                 _("Root directory already exists."));
 
-      /* A NULL parent_baton will cause a segfault.  It should never be
-          NULL for non-root paths. */
-      SVN_ERR_ASSERT(parent_baton);
-
       /* Was this node copied? */
       SVN_ERR(fill_copyfrom(&copyfrom_root, &copyfrom_path, &copyfrom_rev,
                             &src_readable, root, change,
@@ -971,6 +967,11 @@ svn_repos_replay2(svn_fs_root_t *root,
   const char *repos_root = "";
   void *unlock_baton;
 
+  /* If we were not given a low water mark, assume that everything is there,
+     all the way back to revision 0. */
+  if (! SVN_IS_VALID_REVNUM(low_water_mark))
+    low_water_mark = 0;
+
   /* Special-case r0, which we know is an empty revision; if we don't
      special-case it we might end up trying to compare it to "r-1". */
   if (svn_fs_is_revision_root(root)
@@ -1493,7 +1494,7 @@ svn_repos__replay_ev2(svn_fs_root_t *roo
   svn_error_t *err = SVN_NO_ERROR;
   int i;
 
-  SVN_ERR_ASSERT(!svn_dirent_is_absolute(base_repos_relpath));
+  SVN_ERR_ASSERT(svn_relpath_is_canonical(base_repos_relpath));
 
   /* Special-case r0, which we know is an empty revision; if we don't
      special-case it we might end up trying to compare it to "r-1". */

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/reporter.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/reporter.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/reporter.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/reporter.c Tue Jun 23 12:55:43 2015
@@ -1282,7 +1282,7 @@ delta_dirs(report_baton_t *b, svn_revnum
 
       /* Loop over the dirents in the target. */
       SVN_ERR(svn_fs_dir_optimal_order(&t_ordered_entries, b->t_root,
-                                       t_entries, subpool));
+                                       t_entries, subpool, iterpool));
       for (i = 0; i < t_ordered_entries->nelts; ++i)
         {
           const svn_fs_dirent_t *t_entry

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/repos.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/repos.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/repos.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/repos.c Tue Jun 23 12:55:43 2015
@@ -1635,9 +1635,10 @@ svn_repos_fs(svn_repos_t *repos)
 }
 
 const char *
-svn_repos_fs_type(svn_repos_t *repos, apr_pool_t *pool)
+svn_repos_fs_type(svn_repos_t *repos,
+                  apr_pool_t *result_pool)
 {
-  return apr_pstrdup(pool, repos->fs_type);
+  return apr_pstrdup(result_pool, repos->fs_type);
 }
 
 /* For historical reasons, for the Berkeley DB backend, this code uses
@@ -1957,7 +1958,7 @@ svn_repos_hotcopy3(const char *src_path,
                    void *notify_baton,
                    svn_cancel_func_t cancel_func,
                    void *cancel_baton,
-                   apr_pool_t *pool)
+                   apr_pool_t *scratch_pool)
 {
   svn_fs_hotcopy_notify_t fs_notify_func;
   struct fs_hotcopy_notify_baton_t fs_notify_baton;
@@ -1968,8 +1969,8 @@ svn_repos_hotcopy3(const char *src_path,
   svn_repos_t *dst_repos;
   svn_error_t *err;
 
-  SVN_ERR(svn_dirent_get_absolute(&src_abspath, src_path, pool));
-  SVN_ERR(svn_dirent_get_absolute(&dst_abspath, dst_path, pool));
+  SVN_ERR(svn_dirent_get_absolute(&src_abspath, src_path, scratch_pool));
+  SVN_ERR(svn_dirent_get_absolute(&dst_abspath, dst_path, scratch_pool));
   if (strcmp(src_abspath, dst_abspath) == 0)
     return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
                              _("Hotcopy source and destination are equal"));
@@ -1979,7 +1980,7 @@ svn_repos_hotcopy3(const char *src_path,
                     FALSE, FALSE,
                     FALSE,    /* don't try to open the db yet. */
                     NULL,
-                    pool, pool));
+                    scratch_pool, scratch_pool));
 
   /* If we are going to clean logs, then get an exclusive lock on
      db-logs.lock, to ensure that no one else will work with logs.
@@ -1987,7 +1988,7 @@ svn_repos_hotcopy3(const char *src_path,
      If we are just copying, then get a shared lock to ensure that
      no one else will clean logs while we copying them */
 
-  SVN_ERR(lock_db_logs_file(src_repos, clean_logs, pool));
+  SVN_ERR(lock_db_logs_file(src_repos, clean_logs, scratch_pool));
 
   /* Copy the repository to a new path, with exception of
      specially handled directories */
@@ -2001,16 +2002,16 @@ svn_repos_hotcopy3(const char *src_path,
                            0,
                            hotcopy_structure,
                            &hotcopy_context,
-                           pool));
+                           scratch_pool));
 
   /* Prepare dst_repos object so that we may create locks,
      so that we may open repository */
 
-  dst_repos = create_svn_repos_t(dst_abspath, pool);
+  dst_repos = create_svn_repos_t(dst_abspath, scratch_pool);
   dst_repos->fs_type = src_repos->fs_type;
   dst_repos->format = src_repos->format;
 
-  err = create_locks(dst_repos, pool);
+  err = create_locks(dst_repos, scratch_pool);
   if (err)
     {
       if (incremental && err->apr_err == SVN_ERR_DIR_NOT_EMPTY)
@@ -2019,7 +2020,8 @@ svn_repos_hotcopy3(const char *src_path,
         return svn_error_trace(err);
     }
 
-  err = svn_io_dir_make_sgid(dst_repos->db_path, APR_OS_DEFAULT, pool);
+  err = svn_io_dir_make_sgid(dst_repos->db_path, APR_OS_DEFAULT,
+                             scratch_pool);
   if (err)
     {
       if (incremental && APR_STATUS_IS_EEXIST(err->apr_err))
@@ -2030,7 +2032,7 @@ svn_repos_hotcopy3(const char *src_path,
 
   /* Exclusively lock the new repository.
      No one should be accessing it at the moment */
-  SVN_ERR(lock_repos(dst_repos, TRUE, FALSE, pool));
+  SVN_ERR(lock_repos(dst_repos, TRUE, FALSE, scratch_pool));
 
   fs_notify_func = notify_func ? fs_hotcopy_notify : NULL;
   fs_notify_baton.notify_func = notify_func;
@@ -2039,12 +2041,12 @@ svn_repos_hotcopy3(const char *src_path,
   SVN_ERR(svn_fs_hotcopy3(src_repos->db_path, dst_repos->db_path,
                           clean_logs, incremental,
                           fs_notify_func, &fs_notify_baton,
-                          cancel_func, cancel_baton, pool));
+                          cancel_func, cancel_baton, scratch_pool));
 
   /* Destination repository is ready.  Stamp it with a format number. */
   return svn_io_write_version_file
-          (svn_dirent_join(dst_repos->path, SVN_REPOS__FORMAT, pool),
-           dst_repos->format, pool);
+          (svn_dirent_join(dst_repos->path, SVN_REPOS__FORMAT, scratch_pool),
+           dst_repos->format, scratch_pool);
 }
 
 /* Return the library version number. */
@@ -2065,7 +2067,6 @@ svn_repos_stat(svn_dirent_t **dirent,
   svn_node_kind_t kind;
   svn_dirent_t *ent;
   const char *datestring;
-  apr_hash_t *prophash;
 
   SVN_ERR(svn_fs_check_path(&kind, root, path, pool));
 
@@ -2081,9 +2082,7 @@ svn_repos_stat(svn_dirent_t **dirent,
   if (kind == svn_node_file)
     SVN_ERR(svn_fs_file_length(&(ent->size), root, path, pool));
 
-  SVN_ERR(svn_fs_node_proplist(&prophash, root, path, pool));
-  if (apr_hash_count(prophash) > 0)
-    ent->has_props = TRUE;
+  SVN_ERR(svn_fs_node_has_props(&ent->has_props, root, path, pool));
 
   SVN_ERR(svn_repos_get_committed_info(&(ent->created_rev),
                                        &datestring,

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/rev_hunt.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/rev_hunt.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/rev_hunt.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_repos/rev_hunt.c Tue Jun 23 12:55:43 2015
@@ -310,9 +310,8 @@ svn_repos_deleted_rev(svn_fs_t *fs,
                       svn_revnum_t *deleted,
                       apr_pool_t *pool)
 {
-  apr_pool_t *subpool;
-  svn_fs_root_t *start_root, *root, *copy_root;
-  const char *copy_path;
+  apr_pool_t *iterpool;
+  svn_fs_root_t *start_root, *root;
   svn_revnum_t mid_rev;
   svn_node_kind_t kind;
   svn_fs_node_relation_t node_relation;
@@ -379,6 +378,8 @@ svn_repos_deleted_rev(svn_fs_t *fs,
                                    root, path, pool));
       if (node_relation != svn_fs_node_unrelated)
         {
+          svn_fs_root_t *copy_root;
+          const char *copy_path;
           SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, root,
                                       path, pool));
           if (!copy_root ||
@@ -432,15 +433,15 @@ svn_repos_deleted_rev(svn_fs_t *fs,
   */
 
   mid_rev = (start + end) / 2;
-  subpool = svn_pool_create(pool);
+  iterpool = svn_pool_create(pool);
 
   while (1)
     {
-      svn_pool_clear(subpool);
+      svn_pool_clear(iterpool);
 
       /* Get revision root and node id for mid_rev at that revision. */
-      SVN_ERR(svn_fs_revision_root(&root, fs, mid_rev, subpool));
-      SVN_ERR(svn_fs_check_path(&kind, root, path, pool));
+      SVN_ERR(svn_fs_revision_root(&root, fs, mid_rev, iterpool));
+      SVN_ERR(svn_fs_check_path(&kind, root, path, iterpool));
       if (kind == svn_node_none)
         {
           /* Case D: Look lower in the range. */
@@ -449,13 +450,15 @@ svn_repos_deleted_rev(svn_fs_t *fs,
         }
       else
         {
+          svn_fs_root_t *copy_root;
+          const char *copy_path;
           /* Determine the relationship between the start node
              and the current node. */
           SVN_ERR(svn_fs_node_relation(&node_relation, start_root, path,
-                                       root, path, pool));
+                                       root, path, iterpool));
           if (node_relation != svn_fs_node_unrelated)
-          SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, root,
-                                      path, subpool));
+            SVN_ERR(svn_fs_closest_copy(&copy_root, &copy_path, root,
+                                        path, iterpool));
           if (node_relation == svn_fs_node_unrelated ||
               (copy_root &&
                (svn_fs_revision_root_revision(copy_root) > start)))
@@ -479,7 +482,7 @@ svn_repos_deleted_rev(svn_fs_t *fs,
         }
     }
 
-  svn_pool_destroy(subpool);
+  svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
 
@@ -666,8 +669,7 @@ svn_repos_trace_node_locations(svn_fs_t
   /* First - let's sort the array of the revisions from the greatest revision
    * downward, so it will be easier to search on. */
   location_revisions = apr_array_copy(pool, location_revisions_orig);
-  qsort(location_revisions->elts, location_revisions->nelts,
-        sizeof(*revision_ptr), svn_sort_compare_revisions);
+  svn_sort__array(location_revisions, svn_sort_compare_revisions);
 
   revision_ptr = (svn_revnum_t *)location_revisions->elts;
   revision_ptr_end = revision_ptr + location_revisions->nelts;
@@ -835,27 +837,32 @@ svn_repos_node_location_segments(svn_rep
 {
   svn_fs_t *fs = svn_repos_fs(repos);
   svn_stringbuf_t *current_path;
-  svn_revnum_t youngest_rev = SVN_INVALID_REVNUM, current_rev;
+  svn_revnum_t youngest_rev, current_rev;
   apr_pool_t *subpool;
 
+  SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
+
   /* No PEG_REVISION?  We'll use HEAD. */
   if (! SVN_IS_VALID_REVNUM(peg_revision))
-    {
-      SVN_ERR(svn_fs_youngest_rev(&youngest_rev, fs, pool));
-      peg_revision = youngest_rev;
-    }
+    peg_revision = youngest_rev;
 
-  /* No START_REV?  We'll use HEAD (which we may have already fetched). */
+  if (peg_revision > youngest_rev)
+    return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+                             _("No such revision %ld"), peg_revision);
+
+  /* No START_REV?  We'll use peg rev. */
   if (! SVN_IS_VALID_REVNUM(start_rev))
-    {
-      if (SVN_IS_VALID_REVNUM(youngest_rev))
-        start_rev = youngest_rev;
-      else
-        SVN_ERR(svn_fs_youngest_rev(&start_rev, fs, pool));
-    }
+    start_rev = peg_revision;
+  else if (start_rev > peg_revision)
+    return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+                             _("No such revision %ld"), start_rev);
 
   /* No END_REV?  We'll use 0. */
-  end_rev = SVN_IS_VALID_REVNUM(end_rev) ? end_rev : 0;
+  if (! SVN_IS_VALID_REVNUM(end_rev))
+    end_rev = 0;
+  else if (end_rev > start_rev)
+    return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+                             _("No such revision %ld"), end_rev);
 
   /* Are the revision properly ordered?  They better be -- the API
      demands it. */
@@ -1329,6 +1336,7 @@ struct send_baton
   apr_hash_t *last_props;
   const char *last_path;
   svn_fs_root_t *last_root;
+  svn_boolean_t include_merged_revisions;
 };
 
 /* Send PATH_REV to HANDLER and HANDLER_BATON, using information provided by
@@ -1366,15 +1374,32 @@ send_path_revision(struct path_revision
   SVN_ERR(svn_prop_diffs(&prop_diffs, props, sb->last_props,
                          sb->iterpool));
 
-  /* Check if the contents *may* have changed. (Allow false positives,
-     for now, as the blame implementation currently depends on them.) */
-  /* Special case: In the first revision, we always provide a delta. */
-  if (sb->last_root)
-    SVN_ERR(svn_fs_contents_different(&contents_changed, sb->last_root,
-                                      sb->last_path, root, path_rev->path,
-                                      sb->iterpool));
+  /* Check if the contents *may* have changed. */
+  if (! sb->last_root)
+    {
+      /* Special case: In the first revision, we always provide a delta. */
+      contents_changed = TRUE;
+    }
+  else if (sb->include_merged_revisions
+           && strcmp(sb->last_path, path_rev->path))
+    {
+      /* ### This is a HACK!!!
+       * Blame -g, in older clients anyways, relies on getting a notification
+       * whenever the path changes - even if there was no content change.
+       *
+       * TODO: A future release should take an extra parameter and depending
+       * on that either always send a text delta or only send it if there
+       * is a difference. */
+      contents_changed = TRUE;
+    }
   else
-    contents_changed = TRUE;
+    {
+      /* Did the file contents actually change?
+       * It could e.g. be a property-only change. */
+      SVN_ERR(svn_fs_contents_different(&contents_changed, sb->last_root,
+                                        sb->last_path, root, path_rev->path,
+                                        sb->iterpool));
+    }
 
   /* We have all we need, give to the handler. */
   SVN_ERR(handler(handler_baton, path_rev->path, path_rev->revnum,
@@ -1443,6 +1468,7 @@ get_file_revs_backwards(svn_repos_t *rep
   last_pool = svn_pool_create(scratch_pool);
   sb.iterpool = svn_pool_create(scratch_pool);
   sb.last_pool = svn_pool_create(scratch_pool);
+  sb.include_merged_revisions = FALSE;
 
   /* We want the first txdelta to be against the empty file. */
   sb.last_root = NULL;
@@ -1598,6 +1624,9 @@ svn_repos_get_file_revs2(svn_repos_t *re
   /* Create an empty hash table for the first property diff. */
   sb.last_props = apr_hash_make(sb.last_pool);
 
+  /* Inform send_path_revision() whether workarounds / special behavior
+   * may be needed. */
+  sb.include_merged_revisions = include_merged_revisions;
 
   /* Get the revisions we are interested in. */
   duplicate_path_revs = apr_hash_make(scratch_pool);

Propchange: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Jun 23 12:55:43 2015
@@ -12,3 +12,4 @@ internal_statements.h
 errorcode.inc
 libsvn_subr.pc.in
 libsvn_subr.pc
+config_keys.inc

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/atomic.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/atomic.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/atomic.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/atomic.c Tue Jun 23 12:55:43 2015
@@ -20,6 +20,7 @@
  * ====================================================================
  */
 
+#include <assert.h>
 #include <apr_time.h>
 #include "private/svn_atomic.h"
 
@@ -29,11 +30,20 @@
 #define SVN_ATOMIC_INIT_FAILED   2
 #define SVN_ATOMIC_INITIALIZED   3
 
-svn_error_t*
-svn_atomic__init_once(volatile svn_atomic_t *global_status,
-                      svn_error_t *(*init_func)(void*,apr_pool_t*),
-                      void *baton,
-                      apr_pool_t* pool)
+
+/*
+ * This is the actual atomic initialization driver.  The caller must
+ * provide either ERR_INIT_FUNC and ERR_P, or STR_INIT_FUNC and
+ * ERRSTR_P, but never both.
+ */
+static void
+init_once(svn_atomic__err_init_func_t err_init_func,
+          svn_error_t **err_p,
+          svn_atomic__str_init_func_t str_init_func,
+          const char **errstr_p,
+          volatile svn_atomic_t *global_status,
+          void *baton,
+          apr_pool_t* pool)
 {
   /* !! Don't use localizable strings in this function, because these
      !! might cause deadlocks. This function can be used to initialize
@@ -42,44 +52,94 @@ svn_atomic__init_once(volatile svn_atomi
   /* We have to call init_func exactly once.  Because APR
      doesn't have statically-initialized mutexes, we implement a poor
      man's spinlock using svn_atomic_cas. */
+
+  svn_error_t *err = SVN_NO_ERROR;
+  const char *errstr = NULL;
   svn_atomic_t status = svn_atomic_cas(global_status,
                                        SVN_ATOMIC_START_INIT,
                                        SVN_ATOMIC_UNINITIALIZED);
 
-  if (status == SVN_ATOMIC_UNINITIALIZED)
+#ifdef SVN_DEBUG
+  /* Check that the parameters are valid. */
+  assert(!err_init_func != !str_init_func);
+  assert(!err_init_func == !err_p);
+  assert(!str_init_func == !errstr_p);
+#endif /* SVN_DEBUG */
+
+  for (;;)
     {
-      svn_error_t *err = init_func(baton, pool);
-      if (err)
+      switch (status)
         {
-#if APR_HAS_THREADS
-          /* Tell other threads that the initialization failed. */
-          svn_atomic_cas(global_status,
-                         SVN_ATOMIC_INIT_FAILED,
-                         SVN_ATOMIC_START_INIT);
-#endif
-          return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, err,
-                                  "Couldn't perform atomic initialization");
+        case SVN_ATOMIC_UNINITIALIZED:
+          if (err_init_func)
+            err = err_init_func(baton, pool);
+          else
+            errstr = str_init_func(baton);
+          if (err || errstr)
+            {
+              status = svn_atomic_cas(global_status,
+                                      SVN_ATOMIC_INIT_FAILED,
+                                      SVN_ATOMIC_START_INIT);
+            }
+          else
+            {
+              status = svn_atomic_cas(global_status,
+                                      SVN_ATOMIC_INITIALIZED,
+                                      SVN_ATOMIC_START_INIT);
+            }
+
+          /* Take another spin through the switch to report either
+             failure or success. */
+          continue;
+
+        case SVN_ATOMIC_START_INIT:
+          /* Wait for the init function to complete. */
+          apr_sleep(APR_USEC_PER_SEC / 1000);
+          status = svn_atomic_cas(global_status,
+                                  SVN_ATOMIC_UNINITIALIZED,
+                                  SVN_ATOMIC_UNINITIALIZED);
+          continue;
+
+        case SVN_ATOMIC_INIT_FAILED:
+          if (err_init_func)
+            *err_p = svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, err,
+                                      "Couldn't perform atomic initialization");
+          else
+            *errstr_p = errstr;
+          return;
+
+        case SVN_ATOMIC_INITIALIZED:
+          if (err_init_func)
+            *err_p = SVN_NO_ERROR;
+          else
+            *errstr_p = NULL;
+          return;
+
+        default:
+          /* Something went seriously wrong with the atomic operations. */
+          abort();
         }
-      svn_atomic_cas(global_status,
-                     SVN_ATOMIC_INITIALIZED,
-                     SVN_ATOMIC_START_INIT);
-    }
-#if APR_HAS_THREADS
-  /* Wait for whichever thread is performing initialization to finish. */
-  /* XXX FIXME: Should we have a maximum wait here, like we have in
-                the Windows file IO spinner? */
-  else while (status != SVN_ATOMIC_INITIALIZED)
-    {
-      if (status == SVN_ATOMIC_INIT_FAILED)
-        return svn_error_create(SVN_ERR_ATOMIC_INIT_FAILURE, NULL,
-                                "Couldn't perform atomic initialization");
-
-      apr_sleep(APR_USEC_PER_SEC / 1000);
-      status = svn_atomic_cas(global_status,
-                              SVN_ATOMIC_UNINITIALIZED,
-                              SVN_ATOMIC_UNINITIALIZED);
     }
-#endif /* APR_HAS_THREADS */
+}
+
 
-  return SVN_NO_ERROR;
+svn_error_t *
+svn_atomic__init_once(volatile svn_atomic_t *global_status,
+                      svn_atomic__err_init_func_t err_init_func,
+                      void *baton,
+                      apr_pool_t* pool)
+{
+  svn_error_t *err;
+  init_once(err_init_func, &err, NULL, NULL, global_status, baton, pool);
+  return err;
+}
+
+const char *
+svn_atomic__init_once_no_error(volatile svn_atomic_t *global_status,
+                               svn_atomic__str_init_func_t str_init_func,
+                               void *baton)
+{
+  const char *errstr;
+  init_once(NULL, NULL, str_init_func, &errstr, global_status, baton, NULL);
+  return errstr;
 }

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/auth.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/auth.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/auth.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/auth.c Tue Jun 23 12:55:43 2015
@@ -109,10 +109,10 @@ struct svn_auth_baton_t
 
   /* run-time parameters needed by providers. */
   apr_hash_t *parameters;
+  apr_hash_t *slave_parameters;
 
   /* run-time credentials cache. */
   apr_hash_t *creds_cache;
-
 };
 
 /* Abstracted iteration baton */
@@ -125,6 +125,7 @@ struct svn_auth_iterstate_t
   const char *realmstring;      /* The original realmstring passed in */
   const char *cache_key;        /* key to use in auth_baton's creds_cache */
   svn_auth_baton_t *auth_baton; /* the original auth_baton. */
+  apr_hash_t *parameters;
 };
 
 
@@ -142,6 +143,7 @@ svn_auth_open(svn_auth_baton_t **auth_ba
   ab = apr_pcalloc(pool, sizeof(*ab));
   ab->tables = apr_hash_make(pool);
   ab->parameters = apr_hash_make(pool);
+  /* ab->slave_parameters = NULL; */
   ab->creds_cache = apr_hash_make(pool);
   ab->pool = pool;
 
@@ -170,7 +172,8 @@ svn_auth_open(svn_auth_baton_t **auth_ba
   *auth_baton = ab;
 }
 
-
+/* Magic pointer value to allow storing 'NULL' in an apr_hash_t */
+static const void *auth_NULL = NULL;
 
 void
 svn_auth_set_parameter(svn_auth_baton_t *auth_baton,
@@ -178,17 +181,35 @@ svn_auth_set_parameter(svn_auth_baton_t
                        const void *value)
 {
   if (auth_baton)
-    svn_hash_sets(auth_baton->parameters, name, value);
+    {
+      if (auth_baton->slave_parameters)
+        {
+          if (!value)
+            value = &auth_NULL;
+
+          svn_hash_sets(auth_baton->slave_parameters, name, value);
+        }
+      else
+        svn_hash_sets(auth_baton->parameters, name, value);
+    }
 }
 
 const void *
 svn_auth_get_parameter(svn_auth_baton_t *auth_baton,
                        const char *name)
 {
-  if (auth_baton)
-    return svn_hash_gets(auth_baton->parameters, name);
-  else
+  const void *value;
+  if (!auth_baton)
     return NULL;
+  else if (!auth_baton->slave_parameters)
+    return svn_hash_gets(auth_baton->parameters, name);
+
+  value = svn_hash_gets(auth_baton->slave_parameters, name);
+
+  if (value)
+    return (value == &auth_NULL ? NULL : value);
+
+  return svn_hash_gets(auth_baton->parameters, name);
 }
 
 
@@ -218,6 +239,7 @@ svn_auth_first_credentials(void **creden
   svn_boolean_t got_first = FALSE;
   svn_auth_iterstate_t *iterstate;
   const char *cache_key;
+  apr_hash_t *parameters;
 
   if (! auth_baton)
     return svn_error_create(SVN_ERR_AUTHN_NO_PROVIDER, NULL,
@@ -230,6 +252,26 @@ svn_auth_first_credentials(void **creden
                              _("No provider registered for '%s' credentials"),
                              cred_kind);
 
+  if (auth_baton->slave_parameters)
+    {
+      apr_hash_index_t *hi;
+      parameters = apr_hash_copy(pool, auth_baton->parameters);
+
+      for (hi = apr_hash_first(pool, auth_baton->slave_parameters);
+            hi;
+            hi = apr_hash_next(hi))
+        {
+          const void *value = apr_hash_this_val(hi);
+
+          if (value == &auth_NULL)
+            value = NULL;
+
+          svn_hash_sets(parameters, apr_hash_this_key(hi), value);
+        }
+    }
+  else
+    parameters = auth_baton->parameters;
+
   /* First, see if we have cached creds in the auth_baton. */
   cache_key = make_cache_key(cred_kind, realmstring, pool);
   creds = svn_hash_gets(auth_baton->creds_cache, cache_key);
@@ -247,7 +289,7 @@ svn_auth_first_credentials(void **creden
                                    svn_auth_provider_object_t *);
           SVN_ERR(provider->vtable->first_credentials(&creds, &iter_baton,
                                                       provider->provider_baton,
-                                                      auth_baton->parameters,
+                                                      parameters,
                                                       realmstring,
                                                       auth_baton->pool));
 
@@ -274,6 +316,7 @@ svn_auth_first_credentials(void **creden
       iterstate->realmstring = apr_pstrdup(pool, realmstring);
       iterstate->cache_key = cache_key;
       iterstate->auth_baton = auth_baton;
+      iterstate->parameters = parameters;
       *state = iterstate;
 
       /* Put the creds in the cache */
@@ -310,7 +353,7 @@ svn_auth_next_credentials(void **credent
         {
           SVN_ERR(provider->vtable->first_credentials(
                       &creds, &(state->provider_iter_baton),
-                      provider->provider_baton, auth_baton->parameters,
+                      provider->provider_baton, state->parameters,
                       state->realmstring, auth_baton->pool));
           state->got_first = TRUE;
         }
@@ -319,7 +362,7 @@ svn_auth_next_credentials(void **credent
           SVN_ERR(provider->vtable->next_credentials(&creds,
                                                      state->provider_iter_baton,
                                                      provider->provider_baton,
-                                                     auth_baton->parameters,
+                                                     state->parameters,
                                                      state->realmstring,
                                                      auth_baton->pool));
         }
@@ -348,19 +391,17 @@ svn_auth_save_credentials(svn_auth_iters
   svn_auth_provider_object_t *provider;
   svn_boolean_t save_succeeded = FALSE;
   const char *no_auth_cache;
-  svn_auth_baton_t *auth_baton;
   void *creds;
 
   if (! state || state->table->providers->nelts <= state->provider_idx)
     return SVN_NO_ERROR;
 
-  auth_baton = state->auth_baton;
   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 = svn_hash_gets(auth_baton->parameters,
+  no_auth_cache = svn_hash_gets(state->parameters,
                                 SVN_AUTH_PARAM_NO_AUTH_CACHE);
   if (no_auth_cache)
     return SVN_NO_ERROR;
@@ -373,7 +414,7 @@ svn_auth_save_credentials(svn_auth_iters
     SVN_ERR(provider->vtable->save_credentials(&save_succeeded,
                                                creds,
                                                provider->provider_baton,
-                                               auth_baton->parameters,
+                                               state->parameters,
                                                state->realmstring,
                                                pool));
   if (save_succeeded)
@@ -389,7 +430,7 @@ svn_auth_save_credentials(svn_auth_iters
       if (provider->vtable->save_credentials)
         SVN_ERR(provider->vtable->save_credentials(&save_succeeded, creds,
                                                    provider->provider_baton,
-                                                   auth_baton->parameters,
+                                                   state->parameters,
                                                    state->realmstring,
                                                    pool));
 
@@ -687,10 +728,12 @@ svn_auth_get_platform_specific_client_pr
 }
 
 svn_error_t *
-svn_auth__apply_config_for_server(svn_auth_baton_t *auth_baton,
-                                  apr_hash_t *config,
-                                  const char *server_name,
-                                  apr_pool_t *scratch_pool)
+svn_auth__make_session_auth(svn_auth_baton_t **session_auth_baton,
+                            const svn_auth_baton_t *auth_baton,
+                            apr_hash_t *config,
+                            const char *server_name,
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
 {
   svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS;
   svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS;
@@ -702,6 +745,12 @@ svn_auth__apply_config_for_server(svn_au
   svn_config_t *servers = NULL;
   const char *server_group = NULL;
 
+  struct svn_auth_baton_t *ab;
+
+  ab = apr_pmemdup(result_pool, auth_baton, sizeof(*ab));
+
+  ab->slave_parameters = apr_hash_make(result_pool);
+
   /* The 'store-passwords' and 'store-auth-creds' parameters used to
   * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility,
   * if values for these parameters have already been set by our
@@ -716,12 +765,10 @@ svn_auth__apply_config_for_server(svn_au
   * "store-auth-creds = yes" -- they'll get the expected behaviour.
   */
 
-  if (svn_auth_get_parameter(auth_baton,
-                              SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL)
+  if (svn_auth_get_parameter(ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL)
     store_passwords = FALSE;
 
-  if (svn_auth_get_parameter(auth_baton,
-                              SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL)
+  if (svn_auth_get_parameter(ab, SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL)
     store_auth_creds = FALSE;
 
   /* All the svn_auth_set_parameter() calls below this not only affect the
@@ -806,46 +853,30 @@ svn_auth__apply_config_for_server(svn_au
 
   /* Save auth caching parameters in the auth parameter hash. */
   if (! store_passwords)
-    svn_auth_set_parameter(auth_baton,
-                           SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
+    svn_auth_set_parameter(ab, SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, "");
 
-  svn_auth_set_parameter(auth_baton,
+  svn_auth_set_parameter(ab,
                          SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS,
                          store_plaintext_passwords);
 
   if (! store_pp)
-    svn_auth_set_parameter(auth_baton,
+    svn_auth_set_parameter(ab,
                            SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP,
                            "");
 
-  svn_auth_set_parameter(auth_baton,
+  svn_auth_set_parameter(ab,
                          SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
                          store_pp_plaintext);
 
   if (! store_auth_creds)
-    svn_auth_set_parameter(auth_baton,
-                            SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
+    svn_auth_set_parameter(ab, SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
 
-  /* ### This setting may have huge side-effects when the auth baton is shared
-     ### between different ra sessions, as it will change which server settings
-     ### will be used for all future auth requests.
-     ###
-     ### E.g. when you connect using a ssl client cert that is specified in the
-     ### config file, you might have it when you first connect... but if you
-     ### then connect to another repository, you might not see the same
-     ### settings when the SSL connection is built up again later on.
-     ###
-     ### Most current usages should probably have been keyed on the realm
-     ### string instead of this magic flag that changes when multiple repositories
-     ### are used.
-     ###
-     ### This especially affects long living ra sessions, such as those on the
-     ### reuse-ra-session branch.
-   */
   if (server_group)
-    svn_auth_set_parameter(auth_baton,
+    svn_auth_set_parameter(ab,
                            SVN_AUTH_PARAM_SERVER_GROUP,
-                           apr_pstrdup(auth_baton->pool, server_group));
+                           apr_pstrdup(ab->pool, server_group));
+
+  *session_auth_baton = ab;
 
   return SVN_NO_ERROR;
 }

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/cache-inprocess.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/cache-inprocess.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/cache-inprocess.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/cache-inprocess.c Tue Jun 23 12:55:43 2015
@@ -23,8 +23,6 @@
 
 #include <assert.h>
 
-#include <apr_thread_mutex.h>
-
 #include "svn_pools.h"
 
 #include "svn_private_config.h"

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/cache-membuffer.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/cache-membuffer.c Tue Jun 23 12:55:43 2015
@@ -28,13 +28,16 @@
 #include "svn_pools.h"
 #include "svn_checksum.h"
 #include "svn_private_config.h"
-#include "cache.h"
 #include "svn_string.h"
 #include "svn_sorts.h"  /* get the MIN macro */
+
 #include "private/svn_atomic.h"
 #include "private/svn_dep_compat.h"
 #include "private/svn_mutex.h"
-#include "private/svn_pseudo_md5.h"
+#include "private/svn_string_private.h"
+
+#include "cache.h"
+#include "fnv1a.h"
 
 /*
  * This svn_cache__t implementation actually consists of two parts:
@@ -45,8 +48,9 @@
  * A membuffer cache consists of two parts:
  *
  * 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.
+ *    representation, prefixed by their full cache keys. 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
@@ -78,9 +82,10 @@
  * 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.
+ * the new item (plus its full key), 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,
  * L2 uses a randomized variant of LFU (see ensure_data_insertable_l2 for
@@ -104,11 +109,13 @@
  * an already used group to extend it.
  *
  * To limit the entry size and management overhead, not the actual item keys
- * 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
- * unlikely.
+ * but only their hashed "fingerprint" will be stored.  These are reasonably
+ * unique to prevent collisions, so we only need to support up to one entry
+ * per entry key.  To guarantee that there are no conflicts, however, we
+ * store the actual full key immediately in front of the serialized item
+ * data.  That is, the entry offset actually points to the full key and the
+ * key length stored in the entry acts as an additional offset to find the
+ * actual item.
  *
  * All access to the cached data needs to be serialized. Because we want
  * to scale well despite that bottleneck, we simply segment the cache into
@@ -178,17 +185,34 @@
  */
 #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.
+/* We use this structure to identify cache entries. There cannot be two
+ * entries with the same entry key. However unlikely, though, two different
+ * full keys (see full_key_t) may have the same entry key.  That is a
+ * collision and at most one of them can be stored in the cache at any time.
  */
-typedef apr_uint64_t entry_key_t[2];
+typedef struct entry_key_t
+{
+  /* 16 byte finger print of the full key. */
+  apr_uint64_t fingerprint[2];
 
-/* The prefix passed to svn_cache__create_membuffer_cache() effectively
- * defines the type of all items stored by that cache instance. We'll take
- * the last 15 bytes + \0 as plaintext for easy identification by the dev.
+  /* Length of the full key.  This value is aligned to ITEM_ALIGNMENT to
+   * make sure the subsequent item content is properly aligned. */
+  apr_size_t key_len;
+} entry_key_t;
+
+/* A full key, i.e. the combination of the cache's key prefix with some
+ * dynamic part appended to it.  It also contains its ENTRY_KEY.
  */
-#define PREFIX_TAIL_LEN 16
+typedef struct full_key_t
+{
+  /* Reduced form identifying the cache entry (if such an entry exists). */
+  entry_key_t entry_key;
+
+  /* This contains the full combination.  Note that the SIZE element may
+   * be larger than ENTRY_KEY.KEY_LEN, but only the latter determines the
+   * valid key size. */
+  svn_membuf_t full_key;
+} full_key_t;
 
 /* Debugging / corruption detection support.
  * If you define this macro, the getter functions will performed expensive
@@ -198,6 +222,12 @@ typedef apr_uint64_t entry_key_t[2];
  */
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
+/* The prefix passed to svn_cache__create_membuffer_cache() effectively
+ * defines the type of all items stored by that cache instance. We'll take
+ * the last 15 bytes + \0 as plaintext for easy identification by the dev.
+ */
+#define PREFIX_TAIL_LEN 16
+
 /* This record will be attached to any cache entry. It tracks item data
  * (content), key and type as hash values and is the baseline against which
  * the getters will compare their results to detect inconsistencies.
@@ -233,22 +263,33 @@ typedef struct entry_tag_t
 /* Initialize all members of TAG except for the content hash.
  */
 static svn_error_t *store_key_part(entry_tag_t *tag,
-                                   entry_key_t prefix_hash,
-                                   char *prefix_tail,
+                                   const full_key_t *prefix_key,
                                    const void *key,
                                    apr_size_t key_len,
                                    apr_pool_t *pool)
 {
   svn_checksum_t *checksum;
+  const char *prefix = prefix_key->full_key.data;
+  apr_size_t prefix_len = strlen(prefix);
+
+  if (prefix_len > sizeof(tag->prefix_tail))
+    {
+      prefix += prefix_len - (sizeof(tag->prefix_tail) - 1);
+      prefix_len = sizeof(tag->prefix_tail) - 1;
+    }
+
   SVN_ERR(svn_checksum(&checksum,
                        svn_checksum_md5,
                        key,
                        key_len,
                        pool));
 
-  memcpy(tag->prefix_hash, prefix_hash, sizeof(tag->prefix_hash));
+  memcpy(tag->prefix_hash, prefix_key->entry_key.fingerprint,
+         sizeof(tag->prefix_hash));
   memcpy(tag->key_hash, checksum->digest, sizeof(tag->key_hash));
-  memcpy(tag->prefix_tail, prefix_tail, sizeof(tag->prefix_tail));
+
+  memset(tag->prefix_tail, 0, sizeof(tag->key_hash));
+  memcpy(tag->prefix_tail, prefix, prefix_len + 1);
 
   tag->key_len = key_len;
 
@@ -258,7 +299,7 @@ static svn_error_t *store_key_part(entry
 /* Initialize the content hash member of TAG.
  */
 static svn_error_t* store_content_part(entry_tag_t *tag,
-                                       const char *data,
+                                       const void *data,
                                        apr_size_t size,
                                        apr_pool_t *pool)
 {
@@ -305,8 +346,7 @@ static svn_error_t* assert_equal_tags(co
   entry_tag_t *tag = &_tag;                                      \
   if (key)                                                       \
     SVN_ERR(store_key_part(tag,                                  \
-                           cache->prefix,                        \
-                           cache->info_prefix,                   \
+                           &cache->prefix,                       \
                            key,                                  \
                            cache->key_len == APR_HASH_KEY_STRING \
                                ? strlen((const char *) key)      \
@@ -323,23 +363,6 @@ static svn_error_t* assert_equal_tags(co
 
 #endif /* SVN_DEBUG_CACHE_MEMBUFFER */
 
-/* Per svn_cache_t instance initialization helper.
- * Copy the last to up PREFIX_TAIL_LEN-1 chars from PREFIX to PREFIX_TAIL.
- * If the prefix has been structured by ':', only store the last element
- * (which will tell us the type).
- */
-static void get_prefix_tail(const char *prefix, char *prefix_tail)
-{
-  apr_size_t len = strlen(prefix);
-  apr_size_t to_copy = MIN(len, PREFIX_TAIL_LEN - 1);
-  const char *last_colon = strrchr(prefix, ':');
-  apr_size_t last_element_pos = last_colon ? 0 : last_colon - prefix + 1;
-
-  to_copy = MIN(to_copy, len - last_element_pos);
-  memset(prefix_tail, 0, PREFIX_TAIL_LEN);
-  memcpy(prefix_tail, prefix + len - to_copy, to_copy);
-}
-
 /* 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
@@ -360,7 +383,7 @@ typedef struct entry_t
    * above ensures that there will be no overflows.
    * Only valid for used entries.
    */
-  apr_uint32_t size;
+  apr_size_t size;
 
   /* Number of (read) hits for this entry. Will be reset upon write.
    * Only valid for used entries.
@@ -1122,17 +1145,19 @@ insert_entry(svn_membuffer_t *cache, ent
  */
 static apr_uint32_t
 get_group_index(svn_membuffer_t **cache,
-                entry_key_t key)
+                const entry_key_t *key)
 {
   svn_membuffer_t *segment0 = *cache;
+  apr_uint64_t key0 = key->fingerprint[0];
+  apr_uint64_t key1 = key->fingerprint[1];
 
   /* 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 modulus is a prime larger than the largest
    * counts. */
-  *cache = &segment0[(key[1] % APR_UINT64_C(2809637) + (key[0] / 37))
+  *cache = &segment0[(key1 % APR_UINT64_C(2809637) + (key0 / 37))
                      & (segment0->segment_count - 1)];
-  return (key[0] % APR_UINT64_C(5030895599)) % segment0->group_count;
+  return (key0 % APR_UINT64_C(5030895599)) % segment0->group_count;
 }
 
 /* Reduce the hit count of ENTRY and update the accumulated hit info
@@ -1153,6 +1178,17 @@ let_entry_age(svn_membuffer_t *cache, en
     }
 }
 
+/* Return whether the keys in LHS and RHS match.
+ */
+static svn_boolean_t
+entry_keys_match(const entry_key_t *lhs,
+                 const entry_key_t *rhs)
+{
+  return (lhs->fingerprint[0] == rhs->fingerprint[0])
+      && (lhs->fingerprint[1] == rhs->fingerprint[1])
+      && (lhs->key_len == rhs->key_len);
+}
+
 /* Given the GROUP_INDEX that shall contain an entry with the hash key
  * TO_FIND, find that entry in the specified group.
  *
@@ -1164,11 +1200,15 @@ let_entry_age(svn_membuffer_t *cache, en
  * new content), an unused entry or a forcibly removed entry (if all
  * group entries are currently in use). The entries' hash value will be
  * initialized with TO_FIND.
+ *
+ * Note: This function requires the caller to appropriately lock the CACHE.
+ * For FIND_EMPTY==FALSE, a read lock is required, for FIND_EMPTY==TRUE,
+ * the write lock must have been acquired.
  */
 static entry_t *
 find_entry(svn_membuffer_t *cache,
            apr_uint32_t group_index,
-           const apr_uint64_t to_find[2],
+           const full_key_t *to_find,
            svn_boolean_t find_empty)
 {
   entry_group_t *group;
@@ -1189,8 +1229,7 @@ find_entry(svn_membuffer_t *cache,
           entry = &group->entries[0];
 
           /* initialize entry for the new key */
-          entry->key[0] = to_find[0];
-          entry->key[1] = to_find[1];
+          entry->key = to_find->entry_key;
         }
 
       return entry;
@@ -1201,14 +1240,28 @@ find_entry(svn_membuffer_t *cache,
   while (1)
     {
       for (i = 0; i < group->header.used; ++i)
-        if (   to_find[0] == group->entries[i].key[0]
-            && to_find[1] == group->entries[i].key[1])
+        if (entry_keys_match(&group->entries[i].key, &to_find->entry_key))
           {
-            /* found it
-             */
+            /* This is the only entry that _may_ contain the correct data. */
             entry = &group->entries[i];
+
+            /* If we want to preserve it, check that it is actual a match. */
             if (!find_empty)
-              return entry;
+              {
+                /* If there is no full key to compare, we are done. */
+                if (!entry->key.key_len)
+                  return entry;
+
+                /* Compare the full key. */
+                if (memcmp(to_find->full_key.data,
+                           cache->data + entry->offset,
+                           entry->key.key_len) == 0)
+                  return entry;
+
+                /* Key conflict. The entry to find cannot be anywhere else.
+                 * Therefore, it is not cached. */
+                return NULL;
+              }
 
             /* need to empty that entry */
             drop_entry(cache, entry);
@@ -1218,6 +1271,8 @@ find_entry(svn_membuffer_t *cache,
               group = last_group_in_chain(cache,
                                           &cache->directory[group_index]);
 
+            /* No entry found (actually, none left to find). */
+            entry = NULL;
             break;
           }
 
@@ -1300,8 +1355,7 @@ find_entry(svn_membuffer_t *cache,
       /* initialize entry for the new key
        */
       entry = &group->entries[group->header.used];
-      entry->key[0] = to_find[0];
-      entry->key[1] = to_find[1];
+      entry->key = to_find->entry_key;
     }
 
   return entry;
@@ -1883,7 +1937,7 @@ svn_cache__membuffer_clear(svn_membuffer
 static svn_error_t *
 entry_exists_internal(svn_membuffer_t *cache,
                       apr_uint32_t group_index,
-                      entry_key_t to_find,
+                      const full_key_t *to_find,
                       svn_boolean_t *found)
 {
   *found = find_entry(cache, group_index, to_find, FALSE) != NULL;
@@ -1896,7 +1950,7 @@ entry_exists_internal(svn_membuffer_t *c
 static svn_error_t *
 entry_exists(svn_membuffer_t *cache,
              apr_uint32_t group_index,
-             entry_key_t to_find,
+             const full_key_t *to_find,
              svn_boolean_t *found)
 {
   WITH_READ_LOCK(cache,
@@ -1929,9 +1983,9 @@ select_level(svn_membuffer_t *cache,
            && priority > SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY)
     {
       /* Large but important items go into L2. */
-      entry_t dummy_entry = { { 0 } };
+      entry_t dummy_entry = { { { 0 } } };
       dummy_entry.priority = priority;
-      dummy_entry.size = (apr_uint32_t) size;
+      dummy_entry.size = size;
 
       return ensure_data_insertable_l2(cache, &dummy_entry)
            ? &cache->l2
@@ -1942,9 +1996,9 @@ select_level(svn_membuffer_t *cache,
   return NULL;
 }
 
-/* Try to insert the serialized item given in BUFFER with SIZE into
- * the group GROUP_INDEX of CACHE and uniquely identify it by hash
- * value TO_FIND.
+/* Try to insert the serialized item given in BUFFER with ITEM_SIZE
+ * into the group GROUP_INDEX of CACHE and uniquely identify it by
+ * hash value TO_FIND.
  *
  * However, there is no guarantee that it will actually be put into
  * the cache. If there is already some data associated with TO_FIND,
@@ -1952,19 +2006,20 @@ select_level(svn_membuffer_t *cache,
  * be inserted.
  *
  * Note: This function requires the caller to serialization access.
- * Don't call it directly, call membuffer_cache_get_partial instead.
+ * Don't call it directly, call membuffer_cache_set instead.
  */
 static svn_error_t *
 membuffer_cache_set_internal(svn_membuffer_t *cache,
-                             entry_key_t to_find,
+                             const full_key_t *to_find,
                              apr_uint32_t group_index,
                              char *buffer,
-                             apr_size_t size,
+                             apr_size_t item_size,
                              apr_uint32_t priority,
                              DEBUG_CACHE_MEMBUFFER_TAG_ARG
                              apr_pool_t *scratch_pool)
 {
   cache_level_t *level;
+  apr_size_t size = item_size + to_find->entry_key.key_len;
 
   /* first, look for a previous entry for the given key */
   entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
@@ -1978,20 +2033,24 @@ membuffer_cache_set_internal(svn_membuff
        * negative value.
        */
       cache->data_used += (apr_uint64_t)size - entry->size;
-      entry->size = (apr_uint32_t) size;
+      entry->size = size;
       entry->priority = priority;
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
       /* Remember original content, type and key (hashes)
        */
-      SVN_ERR(store_content_part(tag, buffer, size, scratch_pool));
+      SVN_ERR(store_content_part(tag, buffer, item_size, scratch_pool));
       memcpy(&entry->tag, tag, sizeof(*tag));
 
 #endif
 
-      if (size)
-        memcpy(cache->data + entry->offset, buffer, size);
+      if (entry->key.key_len)
+        memcpy(cache->data + entry->offset, to_find->full_key.data,
+               entry->key.key_len);
+      if (item_size)
+        memcpy(cache->data + entry->offset + entry->key.key_len, buffer,
+               item_size);
 
       cache->total_writes++;
       return SVN_NO_ERROR;
@@ -2007,7 +2066,7 @@ membuffer_cache_set_internal(svn_membuff
        * the serialized item's (future) position within data buffer.
        */
       entry = find_entry(cache, group_index, to_find, TRUE);
-      entry->size = (apr_uint32_t) size;
+      entry->size = size;
       entry->offset = level->current_data;
       entry->priority = priority;
 
@@ -2015,7 +2074,7 @@ membuffer_cache_set_internal(svn_membuff
 
       /* Remember original content, type and key (hashes)
        */
-      SVN_ERR(store_content_part(tag, buffer, size, scratch_pool));
+      SVN_ERR(store_content_part(tag, buffer, item_size, scratch_pool));
       memcpy(&entry->tag, tag, sizeof(*tag));
 
 #endif
@@ -2026,8 +2085,12 @@ membuffer_cache_set_internal(svn_membuff
 
       /* Copy the serialized item data into the cache.
        */
-      if (size)
-        memcpy(cache->data + entry->offset, buffer, size);
+      if (entry->key.key_len)
+        memcpy(cache->data + entry->offset, to_find->full_key.data,
+               entry->key.key_len);
+      if (item_size)
+        memcpy(cache->data + entry->offset + entry->key.key_len, buffer,
+               item_size);
 
       cache->total_writes++;
     }
@@ -2056,7 +2119,7 @@ membuffer_cache_set_internal(svn_membuff
  */
 static svn_error_t *
 membuffer_cache_set(svn_membuffer_t *cache,
-                    entry_key_t key,
+                    const full_key_t *key,
                     void *item,
                     svn_cache__serialize_func_t serializer,
                     apr_uint32_t priority,
@@ -2069,7 +2132,7 @@ membuffer_cache_set(svn_membuffer_t *cac
 
   /* find the entry group that will hold the key.
    */
-  group_index = get_group_index(&cache, key);
+  group_index = get_group_index(&cache, &key->entry_key);
 
   /* Serialize data data.
    */
@@ -2112,12 +2175,12 @@ increment_hit_counters(svn_membuffer_t *
  * be done in POOL.
  *
  * Note: This function requires the caller to serialization access.
- * Don't call it directly, call membuffer_cache_get_partial instead.
+ * Don't call it directly, call membuffer_cache_get instead.
  */
 static svn_error_t *
 membuffer_cache_get_internal(svn_membuffer_t *cache,
                              apr_uint32_t group_index,
-                             entry_key_t to_find,
+                             const full_key_t *to_find,
                              char **buffer,
                              apr_size_t *item_size,
                              DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -2140,9 +2203,9 @@ membuffer_cache_get_internal(svn_membuff
       return SVN_NO_ERROR;
     }
 
-  size = ALIGN_VALUE(entry->size);
+  size = ALIGN_VALUE(entry->size) - entry->key.key_len;
   *buffer = ALIGN_POINTER(apr_palloc(result_pool, size + ITEM_ALIGNMENT-1));
-  memcpy(*buffer, (const char*)cache->data + entry->offset, size);
+  memcpy(*buffer, cache->data + entry->offset + entry->key.key_len, size);
 
 #ifdef SVN_DEBUG_CACHE_MEMBUFFER
 
@@ -2154,7 +2217,8 @@ 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 - entry->key.key_len,
+                             result_pool));
   SVN_ERR(assert_equal_tags(&entry->tag, tag));
 
 #endif
@@ -2162,7 +2226,7 @@ membuffer_cache_get_internal(svn_membuff
   /* update hit statistics
    */
   increment_hit_counters(cache, entry);
-  *item_size = entry->size;
+  *item_size = entry->size - entry->key.key_len;
 
   return SVN_NO_ERROR;
 }
@@ -2174,7 +2238,7 @@ membuffer_cache_get_internal(svn_membuff
  */
 static svn_error_t *
 membuffer_cache_get(svn_membuffer_t *cache,
-                    entry_key_t key,
+                    const full_key_t *key,
                     void **item,
                     svn_cache__deserialize_func_t deserializer,
                     DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -2186,7 +2250,7 @@ membuffer_cache_get(svn_membuffer_t *cac
 
   /* find the entry group that will hold the key.
    */
-  group_index = get_group_index(&cache, key);
+  group_index = get_group_index(&cache, &key->entry_key);
   WITH_READ_LOCK(cache,
                  membuffer_cache_get_internal(cache,
                                               group_index,
@@ -2214,7 +2278,7 @@ membuffer_cache_get(svn_membuffer_t *cac
 static svn_error_t *
 membuffer_cache_has_key_internal(svn_membuffer_t *cache,
                                  apr_uint32_t group_index,
-                                 entry_key_t to_find,
+                                 const full_key_t *to_find,
                                  svn_boolean_t *found)
 {
   entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
@@ -2243,12 +2307,12 @@ membuffer_cache_has_key_internal(svn_mem
  */
 static svn_error_t *
 membuffer_cache_has_key(svn_membuffer_t *cache,
-                        entry_key_t key,
+                        const full_key_t *key,
                         svn_boolean_t *found)
 {
   /* find the entry group that will hold the key.
    */
-  apr_uint32_t group_index = get_group_index(&cache, key);
+  apr_uint32_t group_index = get_group_index(&cache, &key->entry_key);
   cache->total_reads++;
 
   WITH_READ_LOCK(cache,
@@ -2274,7 +2338,7 @@ membuffer_cache_has_key(svn_membuffer_t
 static svn_error_t *
 membuffer_cache_get_partial_internal(svn_membuffer_t *cache,
                                      apr_uint32_t group_index,
-                                     entry_key_t to_find,
+                                     const full_key_t *to_find,
                                      void **item,
                                      svn_boolean_t *found,
                                      svn_cache__partial_getter_func_t deserializer,
@@ -2293,6 +2357,8 @@ membuffer_cache_get_partial_internal(svn
     }
   else
     {
+      const void *item_data = cache->data + entry->offset + entry->key.key_len;
+      apr_size_t item_size = entry->size - entry->key.key_len;
       *found = TRUE;
       increment_hit_counters(cache, entry);
 
@@ -2306,19 +2372,12 @@ membuffer_cache_get_partial_internal(svn
 
       /* Compare original content, type and key (hashes)
        */
-      SVN_ERR(store_content_part(tag,
-                                 (const char*)cache->data + entry->offset,
-                                 entry->size,
-                                 result_pool));
+      SVN_ERR(store_content_part(tag, item_data, item_size, result_pool));
       SVN_ERR(assert_equal_tags(&entry->tag, tag));
 
 #endif
 
-      return deserializer(item,
-                          (const char*)cache->data + entry->offset,
-                          entry->size,
-                          baton,
-                          result_pool);
+      return deserializer(item, item_data, item_size, baton, result_pool);
     }
 }
 
@@ -2330,7 +2389,7 @@ membuffer_cache_get_partial_internal(svn
  */
 static svn_error_t *
 membuffer_cache_get_partial(svn_membuffer_t *cache,
-                            entry_key_t key,
+                            const full_key_t *key,
                             void **item,
                             svn_boolean_t *found,
                             svn_cache__partial_getter_func_t deserializer,
@@ -2338,7 +2397,7 @@ membuffer_cache_get_partial(svn_membuffe
                             DEBUG_CACHE_MEMBUFFER_TAG_ARG
                             apr_pool_t *result_pool)
 {
-  apr_uint32_t group_index = get_group_index(&cache, key);
+  apr_uint32_t group_index = get_group_index(&cache, &key->entry_key);
 
   WITH_READ_LOCK(cache,
                  membuffer_cache_get_partial_internal
@@ -2362,7 +2421,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,
-                                     entry_key_t to_find,
+                                     const full_key_t *to_find,
                                      svn_cache__partial_setter_func_t func,
                                      void *baton,
                                      DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -2380,9 +2439,10 @@ membuffer_cache_set_partial_internal(svn
       svn_error_t *err;
 
       /* access the serialized cache item */
-      char *data = (char*)cache->data + entry->offset;
-      char *orig_data = data;
-      apr_size_t size = entry->size;
+      apr_size_t key_len = entry->key.key_len;
+      void *item_data = cache->data + entry->offset + key_len;
+      void *orig_data = item_data;
+      apr_size_t item_size = entry->size - key_len;
 
       increment_hit_counters(cache, entry);
       cache->total_writes++;
@@ -2392,19 +2452,19 @@ membuffer_cache_set_partial_internal(svn
       /* Check for overlapping entries.
        */
       SVN_ERR_ASSERT(entry->next == NO_INDEX ||
-                     entry->offset + size
+                     entry->offset + entry->size
                         <= get_entry(cache, entry->next)->offset);
 
       /* Compare original content, type and key (hashes)
        */
-      SVN_ERR(store_content_part(tag, data, size, scratch_pool));
+      SVN_ERR(store_content_part(tag, item_data, item_size, scratch_pool));
       SVN_ERR(assert_equal_tags(&entry->tag, tag));
 
 #endif
 
       /* modify it, preferably in-situ.
        */
-      err = func((void **)&data, &size, baton, scratch_pool);
+      err = func(&item_data, &item_size, baton, scratch_pool);
 
       if (err)
         {
@@ -2421,21 +2481,26 @@ membuffer_cache_set_partial_internal(svn
           /* if the modification caused a re-allocation, we need to remove
            * the old entry and to copy the new data back into cache.
            */
-          if (data != orig_data)
+          if (item_data != orig_data)
             {
               /* Remove the old entry and try to make space for the new one.
                */
               drop_entry(cache, entry);
-              if (   (cache->max_entry_size >= size)
-                  && ensure_data_insertable_l1(cache, size))
+              if (   (cache->max_entry_size >= item_size + key_len)
+                  && ensure_data_insertable_l1(cache, item_size + key_len))
                 {
                   /* Write the new entry.
                    */
                   entry = find_entry(cache, group_index, to_find, TRUE);
-                  entry->size = (apr_uint32_t) size;
+                  entry->size = item_size + key_len;
                   entry->offset = cache->l1.current_data;
-                  if (size)
-                    memcpy(cache->data + entry->offset, data, size);
+
+                  if (key_len)
+                    memcpy(cache->data + entry->offset,
+                           to_find->full_key.data, key_len);
+                  if (item_size)
+                    memcpy(cache->data + entry->offset + key_len, item_data,
+                           item_size);
 
                   /* Link the entry properly.
                    */
@@ -2447,7 +2512,7 @@ membuffer_cache_set_partial_internal(svn
 
           /* Remember original content, type and key (hashes)
            */
-          SVN_ERR(store_content_part(tag, data, size, scratch_pool));
+          SVN_ERR(store_content_part(tag, item_data, item_size, scratch_pool));
           memcpy(&entry->tag, tag, sizeof(*tag));
 
 #endif
@@ -2464,7 +2529,7 @@ membuffer_cache_set_partial_internal(svn
  */
 static svn_error_t *
 membuffer_cache_set_partial(svn_membuffer_t *cache,
-                            entry_key_t key,
+                            const full_key_t *key,
                             svn_cache__partial_setter_func_t func,
                             void *baton,
                             DEBUG_CACHE_MEMBUFFER_TAG_ARG
@@ -2472,7 +2537,7 @@ membuffer_cache_set_partial(svn_membuffe
 {
   /* cache item lookup
    */
-  apr_uint32_t group_index = get_group_index(&cache, key);
+  apr_uint32_t group_index = get_group_index(&cache, &key->entry_key);
   WITH_WRITE_LOCK(cache,
                   membuffer_cache_set_partial_internal
                      (cache, group_index, key, func, baton,
@@ -2498,22 +2563,6 @@ 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_ssize_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.
@@ -2533,15 +2582,10 @@ typedef struct svn_membuffer_cache_t
   svn_cache__deserialize_func_t deserializer;
 
   /* Prepend this byte sequence to any key passed to us.
-   * This makes (very likely) our keys different from all keys used
-   * by other svn_membuffer_cache_t instances.
+   * This makes our keys different from all keys used by svn_membuffer_cache_t
+   * instances that we don't want to share cached data with.
    */
-  entry_key_t prefix;
-
-  /* The tail of the prefix string. It is being used as a developer-visible
-   * ID for this cache instance.
-   */
-  char info_prefix[PREFIX_TAIL_LEN];
+  full_key_t prefix;
 
   /* length of the keys that will be passed to us through the
    * svn_cache_t interface. May be APR_HASH_KEY_STRING.
@@ -2553,23 +2597,13 @@ typedef struct svn_membuffer_cache_t
 
   /* Temporary buffer containing the hash key for the current access
    */
-  entry_key_t combined_key;
-
-  /* cache for the last key used.
-   * Will be NULL for caches with short fix-sized keys.
-   */
-  last_access_key_t *last_access;
+  full_key_t combined_key;
 
   /* if enabled, this will serialize the access to this instance.
    */
   svn_mutex__t *mutex;
 } svn_membuffer_cache_t;
 
-/* After an estimated ALLOCATIONS_PER_POOL_CLEAR allocations, we should
- * clear the svn_membuffer_cache_t.pool to keep memory consumption in check.
- */
-#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
@@ -2580,70 +2614,35 @@ combine_long_key(svn_membuffer_cache_t *
                  const void *key,
                  apr_ssize_t key_len)
 {
-  assert(cache->last_access);
+  apr_uint32_t *digest_buffer;
+  char *key_copy;
+  apr_size_t prefix_len = cache->prefix.entry_key.key_len;
+  apr_size_t aligned_key_len;
 
   /* handle variable-length keys */
   if (key_len == APR_HASH_KEY_STRING)
     key_len = strlen((const char *) key);
 
-  /* 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)
-    {
-      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];
-
-      /* 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
-    {
-      /* 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_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);
-        }
+  aligned_key_len = ALIGN_VALUE(key_len);
 
-      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));
-    }
+  /* Combine keys. */
+  svn_membuf__ensure(&cache->combined_key.full_key,
+                     aligned_key_len + prefix_len);
+
+  key_copy = (char *)cache->combined_key.full_key.data + prefix_len;
+  cache->combined_key.entry_key.key_len = aligned_key_len + prefix_len;
+  memcpy(key_copy, key, key_len);
+  memset(key_copy + key_len, 0, aligned_key_len - key_len);
+
+  /* Hash key into 16 bytes. */
+  digest_buffer = (apr_uint32_t *)cache->combined_key.entry_key.fingerprint;
+  svn__fnv1a_32x4_raw(digest_buffer, key, key_len);
+
+  /* Combine with prefix. */
+  cache->combined_key.entry_key.fingerprint[0]
+    ^= cache->prefix.entry_key.fingerprint[0];
+  cache->combined_key.entry_key.fingerprint[1]
+    ^= cache->prefix.entry_key.fingerprint[1];
 }
 
 /* Basically calculate a hash value for KEY of length KEY_LEN, combine it
@@ -2654,42 +2653,47 @@ 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)
+  if (key_len != APR_HASH_KEY_STRING && key_len <= 16)
     {
+      const apr_size_t prefix_len = cache->prefix.entry_key.key_len;
+
+      /* Copy of *key, padded with 0.
+       * We put it just behind the prefix already copied into the COMBINED_KEY.
+       * The buffer space has been allocated when the cache was created. */
+      apr_uint64_t *data = (void *)((char *)cache->combined_key.full_key.data + 
+                                    prefix_len);
+      assert(prefix_len <= cache->combined_key.full_key.size - 16);
+      cache->combined_key.entry_key.key_len = prefix_len + 16;
+
       data[0] = 0;
       data[1] = 0;
       memcpy(data, key, key_len);
+
+      /* Scramble key DATA to spread the key space more evenly across the
+       * cache segments and entry buckets.  All of this shall be reversible
+       * to prevent key collisions.  So, we limit ourselves to xor and
+       * permutations.
+       *
+       * As long as we compare the full combined key, the additional
+       * fingerprint collisions introduced by a non-reversible scramble
+       * would simply reduce the cache effectiveness.
+       */
+      data[1] = (data[1] << 27) | (data[1] >> 37);
+      data[1] ^= data[0] & 0xffff;
+      data[0] ^= data[1] & APR_UINT64_C(0xffffffffffff0000);
+
+      /* combine with this cache's namespace */
+      cache->combined_key.entry_key.fingerprint[0]
+        = data[0] ^ cache->prefix.entry_key.fingerprint[0];
+      cache->combined_key.entry_key.fingerprint[1]
+        = data[1] ^ cache->prefix.entry_key.fingerprint[1];
     }
   else
     {
       /* longer or variably sized keys */
       combine_long_key(cache, key, key_len);
-      return;
     }
-
-  /* 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] & APR_UINT64_C(0xffffffffffff0000);
-
-  /* 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)
@@ -2721,7 +2725,7 @@ svn_membuffer_cache_get(void **value_p,
 
   /* Look the item up. */
   SVN_ERR(membuffer_cache_get(cache->membuffer,
-                              cache->combined_key,
+                              &cache->combined_key,
                               value_p,
                               cache->deserializer,
                               DEBUG_CACHE_MEMBUFFER_TAG
@@ -2758,7 +2762,7 @@ svn_membuffer_cache_has_key(svn_boolean_
 
   /* Look the item up. */
   SVN_ERR(membuffer_cache_has_key(cache->membuffer,
-                                  cache->combined_key,
+                                  &cache->combined_key,
                                   found));
 
   /* return result */
@@ -2790,7 +2794,7 @@ svn_membuffer_cache_set(void *cache_void
    * that the item will actually be cached afterwards.
    */
   return membuffer_cache_set(cache->membuffer,
-                             cache->combined_key,
+                             &cache->combined_key,
                              value,
                              cache->serializer,
                              cache->priority,
@@ -2836,7 +2840,7 @@ svn_membuffer_cache_get_partial(void **v
 
   combine_key(cache, key, cache->key_len);
   SVN_ERR(membuffer_cache_get_partial(cache->membuffer,
-                                      cache->combined_key,
+                                      &cache->combined_key,
                                       value_p,
                                       found,
                                       func,
@@ -2864,7 +2868,7 @@ svn_membuffer_cache_set_partial(void *ca
     {
       combine_key(cache, key, cache->key_len);
       SVN_ERR(membuffer_cache_set_partial(cache->membuffer,
-                                          cache->combined_key,
+                                          &cache->combined_key,
                                           func,
                                           baton,
                                           DEBUG_CACHE_MEMBUFFER_TAG
@@ -2936,7 +2940,7 @@ svn_membuffer_cache_get_info(void *cache
 
   /* cache front-end specific data */
 
-  info->id = apr_pstrdup(result_pool, cache->info_prefix);
+  info->id = apr_pstrdup(result_pool, cache->prefix.full_key.data);
 
   /* collect info from shared cache back-end */
 
@@ -3129,11 +3133,12 @@ svn_cache__create_membuffer_cache(svn_ca
                                   apr_pool_t *scratch_pool)
 {
   svn_checksum_t *checksum;
+  apr_size_t prefix_len, prefix_orig_len;
 
   /* allocate the cache header structures
    */
   svn_cache__t *wrapper = apr_pcalloc(result_pool, sizeof(*wrapper));
-  svn_membuffer_cache_t *cache = apr_palloc(result_pool, sizeof(*cache));
+  svn_membuffer_cache_t *cache = apr_pcalloc(result_pool, sizeof(*cache));
 
   /* initialize our internal cache header
    */
@@ -3144,33 +3149,38 @@ svn_cache__create_membuffer_cache(svn_ca
   cache->deserializer = deserializer
                       ? deserializer
                       : deserialize_svn_stringbuf;
-  get_prefix_tail(prefix, cache->info_prefix);
   cache->priority = priority;
   cache->key_len = klen;
 
   SVN_ERR(svn_mutex__init(&cache->mutex, thread_safe, result_pool));
 
-  /* for performance reasons, we don't actually store the full prefix but a
-   * hash value of it
-   */
+  /* Copy the prefix into the prefix full key. Align it to ITEM_ALIGMENT.
+   * Don't forget to include the terminating NUL. */
+  prefix_orig_len = strlen(prefix) + 1;
+  prefix_len = ALIGN_VALUE(prefix_orig_len);
+
+  svn_membuf__create(&cache->prefix.full_key, prefix_len, result_pool);
+  memcpy((char *)cache->prefix.full_key.data, prefix, prefix_orig_len);
+  memset((char *)cache->prefix.full_key.data + prefix_orig_len, 0,
+         prefix_len - prefix_orig_len);
+
+  /* Construct the folded prefix key. */
   SVN_ERR(svn_checksum(&checksum,
                        svn_checksum_md5,
                        prefix,
                        strlen(prefix),
                        scratch_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(result_pool, sizeof(*cache->last_access));
-      cache->last_access->key_len = APR_HASH_KEY_STRING;
-    }
-  else
-    {
-      cache->last_access = NULL;
-    }
+  memcpy(cache->prefix.entry_key.fingerprint, checksum->digest,
+         sizeof(cache->prefix.entry_key.fingerprint));
+  cache->prefix.entry_key.key_len = prefix_len;
+
+  /* Initialize the combined key. Pre-allocate some extra room in the full
+   * key such that we probably don't need to re-alloc. */
+  cache->combined_key.entry_key = cache->prefix.entry_key;
+  svn_membuf__create(&cache->combined_key.full_key, prefix_len + 200,
+                     result_pool);
+  memcpy(cache->combined_key.full_key.data, cache->prefix.full_key.data,
+         prefix_len);
 
   /* initialize the generic cache wrapper
    */

Modified: subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/checksum.c
URL: http://svn.apache.org/viewvc/subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/checksum.c?rev=1687045&r1=1687044&r2=1687045&view=diff
==============================================================================
--- subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/checksum.c (original)
+++ subversion/branches/svn-mergeinfo-normalizer/subversion/libsvn_subr/checksum.c Tue Jun 23 12:55:43 2015
@@ -80,6 +80,14 @@ static const apr_size_t digest_sizes[] =
   sizeof(apr_uint32_t)
 };
 
+/* Checksum type prefixes used in serialized checksums. */
+static const char *ckind_str[] = {
+  "$md5 $",
+  "$sha1$",
+  "$fnv1$",
+  "$fnvm$",
+};
+
 /* Returns the digest size of it's argument. */
 #define DIGESTSIZE(k) \
   (((k) < svn_checksum_md5 || (k) > svn_checksum_fnv1a_32x4) ? 0 : digest_sizes[k])
@@ -317,13 +325,10 @@ svn_checksum_serialize(const svn_checksu
                        apr_pool_t *result_pool,
                        apr_pool_t *scratch_pool)
 {
-  const char *ckind_str;
-
   SVN_ERR_ASSERT_NO_RETURN(checksum->kind >= svn_checksum_md5
                            || checksum->kind <= svn_checksum_fnv1a_32x4);
-  ckind_str = (checksum->kind == svn_checksum_md5 ? "$md5 $" : "$sha1$");
   return apr_pstrcat(result_pool,
-                     ckind_str,
+                     ckind_str[checksum->kind],
                      svn_checksum_to_cstring(checksum, scratch_pool),
                      SVN_VA_NULL);
 }
@@ -335,18 +340,29 @@ svn_checksum_deserialize(const svn_check
                          apr_pool_t *result_pool,
                          apr_pool_t *scratch_pool)
 {
-  svn_checksum_kind_t ckind;
+  svn_checksum_kind_t kind;
   svn_checksum_t *parsed_checksum;
 
-  /* "$md5 $..." or "$sha1$..." */
-  SVN_ERR_ASSERT(strlen(data) > 6);
+  /* All prefixes have the same length. */
+  apr_size_t prefix_len = strlen(ckind_str[0]);
 
-  ckind = (data[1] == 'm' ? svn_checksum_md5 : svn_checksum_sha1);
-  SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, ckind,
-                                 data + 6, result_pool));
-  *checksum = parsed_checksum;
+  /* "$md5 $...", "$sha1$..." or ... */
+  if (strlen(data) <= prefix_len)
+    return svn_error_createf(SVN_ERR_BAD_CHECKSUM_PARSE, NULL,
+                             _("Invalid prefix in checksum '%s'"),
+                             data);
+
+  for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
+    if (strncmp(ckind_str[kind], data, prefix_len) == 0)
+      {
+        SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, kind,
+                                       data + prefix_len, result_pool));
+        *checksum = parsed_checksum;
+        return SVN_NO_ERROR;
+      }
 
-  return SVN_NO_ERROR;
+  return svn_error_createf(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
+                           "Unknown checksum kind in '%s'", data);
 }