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 2013/10/16 01:44:46 UTC

svn commit: r1532597 [6/10] - in /subversion/branches/log-addressing: ./ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ notes/ subversion/bindings/javahl/native/ subversion/bindings/javahl/src/org/a...

Modified: subversion/branches/log-addressing/subversion/libsvn_ra_svn/ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_ra_svn/ra_svn.h?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_ra_svn/ra_svn.h (original)
+++ subversion/branches/log-addressing/subversion/libsvn_ra_svn/ra_svn.h Tue Oct 15 23:44:41 2013
@@ -127,6 +127,7 @@ struct svn_ra_svn__session_baton_t {
   const char *user;
   const char *hostname; /* The remote hostname. */
   const char *realm_prefix;
+  const char *tunnel_name;
   const char **tunnel_argv;
   const svn_ra_callbacks2_t *callbacks;
   void *callbacks_baton;

Modified: subversion/branches/log-addressing/subversion/libsvn_repos/authz.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_repos/authz.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_repos/authz.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_repos/authz.c Tue Oct 15 23:44:41 2013
@@ -854,23 +854,9 @@ authz_retrieve_config_repo(svn_config_t 
   return SVN_NO_ERROR;
 }
 
-/* Given a PATH which might be a relative repo URL (^/), an absolute
- * local repo URL (file://), an absolute path outside of the repo
- * or a location in the Windows registry.
- *
- * Retrieve the configuration data that PATH points at and parse it into
- * CFG_P allocated in POOL.
- *
- * If PATH cannot be parsed as a config file then an error is returned.  The
- * contents of CFG_P is then undefined.  If MUST_EXIST is TRUE, a missing
- * authz file is also an error.
- *
- * REPOS_ROOT points at the root of the repos you are
- * going to apply the authz against, can be NULL if you are sure that you
- * don't have a repos relative URL in PATH. */
-static svn_error_t *
-authz_retrieve_config(svn_config_t **cfg_p, const char *path,
-                      svn_boolean_t must_exist, apr_pool_t *pool)
+svn_error_t *
+svn_repos__retrieve_config(svn_config_t **cfg_p, const char *path,
+                           svn_boolean_t must_exist, apr_pool_t *pool)
 {
   if (svn_path_is_url(path))
     {
@@ -943,7 +929,7 @@ svn_repos__authz_read(svn_authz_t **auth
 
   /* Load the authz file */
   if (accept_urls)
-    SVN_ERR(authz_retrieve_config(&authz->cfg, path, must_exist, pool));
+    SVN_ERR(svn_repos__retrieve_config(&authz->cfg, path, must_exist, pool));
   else
     SVN_ERR(svn_config_read3(&authz->cfg, path, must_exist, TRUE, TRUE, pool));
 
@@ -954,8 +940,8 @@ svn_repos__authz_read(svn_authz_t **auth
 
       /* Load the groups file */
       if (accept_urls)
-        SVN_ERR(authz_retrieve_config(&groups_cfg, groups_path, must_exist,
-                                      pool));
+        SVN_ERR(svn_repos__retrieve_config(&groups_cfg, groups_path,
+                                           must_exist, pool));
       else
         SVN_ERR(svn_config_read3(&groups_cfg, groups_path, must_exist,
                                  TRUE, TRUE, pool));

Modified: subversion/branches/log-addressing/subversion/libsvn_repos/commit.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_repos/commit.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_repos/commit.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_repos/commit.c Tue Oct 15 23:44:41 2013
@@ -1198,20 +1198,6 @@ move_cb(void *baton,
 }
 
 
-/* This implements svn_editor_cb_rotate_t */
-static svn_error_t *
-rotate_cb(void *baton,
-          const apr_array_header_t *relpaths,
-          const apr_array_header_t *revisions,
-          apr_pool_t *scratch_pool)
-{
-  struct ev2_baton *eb = baton;
-
-  SVN_ERR(svn_editor_rotate(eb->inner, relpaths, revisions));
-  return SVN_NO_ERROR;
-}
-
-
 /* This implements svn_editor_cb_complete_t */
 static svn_error_t *
 complete_cb(void *baton,
@@ -1333,7 +1319,6 @@ svn_repos__get_commit_ev2(svn_editor_t *
     delete_cb,
     copy_cb,
     move_cb,
-    rotate_cb,
     complete_cb,
     abort_cb
   };

Modified: subversion/branches/log-addressing/subversion/libsvn_repos/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_repos/deprecated.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_repos/deprecated.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_repos/deprecated.c Tue Oct 15 23:44:41 2013
@@ -472,6 +472,30 @@ svn_repos_fs_get_locks(apr_hash_t **lock
 
 /*** From logs.c ***/
 svn_error_t *
+svn_repos_get_logs4(svn_repos_t *repos,
+                    const apr_array_header_t *paths,
+                    svn_revnum_t start,
+                    svn_revnum_t end,
+                    int limit,
+                    svn_boolean_t discover_changed_paths,
+                    svn_boolean_t strict_node_history,
+                    svn_boolean_t include_merged_revisions,
+                    const apr_array_header_t *revprops,
+                    svn_repos_authz_func_t authz_read_func,
+                    void *authz_read_baton,
+                    svn_log_entry_receiver_t receiver,
+                    void *receiver_baton,
+                    apr_pool_t *pool)
+{
+  return svn_repos_get_logs5(repos, paths, start, end, limit,
+                             discover_changed_paths, strict_node_history,
+                             include_merged_revisions,
+                             svn_move_behavior_no_moves, revprops,
+                             authz_read_func, authz_read_baton,
+                             receiver, receiver_baton, pool);
+}
+
+svn_error_t *
 svn_repos_get_logs3(svn_repos_t *repos,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,

Modified: subversion/branches/log-addressing/subversion/libsvn_repos/dump.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_repos/dump.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_repos/dump.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_repos/dump.c Tue Oct 15 23:44:41 2013
@@ -38,6 +38,7 @@
 
 #include "private/svn_mergeinfo_private.h"
 #include "private/svn_fs_private.h"
+#include "private/svn_cache.h"
 
 #define ARE_VALID_COPY_ARGS(p,r) ((p) && SVN_IS_VALID_REVNUM(r))
 
@@ -130,6 +131,13 @@ struct edit_baton
   /* reusable buffer for writing file contents */
   char buffer[SVN__STREAM_CHUNK_SIZE];
   apr_size_t bufsize;
+
+  /* map nodeID -> node kind.  May be NULL.
+     The key is the string representation of the node ID given in
+     directory entries.  If we find an entry in this cache, the
+     respective node has already been verified as readable and being
+     of the type stored as value in the cache. */
+  svn_cache__t *verified_dirents_cache;
 };
 
 struct dir_baton
@@ -960,6 +968,7 @@ get_dump_editor(const svn_delta_editor_t
                 svn_revnum_t oldest_dumped_rev,
                 svn_boolean_t use_deltas,
                 svn_boolean_t verify,
+                svn_cache__t *verified_dirents_cache,
                 apr_pool_t *pool)
 {
   /* Allocate an edit baton to be stored in every directory baton.
@@ -984,6 +993,7 @@ get_dump_editor(const svn_delta_editor_t
   eb->verify = verify;
   eb->found_old_reference = found_old_reference;
   eb->found_old_mergeinfo = found_old_mergeinfo;
+  eb->verified_dirents_cache = verified_dirents_cache;
 
   /* Set up the editor. */
   dump_editor->open_root = open_root;
@@ -1180,7 +1190,8 @@ svn_repos_dump_fs3(svn_repos_t *repos,
                               "", stream, &found_old_reference,
                               &found_old_mergeinfo, NULL,
                               notify_func, notify_baton,
-                              start_rev, use_deltas_for_rev, FALSE, subpool));
+                              start_rev, use_deltas_for_rev, FALSE,
+                              NULL, subpool));
 
       /* Drive the editor in one way or another. */
       SVN_ERR(svn_fs_revision_root(&to_root, fs, rev, subpool));
@@ -1292,9 +1303,42 @@ verify_directory_entry(void *baton, cons
 {
   struct dir_baton *db = baton;
   svn_fs_dirent_t *dirent = (svn_fs_dirent_t *)val;
-  char *path = svn_relpath_join(db->path, (const char *)key, pool);
+  char *path;
   apr_hash_t *dirents;
   svn_filesize_t len;
+  svn_string_t *unparsed_id;
+
+  /* most directory entries will be unchanged from previous revs.
+     We should find those in the cache and they must match the
+     type defined in the DIRENT. */
+  if (db->edit_baton->verified_dirents_cache)
+    {
+      svn_node_kind_t kind;
+      svn_boolean_t found;
+      unparsed_id = svn_fs_unparse_id(dirent->id, pool);
+
+      SVN_ERR(svn_cache__get((void **)&kind, &found,
+                             db->edit_baton->verified_dirents_cache,
+                             unparsed_id->data, pool));
+
+      if (found)
+        {
+          if (kind == dirent->kind)
+            return SVN_NO_ERROR;
+          else
+            {
+              path = svn_relpath_join(db->path, (const char *)key, pool);
+
+              return
+                  svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
+                                    _("Unexpected node kind %d for '%s'. "
+                                      "Expected kind was %d."),
+                                    dirent->kind, path, kind);
+            }
+        }
+    }
+
+  path = svn_relpath_join(db->path, (const char *)key, pool);
 
   /* since we can't access the directory entries directly by their ID,
      we need to navigate from the FS_ROOT to them (relatively expensive
@@ -1316,6 +1360,11 @@ verify_directory_entry(void *baton, cons
                              dirent->kind, path);
   }
 
+  /* remember ID, kind pair */
+  if (db->edit_baton->verified_dirents_cache)
+    SVN_ERR(svn_cache__set(db->edit_baton->verified_dirents_cache,
+                           unparsed_id->data, &dirent->kind, pool));
+
   return SVN_NO_ERROR;
 }
 
@@ -1359,6 +1408,7 @@ verify_one_revision(svn_fs_t *fs,
                     svn_revnum_t start_rev,
                     svn_cancel_func_t cancel_func,
                     void *cancel_baton,
+                    svn_cache__t *verified_dirents_cache,
                     apr_pool_t *scratch_pool)
 {
   const svn_delta_editor_t *dump_editor;
@@ -1377,6 +1427,7 @@ verify_one_revision(svn_fs_t *fs,
                           notify_func, notify_baton,
                           start_rev,
                           FALSE, TRUE, /* use_deltas, verify */
+                          verified_dirents_cache,
                           scratch_pool));
   SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
                                             dump_editor, dump_edit_baton,
@@ -1424,6 +1475,30 @@ verify_fs2_notify_func(svn_revnum_t revi
                             notify_baton->notify, pool);
 }
 
+/* cache entry (de-)serialization support for svn_node_kind_t. */
+static svn_error_t *
+serialize_node_kind(void **data,
+                    apr_size_t *data_len,
+                    void *in,
+                    apr_pool_t *pool)
+{
+  *data_len = sizeof(svn_node_kind_t);
+  *data = in;
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+deserialize_node_kind(void **out,
+                      void *data,
+                      apr_size_t data_len,
+                      apr_pool_t *pool)
+{
+  *(svn_node_kind_t *)out = *(svn_node_kind_t *)data;
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_repos_verify_fs3(svn_repos_t *repos,
                      svn_revnum_t start_rev,
@@ -1444,6 +1519,7 @@ svn_repos_verify_fs3(svn_repos_t *repos,
   struct verify_fs2_notify_func_baton_t *verify_notify_baton = NULL;
   svn_error_t *err;
   svn_boolean_t found_corruption = FALSE;
+  svn_cache__t *verified_dirents_cache = NULL;
 
   /* Determine the current youngest revision of the filesystem. */
   SVN_ERR(svn_fs_youngest_rev(&youngest, fs, pool));
@@ -1505,13 +1581,26 @@ svn_repos_verify_fs3(svn_repos_t *repos,
       svn_error_clear(err);
     }
 
+  if (svn_cache__get_global_membuffer_cache())
+    SVN_ERR(svn_cache__create_membuffer_cache
+                                 (&verified_dirents_cache,
+                                  svn_cache__get_global_membuffer_cache(),
+                                  serialize_node_kind,
+                                  deserialize_node_kind,
+                                  APR_HASH_KEY_STRING,
+                                  svn_uuid_generate(pool),
+                                  SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
+                                  FALSE,
+                                  pool));
+
   for (rev = start_rev; rev <= end_rev; rev++)
     {
       svn_pool_clear(iterpool);
 
       /* Wrapper function to catch the possible errors. */
-      err = verify_one_revision(fs, rev, notify_func, notify_baton, start_rev,
-                                cancel_func, cancel_baton, iterpool);
+      err = verify_one_revision(fs, rev, notify_func, notify_baton,
+                                start_rev, cancel_func, cancel_baton,
+                                verified_dirents_cache, iterpool);
 
       if (err)
         {

Modified: subversion/branches/log-addressing/subversion/libsvn_repos/log.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_repos/log.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_repos/log.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_repos/log.c Tue Oct 15 23:44:41 2013
@@ -69,7 +69,8 @@ svn_repos_check_revision_access(svn_repo
 
   /* Fetch the changes associated with REVISION. */
   SVN_ERR(svn_fs_revision_root(&rev_root, fs, revision, pool));
-  SVN_ERR(svn_fs_paths_changed2(&changes, rev_root, pool));
+  SVN_ERR(svn_fs_paths_changed3(&changes, rev_root,
+                                svn_move_behavior_explicit_moves, pool));
 
   /* No changed paths?  We're done. */
   if (apr_hash_count(changes) == 0)
@@ -105,6 +106,8 @@ svn_repos_check_revision_access(svn_repo
         {
         case svn_fs_path_change_add:
         case svn_fs_path_change_replace:
+        case svn_fs_path_change_move:
+        case svn_fs_path_change_movereplace:
           {
             const char *copyfrom_path;
             svn_revnum_t copyfrom_rev;
@@ -163,7 +166,8 @@ svn_repos_check_revision_access(svn_repo
  *
  * To prevent changes from being processed over and over again, the
  * changed paths for ROOT may be passed in PREFETCHED_CHANGES.  If the
- * latter is NULL, we will request the list inside this function.
+ * latter is NULL, we will request the list inside this function using
+ * the specified MOVE_BEHAVIOR.
  *
  * If optional AUTHZ_READ_FUNC is non-NULL, then use it (with
  * AUTHZ_READ_BATON and FS) to check whether each changed-path (and
@@ -184,6 +188,7 @@ detect_changed(apr_hash_t **changed,
                svn_fs_root_t *root,
                svn_fs_t *fs,
                apr_hash_t *prefetched_changes,
+               svn_move_behavior_t move_behavior,
                svn_repos_authz_func_t authz_read_func,
                void *authz_read_baton,
                apr_pool_t *pool)
@@ -196,7 +201,7 @@ detect_changed(apr_hash_t **changed,
 
   *changed = svn_hash__make(pool);
   if (changes == NULL)
-    SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+    SVN_ERR(svn_fs_paths_changed3(&changes, root, move_behavior, pool));
 
   if (apr_hash_count(changes) == 0)
     /* No paths changed in this revision?  Uh, sure, I guess the
@@ -255,6 +260,14 @@ detect_changed(apr_hash_t **changed,
           action = 'D';
           break;
 
+        case svn_fs_path_change_move:
+          action = 'V';
+          break;
+
+        case svn_fs_path_change_movereplace:
+          action = 'E';
+          break;
+
         case svn_fs_path_change_modify:
         default:
           action = 'M';
@@ -306,7 +319,8 @@ detect_changed(apr_hash_t **changed,
         }
 
 
-      if ((action == 'A') || (action == 'R'))
+      if (   (action == 'A') || (action == 'R')
+          || (action == 'V') || (action == 'E'))
         {
           const char *copyfrom_path = change->copyfrom_path;
           svn_revnum_t copyfrom_rev = change->copyfrom_rev;
@@ -563,7 +577,9 @@ next_history_rev(const apr_array_header_
    catalogs describing how mergeinfo values on paths (which are the
    keys of those catalogs) were changed in REV.  If *PREFETCHED_CAHNGES
    already contains the changed paths for REV, use that.  Otherwise,
-   request that data and return it in *PREFETCHED_CHANGES. */
+   request that data and return it in *PREFETCHED_CHANGES.
+   MOVE_BEHAVIOR is a simple pass-through parameter that tells the FS
+   layer which changes to report as moves instead of additions. */
 /* ### TODO: This would make a *great*, useful public function,
    ### svn_repos_fs_mergeinfo_changed()!  -- cmpilato  */
 static svn_error_t *
@@ -572,6 +588,7 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
                      apr_hash_t **prefetched_changes,
                      svn_fs_t *fs,
                      svn_revnum_t rev,
+                     svn_move_behavior_t move_behavior,
                      apr_pool_t *result_pool,
                      apr_pool_t *scratch_pool)
 
@@ -592,7 +609,8 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
      narrow down our search. */
   SVN_ERR(svn_fs_revision_root(&root, fs, rev, scratch_pool));
   if (*prefetched_changes == NULL)
-    SVN_ERR(svn_fs_paths_changed2(prefetched_changes, root, scratch_pool));
+    SVN_ERR(svn_fs_paths_changed3(prefetched_changes, root, move_behavior,
+                                  scratch_pool));
 
   /* No changed paths?  We're done. */
   if (apr_hash_count(*prefetched_changes) == 0)
@@ -638,6 +656,8 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
            was.  If not, there's no previous location to examine.  */
         case svn_fs_path_change_add:
         case svn_fs_path_change_replace:
+        case svn_fs_path_change_move:
+        case svn_fs_path_change_movereplace:
           {
             const char *copyfrom_path;
             svn_revnum_t copyfrom_rev;
@@ -782,7 +802,8 @@ fs_mergeinfo_changed(svn_mergeinfo_catal
    *ADDED_MERGEINFO and deleted mergeinfo in *DELETED_MERGEINFO.
    If *PREFETCHED_CAHNGES already contains the changed paths for
    REV, use that.  Otherwise, request that data and return it in
-   *PREFETCHED_CHANGES.
+   *PREFETCHED_CHANGES.  MOVE_BEHAVIOR tells the FS layer which
+   changes to report as moves instead of additions.
    Use POOL for all allocations. */
 static svn_error_t *
 get_combined_mergeinfo_changes(svn_mergeinfo_t *added_mergeinfo,
@@ -791,6 +812,7 @@ get_combined_mergeinfo_changes(svn_merge
                                svn_fs_t *fs,
                                const apr_array_header_t *paths,
                                svn_revnum_t rev,
+                               svn_move_behavior_t move_behavior,
                                apr_pool_t *result_pool,
                                apr_pool_t *scratch_pool)
 {
@@ -820,7 +842,8 @@ get_combined_mergeinfo_changes(svn_merge
   err = fs_mergeinfo_changed(&deleted_mergeinfo_catalog,
                              &added_mergeinfo_catalog,
                              prefetched_changes,
-                             fs, rev, scratch_pool, scratch_pool);
+                             fs, rev, move_behavior,
+                             scratch_pool, scratch_pool);
   if (err)
     {
       if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -1025,6 +1048,7 @@ fill_log_entry(svn_log_entry_t *log_entr
                svn_fs_t *fs,
                apr_hash_t *prefetched_changes,
                svn_boolean_t discover_changed_paths,
+               svn_move_behavior_t move_behavior,
                const apr_array_header_t *revprops,
                svn_repos_authz_func_t authz_read_func,
                void *authz_read_baton,
@@ -1043,7 +1067,7 @@ fill_log_entry(svn_log_entry_t *log_entr
 
       SVN_ERR(svn_fs_revision_root(&newroot, fs, rev, pool));
       patherr = detect_changed(&changed_paths,
-                               newroot, fs, prefetched_changes,
+                               newroot, fs, prefetched_changes, move_behavior,
                                authz_read_func, authz_read_baton,
                                pool);
 
@@ -1160,9 +1184,9 @@ fill_log_entry(svn_log_entry_t *log_entr
    only the revision properties named by the (const char *) array elements
    (i.e. retrieve none if the array is empty).
 
-   LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, and
-   NESTED_MERGES are as per the arguments of the same name to DO_LOGS.  If
-   HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
+   LOG_TARGET_HISTORY_AS_MERGEINFO, HANDLING_MERGED_REVISION, MOVE_BEHAVIOR,
+   and NESTED_MERGES are as per the arguments of the same name to DO_LOGS.
+   If HANDLING_MERGED_REVISION is true and *all* changed paths within REV are
    already represented in LOG_TARGET_HISTORY_AS_MERGEINFO, then don't send
    the log message for REV.  If SUBTRACTIVE_MERGE is true, then REV was
    reverse merged.
@@ -1180,6 +1204,7 @@ send_log(svn_revnum_t rev,
          svn_boolean_t discover_changed_paths,
          svn_boolean_t subtractive_merge,
          svn_boolean_t handling_merged_revision,
+         svn_move_behavior_t move_behavior,
          const apr_array_header_t *revprops,
          svn_boolean_t has_children,
          svn_log_entry_receiver_t receiver,
@@ -1195,8 +1220,8 @@ send_log(svn_revnum_t rev,
   log_entry = svn_log_entry_create(pool);
   SVN_ERR(fill_log_entry(log_entry, rev, fs, prefetched_changes,
                          discover_changed_paths || handling_merged_revision,
-                         revprops, authz_read_func, authz_read_baton,
-                         pool));
+                         move_behavior, revprops, 
+                         authz_read_func, authz_read_baton, pool));
   log_entry->has_children = has_children;
   log_entry->subtractive_merge = subtractive_merge;
 
@@ -1663,6 +1688,7 @@ do_logs(svn_fs_t *fs,
         svn_boolean_t handling_merged_revisions,
         svn_boolean_t subtractive_merge,
         svn_boolean_t ignore_missing_locations,
+        svn_move_behavior_t move_behavior,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
         svn_log_entry_receiver_t receiver,
@@ -1712,6 +1738,7 @@ handle_merged_revisions(svn_revnum_t rev
                         svn_mergeinfo_t deleted_mergeinfo,
                         svn_boolean_t discover_changed_paths,
                         svn_boolean_t strict_node_history,
+                        svn_move_behavior_t move_behavior,
                         const apr_array_header_t *revprops,
                         svn_log_entry_receiver_t receiver,
                         void *receiver_baton,
@@ -1754,7 +1781,7 @@ handle_merged_revisions(svn_revnum_t rev
                       pl_range->range.start, pl_range->range.end, 0,
                       discover_changed_paths, strict_node_history,
                       TRUE, pl_range->reverse_merge, TRUE, TRUE,
-                      revprops, TRUE, receiver, receiver_baton,
+                      move_behavior, revprops, TRUE, receiver, receiver_baton,
                       authz_read_func, authz_read_baton, iterpool));
     }
   svn_pool_destroy(iterpool);
@@ -1888,6 +1915,9 @@ store_search(svn_mergeinfo_t processed,
    If IGNORE_MISSING_LOCATIONS is set, don't treat requests for bogus
    repository locations as fatal -- just ignore them.
 
+   MOVE_BEHAVIOR is a simple pass-through parameter that tells the FS
+   layer which changes to report as moves instead of additions.
+
    If LOG_TARGET_HISTORY_AS_MERGEINFO is not NULL then it contains mergeinfo
    representing the history of PATHS between HIST_START and HIST_END.
 
@@ -1925,6 +1955,7 @@ do_logs(svn_fs_t *fs,
         svn_boolean_t subtractive_merge,
         svn_boolean_t handling_merged_revisions,
         svn_boolean_t ignore_missing_locations,
+        svn_move_behavior_t move_behavior,
         const apr_array_header_t *revprops,
         svn_boolean_t descending_order,
         svn_log_entry_receiver_t receiver,
@@ -2019,8 +2050,8 @@ do_logs(svn_fs_t *fs,
                                                      &deleted_mergeinfo,
                                                      &changes,
                                                      fs, cur_paths,
-                                                     current, iterpool,
-                                                     iterpool));
+                                                     current, move_behavior,
+                                                     iterpool, iterpool));
               has_children = (apr_hash_count(added_mergeinfo) > 0
                               || apr_hash_count(deleted_mergeinfo) > 0);
             }
@@ -2034,7 +2065,7 @@ do_logs(svn_fs_t *fs,
                                log_target_history_as_mergeinfo, nested_merges,
                                discover_changed_paths,
                                subtractive_merge, handling_merged_revisions,
-                               revprops, has_children,
+                               move_behavior, revprops, has_children,
                                receiver, receiver_baton,
                                authz_read_func, authz_read_baton, iterpool));
 
@@ -2057,6 +2088,7 @@ do_logs(svn_fs_t *fs,
                     added_mergeinfo, deleted_mergeinfo,
                     discover_changed_paths,
                     strict_node_history,
+                    move_behavior,
                     revprops,
                     receiver, receiver_baton,
                     authz_read_func,
@@ -2138,7 +2170,8 @@ do_logs(svn_fs_t *fs,
           SVN_ERR(send_log(current, fs, NULL,
                            log_target_history_as_mergeinfo, nested_merges,
                            discover_changed_paths, subtractive_merge,
-                           handling_merged_revisions, revprops, has_children,
+                           handling_merged_revisions, move_behavior,
+                           revprops, has_children,
                            receiver, receiver_baton, authz_read_func,
                            authz_read_baton, iterpool));
           if (has_children)
@@ -2156,7 +2189,8 @@ do_logs(svn_fs_t *fs,
                                               added_mergeinfo,
                                               deleted_mergeinfo,
                                               discover_changed_paths,
-                                              strict_node_history, revprops,
+                                              strict_node_history,
+                                              move_behavior, revprops,
                                               receiver, receiver_baton,
                                               authz_read_func,
                                               authz_read_baton,
@@ -2258,7 +2292,7 @@ get_paths_history_as_mergeinfo(svn_merge
 }
 
 svn_error_t *
-svn_repos_get_logs4(svn_repos_t *repos,
+svn_repos_get_logs5(svn_repos_t *repos,
                     const apr_array_header_t *paths,
                     svn_revnum_t start,
                     svn_revnum_t end,
@@ -2266,6 +2300,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
                     svn_boolean_t discover_changed_paths,
                     svn_boolean_t strict_node_history,
                     svn_boolean_t include_merged_revisions,
+                    svn_move_behavior_t move_behavior,
                     const apr_array_header_t *revprops,
                     svn_repos_authz_func_t authz_read_func,
                     void *authz_read_baton,
@@ -2374,7 +2409,7 @@ svn_repos_get_logs4(svn_repos_t *repos,
             rev = start + i;
           SVN_ERR(send_log(rev, fs, NULL, NULL, NULL,
                            discover_changed_paths, FALSE,
-                           FALSE, revprops, FALSE, receiver,
+                           FALSE, move_behavior, revprops, FALSE, receiver,
                            receiver_baton, authz_read_func,
                            authz_read_baton, iterpool));
         }
@@ -2402,7 +2437,8 @@ svn_repos_get_logs4(svn_repos_t *repos,
 
   return do_logs(repos->fs, paths, paths_history_mergeinfo, NULL, NULL, start, end,
                  limit, discover_changed_paths, strict_node_history,
-                 include_merged_revisions, FALSE, FALSE, FALSE, revprops,
+                 include_merged_revisions, FALSE, FALSE, FALSE,
+                 move_behavior, revprops,
                  descending_order, receiver, receiver_baton,
                  authz_read_func, authz_read_baton, pool);
 }

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/cache-membuffer.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/cache-membuffer.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/cache-membuffer.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/cache-membuffer.c Tue Oct 15 23:44:41 2013
@@ -50,7 +50,8 @@
  * 2. A directory of cache entries. This is organized similar to CPU
  *    data caches: for every possible key, there is exactly one group
  *    of entries that may contain the header info for an item with
- *    that given key. The result is a GROUP_SIZE-way associative cache.
+ *    that given key.  The result is a GROUP_SIZE+-way associative cache
+ *    whose associativity can be dynamically increased.
  *
  * Only the start address of these two data parts are given as a native
  * pointer. All other references are expressed as offsets to these pointers.
@@ -96,6 +97,10 @@
  * about 50% of the content survives every 50% of the cache being re-written
  * with new entries. For details on the fine-tuning involved, see the
  * comments in ensure_data_insertable_l2().
+ * 
+ * Due to the randomized mapping of keys to entry groups, some groups may
+ * overflow.  In that case, there are spare groups that can be chained to
+ * 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
@@ -110,13 +115,6 @@
  * on their hash key.
  */
 
-/* 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 16
-
 /* For more efficient copy operations, let's align all data items properly.
  * Must be a power of 2.
  */
@@ -297,17 +295,18 @@ static svn_error_t* assert_equal_tags(co
 
 #define DEBUG_CACHE_MEMBUFFER_TAG tag,
 
-#define DEBUG_CACHE_MEMBUFFER_INIT_TAG                         \
-  entry_tag_t _tag;                                            \
-  entry_tag_t *tag = &_tag;                                    \
-  SVN_ERR(store_key_part(tag,                                  \
-                         cache->prefix,                        \
-                         cache->prefix_tail,                   \
-                         key,                                  \
-                         cache->key_len == APR_HASH_KEY_STRING \
-                             ? strlen((const char *) key)      \
-                             : cache->key_len,                 \
-                         cache->pool));
+#define DEBUG_CACHE_MEMBUFFER_INIT_TAG                           \
+  entry_tag_t _tag;                                              \
+  entry_tag_t *tag = &_tag;                                      \
+  if (key)                                                       \
+    SVN_ERR(store_key_part(tag,                                  \
+                           cache->prefix,                        \
+                           cache->prefix_tail,                   \
+                           key,                                  \
+                           cache->key_len == APR_HASH_KEY_STRING \
+                               ? strlen((const char *) key)      \
+                               : cache->key_len,                 \
+                           cache->pool));
 
 #else
 
@@ -335,10 +334,11 @@ typedef struct entry_t
    */
   apr_uint64_t offset;
 
-  /* Size of the serialized item data. May be 0.
+  /* Size of the serialized item data. May be 0.  The MAX_ITEM_SIZE macro
+   * above ensures that there will be no overflows.
    * Only valid for used entries.
    */
-  apr_size_t size;
+  apr_uint32_t size;
 
   /* Number of (read) hits for this entry. Will be reset upon write.
    * Only valid for used entries.
@@ -371,15 +371,60 @@ typedef struct entry_t
 #endif
 } entry_t;
 
-/* We group dictionary entries to make this GROUP-SIZE-way associative.
+/* Group header struct.
  */
-typedef struct entry_group_t
+typedef struct group_header_t
 {
   /* number of entries used [0 .. USED-1] */
   apr_uint32_t used;
 
+  /* next group in the chain or NO_INDEX for the last.
+   * For recycleable unused spare groups, this points to the next
+   * unused spare group */
+  apr_uint32_t next;
+
+  /* previously group in the chain or NO_INDEX for the first */
+  apr_uint32_t previous;
+
+  /* number of elements in the chain from start to here.
+   * >= 1 for used groups, 0 for unused spare groups */
+  apr_uint32_t chain_length;
+ 
+} group_header_t;
+
+/* The size of the group struct should be a power of two make sure it does
+ * not cross memory page boundaries.  Since we already access the cache
+ * randomly, having two page table lookups instead of one is bad.
+ */
+#define GROUP_BLOCK_SIZE 512
+
+/* A ~10-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 \
+          ((GROUP_BLOCK_SIZE - sizeof(group_header_t)) / sizeof(entry_t))
+
+/* Maximum number of groups in a chain, i.e. a cache index group can hold
+ * up to GROUP_SIZE * MAX_GROUP_CHAIN_LENGTH entries.
+ */
+#define MAX_GROUP_CHAIN_LENGTH 8
+
+/* We group dictionary entries to make this GROUP-SIZE-way associative.
+ */
+typedef struct entry_group_t
+{
+  /* group globals */
+  group_header_t header;
+  
+  /* padding and also room for future extensions */
+  char padding[GROUP_BLOCK_SIZE - sizeof(group_header_t)
+               - sizeof(entry_t) * GROUP_SIZE];
+
   /* the actual entries */
   entry_t entries[GROUP_SIZE];
+
 } entry_group_t;
 
 /* Per-cache level header structure.  Instances of this are members of
@@ -431,7 +476,8 @@ struct svn_membuffer_t
      and that all segments must / will report the same values here. */
   apr_uint32_t segment_count;
 
-  /* The dictionary, GROUP_SIZE * group_count entries long. Never NULL.
+  /* The dictionary, GROUP_SIZE * (group_count + spare_group_count)
+   * entries long.  Never NULL.
    */
   entry_group_t *directory;
 
@@ -444,6 +490,20 @@ struct svn_membuffer_t
    */
   apr_uint32_t group_count;
 
+  /* Total number of spare groups.
+   */
+  apr_uint32_t spare_group_count;
+
+  /* First recycleable spare group.
+   */
+  apr_uint32_t first_spare_group;
+
+  /* Maximum number of spare groups ever used.  I.e. group index
+   * group_count + max_spare_used is the first unused spare group
+   * if first_spare_group is NO_INDEX.
+   */
+  apr_uint32_t max_spare_used;
+
   /* Pointer to the data buffer, data_size bytes long. Never NULL.
    */
   unsigned char *data;
@@ -644,6 +704,132 @@ do {                                    
   SVN_ERR(unlock_cache(cache, (expr)));                         \
 } while (0)
 
+/* Returns 0 if the entry group identified by GROUP_INDEX in CACHE has not
+ * been initialized, yet. In that case, this group can not data. Otherwise,
+ * a non-zero value is returned.
+ */
+static APR_INLINE unsigned char
+is_group_initialized(svn_membuffer_t *cache, apr_uint32_t group_index)
+{
+  unsigned char flags
+    = cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)];
+  unsigned char bit_mask
+    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
+
+  return flags & bit_mask;
+}
+
+/* Initializes the section of the directory in CACHE that contains
+ * the entry group identified by GROUP_INDEX. */
+static void
+initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
+{
+  unsigned char bit_mask;
+  apr_uint32_t i;
+
+  /* range of groups to initialize due to GROUP_INIT_GRANULARITY */
+  apr_uint32_t first_index =
+      (group_index / GROUP_INIT_GRANULARITY) * GROUP_INIT_GRANULARITY;
+  apr_uint32_t last_index = first_index + GROUP_INIT_GRANULARITY;
+  if (last_index > cache->group_count)
+    last_index = cache->group_count;
+
+  for (i = first_index; i < last_index; ++i)
+    {
+      group_header_t *header = &cache->directory[i].header;
+      header->used = 0;
+      header->chain_length = 1;
+      header->next = NO_INDEX;
+      header->previous = NO_INDEX;
+    }
+
+  /* set the "initialized" bit for these groups */
+  bit_mask
+    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
+  cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)]
+    |= bit_mask;
+}
+
+/* Return the next available spare group from CACHE and mark it as used.
+ * May return NULL.
+ */
+static entry_group_t *
+allocate_spare_group(svn_membuffer_t *cache)
+{
+  entry_group_t *group = NULL;
+
+  /* is there some ready-to-use group? */
+  if (cache->first_spare_group != NO_INDEX)
+    {
+      group = &cache->directory[cache->first_spare_group];
+      cache->first_spare_group = group->header.next;
+    }
+
+  /* any so far untouched spares available? */
+  else if (cache->max_spare_used < cache->spare_group_count)
+    {
+      apr_uint32_t group_index = cache->group_count + cache->max_spare_used;
+      ++cache->max_spare_used;
+
+      if (!is_group_initialized(cache, group_index))
+        initialize_group(cache, group_index);
+
+      group = &cache->directory[group_index];
+    }
+
+  /* spare groups must be empty */
+  assert(!group || !group->header.used);
+  return group;
+}
+
+/* Mark previously allocated spare group GROUP in CACHE as "unused".
+ */
+static void
+free_spare_group(svn_membuffer_t *cache,
+                 entry_group_t *group)
+{
+  assert(group->header.used == 0);
+  assert(group->header.previous != NO_INDEX);
+  assert(group - cache->directory >= cache->group_count);
+
+  /* unchain */
+  cache->directory[group->header.previous].header.next = NO_INDEX;
+  group->header.chain_length = 0;
+  group->header.previous = NO_INDEX;
+
+  /* add to chain of spares */
+  group->header.next = cache->first_spare_group;
+  cache->first_spare_group = group - cache->directory;
+}
+
+/* Follow the group chain from GROUP in CACHE to its end and return the last
+ * group.  May return GROUP.
+ */
+static entry_group_t *
+last_group_in_chain(svn_membuffer_t *cache,
+                    entry_group_t *group)
+{
+  while (group->header.next != NO_INDEX)
+    group = &cache->directory[group->header.next];
+
+  return group;
+}
+
+/* Return the CHAIN_INDEX-th element in the group chain starting from group
+ * START_GROUP_INDEX in CACHE.
+ */
+static entry_group_t *
+get_group(svn_membuffer_t *cache,
+          apr_uint32_t start_group_index,
+          apr_uint32_t chain_index)
+{
+  entry_group_t *group = &cache->directory[start_group_index];
+  for (; chain_index; --chain_index)
+    group = &cache->directory[group->header.next];
+
+  return group;
+}
+
 /* Resolve a dictionary entry reference, i.e. return the entry
  * for the given IDX.
  */
@@ -765,13 +951,13 @@ drop_entry(svn_membuffer_t *cache, entry
    */
   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;
-  cache_level_t *level = get_cache_level(cache, entry);
+  entry_group_t *last_group
+    = last_group_in_chain(cache, &cache->directory[group_index]);
+  apr_uint32_t last_in_group
+    = (last_group - cache->directory) * GROUP_SIZE
+    + last_group->header.used - 1;
 
-  /* Only valid to be called for used entries.
-   */
-  assert(idx <= last_in_group);
+  cache_level_t *level = get_cache_level(cache, entry);
 
   /* update global cache usage counters
    */
@@ -805,16 +991,16 @@ drop_entry(svn_membuffer_t *cache, entry
   /* unlink it from the chain of used entries
    */
   unchain_entry(cache, level, entry, idx);
-  
+
   /* Move last entry into hole (if the removed one is not the last used).
    * We need to do this since all used entries are at the beginning of
    * the group's entries array.
    */
-  if (idx < last_in_group)
+  if (idx != last_in_group)
     {
       /* copy the last used entry to the removed entry's index
        */
-      *entry = group->entries[group->used-1];
+      *entry = last_group->entries[last_group->header.used-1];
 
       /* this ENTRY may belong to a different cache level than the entry
        * we have just removed */
@@ -838,7 +1024,12 @@ drop_entry(svn_membuffer_t *cache, entry
 
   /* Update the number of used entries.
    */
-  group->used--;
+  last_group->header.used--;
+
+  /* Release the last group in the chain if it is a spare group
+   */
+  if (!last_group->header.used && last_group->header.previous != NO_INDEX)
+    free_spare_group(cache, last_group);
 }
 
 /* Insert ENTRY into the chain of used dictionary entries. The entry's
@@ -859,7 +1050,7 @@ insert_entry(svn_membuffer_t *cache, ent
    * It must also be the first unused entry in the group.
    */
   assert(entry->offset == level->current_data);
-  assert(idx == group_index * GROUP_SIZE + group->used);
+  assert(idx == group_index * GROUP_SIZE + group->header.used);
   level->current_data = ALIGN_VALUE(entry->offset + entry->size);
 
   /* update usage counters
@@ -867,7 +1058,7 @@ insert_entry(svn_membuffer_t *cache, ent
   cache->used_entries++;
   cache->data_used += entry->size;
   entry->hit_count = 0;
-  group->used++;
+  group->header.used++;
 
   /* update entry chain
    */
@@ -909,46 +1100,6 @@ let_entry_age(svn_membuffer_t *cache, en
   entry->hit_count -= hits_removed;
 }
 
-/* Returns 0 if the entry group identified by GROUP_INDEX in CACHE has not
- * been initialized, yet. In that case, this group can not data. Otherwise,
- * a non-zero value is returned.
- */
-static APR_INLINE unsigned char
-is_group_initialized(svn_membuffer_t *cache, apr_uint32_t group_index)
-{
-  unsigned char flags
-    = cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)];
-  unsigned char bit_mask
-    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
-
-  return flags & bit_mask;
-}
-
-/* Initializes the section of the directory in CACHE that contains
- * the entry group identified by GROUP_INDEX. */
-static void
-initialize_group(svn_membuffer_t *cache, apr_uint32_t group_index)
-{
-  unsigned char bit_mask;
-  apr_uint32_t i;
-
-  /* range of groups to initialize due to GROUP_INIT_GRANULARITY */
-  apr_uint32_t first_index =
-      (group_index / GROUP_INIT_GRANULARITY) * GROUP_INIT_GRANULARITY;
-  apr_uint32_t last_index = first_index + GROUP_INIT_GRANULARITY;
-  if (last_index > cache->group_count)
-    last_index = cache->group_count;
-
-  for (i = first_index; i < last_index; ++i)
-    cache->directory[i].used = 0;
-
-  /* set the "initialized" bit for these groups */
-  bit_mask
-    = (unsigned char)(1 << ((group_index / GROUP_INIT_GRANULARITY) % 8));
-  cache->group_initialized[group_index / (8 * GROUP_INIT_GRANULARITY)]
-    |= bit_mask;
-}
-
 /* Given the GROUP_INDEX that shall contain an entry with the hash key
  * TO_FIND, find that entry in the specified group.
  *
@@ -994,45 +1145,98 @@ find_entry(svn_membuffer_t *cache,
 
   /* try to find the matching entry
    */
-  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->entries[i];
-        if (find_empty)
-          drop_entry(cache, entry);
-        else
-          return entry;
-      }
+  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])
+          {
+            /* found it
+             */
+            entry = &group->entries[i];
+            if (!find_empty)
+              return entry;
+
+            /* need to empty that entry */
+            drop_entry(cache, entry);
+            if (group->header.used == GROUP_SIZE)
+              group = last_group_in_chain(cache, group);
+            else if (group->header.chain_length == 0)
+              group = last_group_in_chain(cache,
+                                          &cache->directory[group_index]);
+
+            break;
+          }
+
+      /* end of chain? */
+      if (group->header.next == NO_INDEX)
+        break;
+
+      /* only full groups may chain */
+      assert(group->header.used == GROUP_SIZE);
+      group = &cache->directory[group->header.next];
+    }
 
   /* None found. Are we looking for a free entry?
    */
   if (find_empty)
     {
-      /* if there is no empty entry, delete the oldest entry
+      /* There is no empty entry in the chain, try chaining a spare group.
        */
-      if (group->used == GROUP_SIZE)
+      if (   group->header.used == GROUP_SIZE
+          && group->header.chain_length < MAX_GROUP_CHAIN_LENGTH)
+        {
+          entry_group_t *new_group = allocate_spare_group(cache);
+          if (new_group)
+            {
+              /* chain groups
+               */
+              new_group->header.chain_length = group->header.chain_length + 1;
+              new_group->header.previous = group - cache->directory;
+              new_group->header.next = NO_INDEX;
+              group->header.next = new_group - cache->directory;
+              group = new_group;
+            }
+        }
+
+      /* if GROUP is still filled, we need to remove a random entry */
+      if (group->header.used == GROUP_SIZE)
         {
           /* 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.  Also, apply priorities strictly.
+           * Otherwise, we free the first entry, fill it and
+           * remove it again on the next occasion without considering
+           * the other entries in this group.
+           *
+           * We hit only one random group instead of processing all
+           * groups in the chain.
            */
-          entry = &group->entries[rand() % GROUP_SIZE];
-          for (i = 1; i < GROUP_SIZE; ++i)
-            if (   (entry->priority > group->entries[i].priority)
-                || (   entry->priority == group->entries[i].priority
-                    && entry->hit_count > group->entries[i].hit_count))
-              entry = &group->entries[i];
+          cache_level_t *entry_level;
+          int to_remove = rand() % (GROUP_SIZE * group->header.chain_length);
+          entry_group_t *to_shrink
+            = get_group(cache, group_index, to_remove / GROUP_SIZE);
+
+          entry = &to_shrink->entries[to_remove % GROUP_SIZE];
+          entry_level = get_cache_level(cache, entry);
+          for (i = 0; i < GROUP_SIZE; ++i)
+            {
+              /* keep L1 entries whenever possible */
+
+              cache_level_t *level
+                = get_cache_level(cache, &to_shrink->entries[i]);
+              if (   (level != entry_level && entry_level == &cache->l1)
+                  || (entry->hit_count > to_shrink->entries[i].hit_count))
+                {
+                  entry_level = level;
+                  entry = &to_shrink->entries[i];
+                }
+            }
 
           /* for the entries that don't have been removed,
            * reduce their hit counts to put them at a relative
            * disadvantage the next time.
            */
           for (i = 0; i < GROUP_SIZE; ++i)
-            if (entry != &group->entries[i])
+            if (entry != &to_shrink->entries[i])
               let_entry_age(cache, entry);
 
           drop_entry(cache, entry);
@@ -1040,7 +1244,7 @@ find_entry(svn_membuffer_t *cache,
 
       /* initialize entry for the new key
        */
-      entry = &group->entries[group->used];
+      entry = &group->entries[group->header.used];
       entry->key[0] = to_find[0];
       entry->key[1] = to_find[1];
     }
@@ -1096,6 +1300,7 @@ promote_entry(svn_membuffer_t *cache, en
   apr_uint32_t idx = get_index(cache, entry);
   apr_size_t size = ALIGN_VALUE(entry->size);
   assert(get_cache_level(cache, entry) == &cache->l1);
+  assert(idx == cache->l1.next);
 
   /* copy item from the current location in L1 to the start of L2's
    * insertion window */
@@ -1126,8 +1331,7 @@ promote_entry(svn_membuffer_t *cache, en
  */
 static svn_boolean_t
 ensure_data_insertable_l2(svn_membuffer_t *cache,
-                          entry_t *to_fit_in,
-                          apr_uint32_t idx)
+                          entry_t *to_fit_in)
 {
   entry_t *entry;
   apr_uint64_t average_hit_value;
@@ -1146,9 +1350,6 @@ ensure_data_insertable_l2(svn_membuffer_
   /* accumulated "worth" of items dropped so far */
   apr_size_t drop_hits = 0;
 
-  /* verify parameters */
-  assert(idx == get_index(cache, to_fit_in));
-
   /* This loop will eventually terminate because every cache entry
    * would get dropped eventually:
    * - hit counts become 0 after the got kept for 32 full scans
@@ -1220,16 +1421,6 @@ ensure_data_insertable_l2(svn_membuffer_
             {
               keep = TRUE;
             }
-          else if (cache->l2.next / GROUP_SIZE == idx / GROUP_SIZE)
-            {
-              /* Special case: we cannot drop entries that are in the same
-               * group as TO_FIT_IN because that might the latter to become
-               * invalidated it it happens to be the highest used entry in
-               * the group.  So, we must keep ENTRY unconditionally.
-               * (this is a very rare condition)
-               */
-              keep = TRUE;
-            }
           else if (   entry->priority < SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY
                    && to_fit_in->priority > entry->priority)
             {
@@ -1307,8 +1498,6 @@ ensure_data_insertable_l2(svn_membuffer_
 static svn_boolean_t
 ensure_data_insertable_l1(svn_membuffer_t *cache, apr_size_t size)
 {
-  entry_t *entry;
-
   /* Guarantees that the while loop will terminate. */
   if (size > cache->l1.size)
     return FALSE;
@@ -1320,9 +1509,11 @@ ensure_data_insertable_l1(svn_membuffer_
     {
       /* first offset behind the insertion window
        */
+      apr_uint32_t entry_index = cache->l1.next;
+      entry_t *entry = get_entry(cache, entry_index);
       apr_uint64_t end = cache->l1.next == NO_INDEX
                        ? cache->l1.start_offset + cache->l1.size
-                       : get_entry(cache, cache->l1.next)->offset;
+                       : entry->offset;
 
       /* leave function as soon as the insertion window is large enough
        */
@@ -1346,12 +1537,16 @@ ensure_data_insertable_l1(svn_membuffer_
           /* Remove the entry from the end of insertion window and promote
            * it to L2, if it is important enough.
            */
-          entry = get_entry(cache, cache->l1.next);
+          svn_boolean_t keep = ensure_data_insertable_l2(cache, entry);
 
-          if (ensure_data_insertable_l2(cache, entry, cache->l1.next))
-            promote_entry(cache, entry);
-          else
-            drop_entry(cache, entry);
+          /* We might have touched the group that contains ENTRY. Recheck. */
+          if (entry_index == cache->l1.next)
+            {
+              if (keep)
+                promote_entry(cache, entry);
+              else
+                drop_entry(cache, entry);
+            }
         }
     }
 
@@ -1394,6 +1589,8 @@ svn_cache__membuffer_cache_create(svn_me
 
   apr_uint32_t seg;
   apr_uint32_t group_count;
+  apr_uint32_t main_group_count;
+  apr_uint32_t spare_group_count;
   apr_uint32_t group_init_size;
   apr_uint64_t data_size;
   apr_uint64_t max_entry_size;
@@ -1468,8 +1665,8 @@ svn_cache__membuffer_cache_create(svn_me
    */
   if (directory_size > total_size - sizeof(entry_group_t))
     directory_size = total_size - sizeof(entry_group_t);
-  if (directory_size < sizeof(entry_group_t))
-    directory_size = sizeof(entry_group_t);
+  if (directory_size < 2 * sizeof(entry_group_t))
+    directory_size = 2 * sizeof(entry_group_t);
 
   /* limit the data size to what we can address.
    * Note that this cannot overflow since all values are of size_t.
@@ -1497,6 +1694,11 @@ svn_cache__membuffer_cache_create(svn_me
               ? (APR_UINT32_MAX / GROUP_SIZE) - 1
               : (apr_uint32_t)(directory_size / sizeof(entry_group_t));
 
+  /* set some of the index directory aside as over-flow (spare) buffers */
+  spare_group_count = MAX(group_count / 4, 1);
+  main_group_count = group_count - spare_group_count;
+  assert(spare_group_count > 0 && main_group_count > 0);
+
   group_init_size = 1 + group_count / (8 * GROUP_INIT_GRANULARITY);
   for (seg = 0; seg < segment_count; ++seg)
     {
@@ -1504,7 +1706,11 @@ svn_cache__membuffer_cache_create(svn_me
        */
       c[seg].segment_count = (apr_uint32_t)segment_count;
 
-      c[seg].group_count = group_count;
+      c[seg].group_count = main_group_count;
+      c[seg].spare_group_count = spare_group_count;
+      c[seg].first_spare_group = NO_INDEX;
+      c[seg].max_spare_used = 0;
+
       c[seg].directory = apr_pcalloc(pool,
                                      group_count * sizeof(entry_group_t));
 
@@ -1869,7 +2075,24 @@ membuffer_cache_has_key_internal(svn_mem
                                  entry_key_t to_find,
                                  svn_boolean_t *found)
 {
-  *found = find_entry(cache, group_index, to_find, FALSE) != NULL;
+  entry_t *entry = find_entry(cache, group_index, to_find, FALSE);
+  if (entry)
+    {
+      /* This is often happen in "block read" where most data is already
+         in L2 and only a few previously evicted items are added to L1
+         again.  While items in L1 are well protected for a while, L2
+         items may get evicted soon.  Thus, mark all them as "hit" to give
+         them a higher chance for survival. */
+      entry->hit_count++;
+      cache->hit_count++;
+      cache->total_hits++;
+
+      *found = TRUE;
+    }
+  else
+    {
+      *found = FALSE;
+    }
 
   return SVN_NO_ERROR;
 }
@@ -1887,6 +2110,8 @@ membuffer_cache_has_key(svn_membuffer_t 
   /* find the entry group that will hold the key.
    */
   apr_uint32_t group_index = get_group_index(&cache, key);
+  cache->total_reads++;
+
   WITH_READ_LOCK(cache,
                  membuffer_cache_has_key_internal(cache,
                                                   group_index,
@@ -2572,8 +2797,10 @@ svn_membuffer_get_segment_info(svn_membu
   if (include_histogram)
     for (i = 0; i < segment->group_count; ++i)
       {
+        entry_group_t *chain_end
+          = last_group_in_chain(segment, &segment->directory[i]);
         apr_size_t use
-          = MIN(segment->directory[i].used,
+          = MIN(chain_end->header.used,
                 sizeof(info->histogram) / sizeof(info->histogram[0]) - 1);
         info->histogram[use]++;
       }

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/cache_config.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/cache_config.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/cache_config.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/cache_config.c Tue Oct 15 23:44:41 2013
@@ -23,6 +23,7 @@
 #include <apr_atomic.h>
 
 #include "svn_cache_config.h"
+#include "private/svn_atomic.h"
 #include "private/svn_cache.h"
 
 #include "svn_pools.h"
@@ -69,30 +70,27 @@ svn_cache_config_get(void)
   return &cache_settings;
 }
 
-/* Access the process-global (singleton) membuffer cache. The first call
- * will automatically allocate the cache using the current cache config.
- * NULL will be returned if the desired cache size is 0 or if the cache
- * could not be created for some reason.
+/* Initializer function as required by svn_atomic__init_once.  Allocate
+ * the process-global (singleton) membuffer cache and return it in the
+ * svn_membuffer_t * in *BATON.  UNUSED_POOL is unused and should be NULL.
  */
-svn_membuffer_t *
-svn_cache__get_global_membuffer_cache(void)
+static svn_error_t *
+initialize_cache(void *baton, apr_pool_t *unused_pool)
 {
-  static svn_membuffer_t * volatile cache = NULL;
+  svn_membuffer_t **cache_p = baton;
+  svn_membuffer_t *cache = NULL;
 
   apr_uint64_t cache_size = cache_settings.cache_size;
-  if (!cache && cache_size)
+  if (cache_size)
     {
       svn_error_t *err;
 
-      svn_membuffer_t *old_cache = NULL;
-      svn_membuffer_t *new_cache = NULL;
-
       /* auto-allocate cache */
       apr_allocator_t *allocator = NULL;
       apr_pool_t *pool = NULL;
 
       if (apr_allocator_create(&allocator))
-        return NULL;
+        return SVN_NO_ERROR;
 
       /* Ensure that we free partially allocated data if we run OOM
        * before the cache is complete: If the cache cannot be allocated
@@ -112,13 +110,13 @@ svn_cache__get_global_membuffer_cache(vo
        */
       apr_pool_create_ex(&pool, NULL, NULL, allocator);
       if (pool == NULL)
-        return NULL;
+        return SVN_NO_ERROR;
       apr_allocator_owner_set(allocator, pool);
 
       err = svn_cache__membuffer_cache_create(
-          &new_cache,
+          &cache,
           (apr_size_t)cache_size,
-          (apr_size_t)(cache_size / 10),
+          (apr_size_t)(cache_size / 5),
           0,
           ! svn_cache_config_get()->single_threaded,
           FALSE,
@@ -129,33 +127,40 @@ svn_cache__get_global_membuffer_cache(vo
        */
       if (err)
         {
-          /* Memory and error cleanup */
-          svn_error_clear(err);
+          /* Memory cleanup */
           svn_pool_destroy(pool);
 
-          /* Prevent future attempts to create the cache. However, an
-           * existing cache instance (see next comment) remains valid.
-           */
+          /* Document that we actually don't have a cache. */
           cache_settings.cache_size = 0;
 
-          /* The current caller won't get the cache object.
-           * However, a concurrent call might have succeeded in creating
-           * the cache object. That call and all following ones will then
-           * use the successfully created cache instance.
-           */
-          return NULL;
+          return svn_error_trace(err);
         }
 
-      /* Handle race condition: if we are the first to create a
-       * cache object, make it our global singleton. Otherwise,
-       * discard the new cache and keep the existing one.
-       *
-       * Cast is necessary because of APR bug:
-       * https://issues.apache.org/bugzilla/show_bug.cgi?id=50731
-       */
-      old_cache = apr_atomic_casptr((volatile void **)&cache, new_cache, NULL);
-      if (old_cache != NULL)
-        svn_pool_destroy(pool);
+      /* done */
+      *cache_p = cache;
+    }
+
+  return SVN_NO_ERROR;
+}
+
+/* Access the process-global (singleton) membuffer cache. The first call
+ * will automatically allocate the cache using the current cache config.
+ * NULL will be returned if the desired cache size is 0 or if the cache
+ * could not be created for some reason.
+ */
+svn_membuffer_t *
+svn_cache__get_global_membuffer_cache(void)
+{
+  static svn_membuffer_t *cache = NULL;
+  static svn_atomic_t initialized = 0;
+
+  svn_error_t *err
+    = svn_atomic__init_once(&initialized, initialize_cache, &cache, NULL);
+  if (err)
+    {
+      /* no caches today ... */
+      svn_error_clear(err);
+      return NULL;
     }
 
   return cache;

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/cmdline.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/cmdline.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/cmdline.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/cmdline.c Tue Oct 15 23:44:41 2013
@@ -67,11 +67,17 @@
 
 #include "win32_crashrpt.h"
 
+#if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER < 1400)
+/* Before Visual Studio 2005, the C runtime didn't handle encodings for the
+   for the stdio output handling. */
+#define CMDLINE_USE_CUSTOM_ENCODING
+
 /* The stdin encoding. If null, it's the same as the native encoding. */
 static const char *input_encoding = NULL;
 
 /* The stdout encoding. If null, it's the same as the native encoding. */
 static const char *output_encoding = NULL;
+#endif
 
 
 int
@@ -113,7 +119,7 @@ svn_cmdline_init(const char *progname, F
 #endif
 
 #ifdef WIN32
-#if _MSC_VER < 1400
+#ifdef CMDLINE_USE_CUSTOM_ENCODING
   /* Initialize the input and output encodings. */
   {
     static char input_encoding_buffer[16];
@@ -127,7 +133,7 @@ svn_cmdline_init(const char *progname, F
                  "CP%u", (unsigned) GetConsoleOutputCP());
     output_encoding = output_encoding_buffer;
   }
-#endif /* _MSC_VER < 1400 */
+#endif /* CMDLINE_USE_CUSTOM_ENCODING */
 
 #ifdef SVN_USE_WIN32_CRASHHANDLER
   if (!getenv("SVN_CMDLINE_DISABLE_CRASH_HANDLER"))
@@ -257,10 +263,12 @@ svn_cmdline_cstring_from_utf8(const char
                               const char *src,
                               apr_pool_t *pool)
 {
-  if (output_encoding == NULL)
-    return svn_utf_cstring_from_utf8(dest, src, pool);
-  else
+#ifdef CMDLINE_USE_CUSTOM_ENCODING
+  if (output_encoding != NULL)
     return svn_utf_cstring_from_utf8_ex2(dest, src, output_encoding, pool);
+#endif
+
+  return svn_utf_cstring_from_utf8(dest, src, pool);
 }
 
 
@@ -278,10 +286,12 @@ svn_cmdline_cstring_to_utf8(const char *
                             const char *src,
                             apr_pool_t *pool)
 {
-  if (input_encoding == NULL)
-    return svn_utf_cstring_to_utf8(dest, src, pool);
-  else
+#ifdef CMDLINE_USE_CUSTOM_ENCODING
+  if (input_encoding != NULL)
     return svn_utf_cstring_to_utf8_ex2(dest, src, input_encoding, pool);
+#endif
+
+  return svn_utf_cstring_to_utf8(dest, src, pool);
 }
 
 
@@ -357,7 +367,7 @@ svn_cmdline_fputs(const char *string, FI
         {
           /* ### Issue #3014: Return a specific error for broken pipes,
            * ### with a single element in the error chain. */
-          if (APR_STATUS_IS_EPIPE(apr_get_os_error()))
+          if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
             return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
           else
             return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
@@ -380,7 +390,7 @@ svn_cmdline_fflush(FILE *stream)
         {
           /* ### Issue #3014: Return a specific error for broken pipes,
            * ### with a single element in the error chain. */
-          if (APR_STATUS_IS_EPIPE(apr_get_os_error()))
+          if (SVN__APR_STATUS_IS_EPIPE(apr_get_os_error()))
             return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
           else
             return svn_error_wrap_apr(apr_get_os_error(), _("Write error"));
@@ -394,10 +404,12 @@ svn_cmdline_fflush(FILE *stream)
 
 const char *svn_cmdline_output_encoding(apr_pool_t *pool)
 {
+#ifdef CMDLINE_USE_CUSTOM_ENCODING
   if (output_encoding)
     return apr_pstrdup(pool, output_encoding);
-  else
-    return SVN_APR_LOCALE_CHARSET;
+#endif
+
+  return SVN_APR_LOCALE_CHARSET;
 }
 
 int

Propchange: subversion/branches/log-addressing/subversion/libsvn_subr/compress.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/config.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/config.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/config.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/config.c Tue Oct 15 23:44:41 2013
@@ -23,6 +23,8 @@
 
 
 
+#include <assert.h>
+
 #define APR_WANT_STRFUNC
 #define APR_WANT_MEMFUNC
 #include <apr_want.h>
@@ -93,6 +95,7 @@ svn_config_create2(svn_config_t **cfgp,
   cfg->tmp_value = svn_stringbuf_create_empty(result_pool);
   cfg->section_names_case_sensitive = section_names_case_sensitive;
   cfg->option_names_case_sensitive = option_names_case_sensitive;
+  cfg->read_only = FALSE;
 
   *cfgp = cfg;
   return SVN_NO_ERROR;
@@ -484,7 +487,13 @@ make_string_from_option(const char **val
        */
       if (opt->value && strchr(opt->value, '%'))
         {
-          apr_pool_t *tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
+          apr_pool_t *tmp_pool;
+
+          /* setting read-only mode should have expanded all values
+           * automatically. */
+          assert(!cfg->read_only);
+
+          tmp_pool = (x_pool ? x_pool : svn_pool_create(cfg->x_pool));
 
           expand_option_value(cfg, section, opt->value, &opt->x_value, tmp_pool);
           opt->expanded = TRUE;
@@ -687,6 +696,15 @@ svn_config_set(svn_config_t *cfg,
   cfg_section_t *sec;
   cfg_option_t *opt;
 
+  /* Ignore write attempts to r/o configurations.
+   * 
+   * Since we should never try to modify r/o data, trigger an assertion
+   * in debug mode.
+   */
+  assert(!cfg->read_only);
+  if (cfg->read_only)
+    return;
+
   remove_expansions(cfg);
 
   opt = find_option(cfg, section, option, &sec);

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/config_file.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/config_file.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/config_file.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/config_file.c Tue Oct 15 23:44:41 2013
@@ -37,6 +37,7 @@
 #include "svn_ctype.h"
 
 #include "svn_private_config.h"
+#include "private/svn_subr_private.h"
 
 #ifdef __HAIKU__
 #  include <FindDirectory.h>
@@ -418,9 +419,48 @@ svn_config__sys_config_path(const char *
   return SVN_NO_ERROR;
 }
 
+/* Callback for svn_config_enumerate2: Continue to next value. */
+static svn_boolean_t
+expand_value(const char *name,
+             const char *value,
+             void *baton,
+             apr_pool_t *pool)
+{
+  return TRUE;
+}
+
+/* Callback for svn_config_enumerate_sections2:
+ * Enumerate and implicitly expand all values in this section.
+ */
+static svn_boolean_t
+expand_values_in_section(const char *name,
+                         void *baton,
+                         apr_pool_t *pool)
+{
+  svn_config_t *cfg = baton;
+  svn_config_enumerate2(cfg, name, expand_value, NULL, pool);
+
+  return TRUE;
+}
+
 
 /*** Exported interfaces. ***/
 
+void
+svn_config__set_read_only(svn_config_t *cfg,
+                          apr_pool_t *scratch_pool)
+{
+  /* expand all items such that later calls to getters won't need to
+   * change internal state */
+  svn_config_enumerate_sections2(cfg, expand_values_in_section,
+                                 cfg, scratch_pool);
+
+  /* now, any modification attempt will be ignored / trigger an assertion
+   * in debug mode */
+  cfg->read_only = TRUE;
+}
+
+
 
 svn_error_t *
 svn_config__parse_file(svn_config_t *cfg, const char *file,

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/config_impl.h
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/config_impl.h?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/config_impl.h (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/config_impl.h Tue Oct 15 23:44:41 2013
@@ -70,8 +70,11 @@ struct svn_config_t
 
   /* Specifies whether option names are populated case sensitively. */
   svn_boolean_t option_names_case_sensitive;
-};
 
+  /* When set, all modification attempts will be ignored.
+   * In debug mode, we will trigger an assertion. */
+  svn_boolean_t read_only;
+};
 
 /* Read sections and options from a file. */
 svn_error_t *svn_config__parse_file(svn_config_t *cfg,

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/deprecated.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/deprecated.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/deprecated.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/deprecated.c Tue Oct 15 23:44:41 2013
@@ -1250,6 +1250,8 @@ svn_xml_make_header(svn_stringbuf_t **st
   svn_xml_make_header2(str, NULL, pool);
 }
 
+
+/*** From utf.c ***/
 void
 svn_utf_initialize(apr_pool_t *pool)
 {
@@ -1257,6 +1259,18 @@ svn_utf_initialize(apr_pool_t *pool)
 }
 
 svn_error_t *
+svn_utf_cstring_from_utf8_ex(const char **dest,
+                             const char *src,
+                             const char *topage,
+                             const char *convset_key,
+                             apr_pool_t *pool)
+{
+  return svn_utf_cstring_from_utf8_ex2(dest, src, topage, pool);
+}
+
+
+/*** From subst.c ***/
+svn_error_t *
 svn_subst_build_keywords(svn_subst_keywords_t *kw,
                          const char *keywords_val,
                          const char *rev,
@@ -1306,3 +1320,4 @@ svn_ver_check_list(const svn_version_t *
 {
   return svn_ver_check_list2(my_version, checklist, svn_ver_compatible);
 }
+

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/dirent_uri.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/dirent_uri.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/dirent_uri.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/dirent_uri.c Tue Oct 15 23:44:41 2013
@@ -1862,6 +1862,9 @@ svn_uri_is_canonical(const char *uri, ap
 #endif /* SVN_USE_DOS_PATHS */
 
   /* Now validate the rest of the URI. */
+  seg = ptr;
+  while (*ptr && (*ptr != '/'))
+    ptr++;
   while(1)
     {
       apr_size_t seglen = ptr - seg;
@@ -1880,9 +1883,8 @@ svn_uri_is_canonical(const char *uri, ap
 
       if (*ptr == '/')
         ptr++;
-      seg = ptr;
-
 
+      seg = ptr;
       while (*ptr && (*ptr != '/'))
         ptr++;
     }

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/io.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/io.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/io.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/io.c Tue Oct 15 23:44:41 2013
@@ -3487,7 +3487,7 @@ do_io_file_wrapper_cleanup(apr_file_t *f
 
   /* ### Issue #3014: Return a specific error for broken pipes,
    * ### with a single element in the error chain. */
-  if (APR_STATUS_IS_EPIPE(status))
+  if (SVN__APR_STATUS_IS_EPIPE(status))
     return svn_error_create(SVN_ERR_IO_PIPE_WRITE_ERROR, NULL, NULL);
 
   if (name)

Propchange: subversion/branches/log-addressing/subversion/libsvn_subr/packed_data.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/prefix_string.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/prefix_string.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/prefix_string.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/prefix_string.c Tue Oct 15 23:44:41 2013
@@ -219,7 +219,7 @@ svn_prefix_string__create(svn_prefix_tre
       new_node->sub_nodes = apr_palloc(tree->pool, sizeof(node_t *));
       new_node->sub_nodes[0] = sub_node;
 
-      memcpy(sub_node->key.data, sub_node->key.data + match, 8 - match);
+      memmove(sub_node->key.data, sub_node->key.data + match, 8 - match);
 
       /* replace old sub-node with new one and continue lookup */
       sub_node->key.prefix = new_node;

Propchange: subversion/branches/log-addressing/subversion/libsvn_subr/prefix_string.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/sorts.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/sorts.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/sorts.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/sorts.c Tue Oct 15 23:44:41 2013
@@ -453,4 +453,4 @@ svn_priority_queue__push(svn_priority_qu
 
   memcpy(apr_array_push(queue->elements), element, queue->elements->elt_size);
   heap_bubble_down(queue, queue->elements->nelts - 1);
-}
\ No newline at end of file
+}

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/stream.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/stream.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/stream.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/stream.c Tue Oct 15 23:44:41 2013
@@ -1379,6 +1379,36 @@ svn_stream_checksummed(svn_stream_t *str
 
 
 /* Miscellaneous stream functions. */
+
+svn_error_t *
+svn_stringbuf_from_stream(svn_stringbuf_t **str,
+                          svn_stream_t *stream,
+                          apr_size_t len_hint,
+                          apr_pool_t *pool)
+{
+#define MIN_READ_SIZE 64
+
+  apr_size_t to_read = 0;
+  svn_stringbuf_t *text
+    = svn_stringbuf_create_ensure(len_hint ? len_hint : MIN_READ_SIZE, pool);
+
+  do
+    {
+      to_read = text->blocksize - 1 - text->len;
+      SVN_ERR(svn_stream_read(stream, text->data + text->len, &to_read));
+      text->len += to_read;
+
+      if (to_read && text->blocksize < text->len + MIN_READ_SIZE)
+        svn_stringbuf_ensure(text, text->blocksize * 2);
+    }
+  while (to_read);
+
+  text->data[text->len] = '\0';
+  *str = text;
+
+  return SVN_NO_ERROR;
+}
+
 struct stringbuf_stream_baton
 {
   svn_stringbuf_t *str;

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/string.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/string.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/string.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/string.c Tue Oct 15 23:44:41 2013
@@ -53,9 +53,9 @@ membuf_create(void **data, apr_size_t *s
   /* apr_palloc will allocate multiples of 8.
    * Thus, we would waste some of that memory if we stuck to the
    * smaller size. Note that this is safe even if apr_palloc would
-   * use some other aligment or none at all. */
+   * use some other alignment or none at all. */
   minimum_size = APR_ALIGN_DEFAULT(minimum_size);
-  *data = (!minimum_size ? NULL : apr_palloc(pool, minimum_size));
+  *data = apr_palloc(pool, minimum_size);
   *size = minimum_size;
 }
 
@@ -121,7 +121,10 @@ svn_membuf__resize(svn_membuf_t *membuf,
   const apr_size_t old_size = membuf->size;
 
   membuf_ensure(&membuf->data, &membuf->size, size, membuf->pool);
-  if (membuf->data && old_data && old_data != membuf->data)
+
+  /* If we re-allocated MEMBUF->DATA, it cannot be NULL.
+   * Statically initialized membuffers (OLD_DATA) may be NULL, though. */
+  if (old_data && old_data != membuf->data)
     memcpy(membuf->data, old_data, old_size);
 }
 

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/utf.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/utf.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/utf.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/utf.c Tue Oct 15 23:44:41 2013
@@ -934,18 +934,6 @@ svn_utf_cstring_from_utf8_ex2(const char
   return err;
 }
 
-
-svn_error_t *
-svn_utf_cstring_from_utf8_ex(const char **dest,
-                             const char *src,
-                             const char *topage,
-                             const char *convset_key,
-                             apr_pool_t *pool)
-{
-  return svn_utf_cstring_from_utf8_ex2(dest, src, topage, pool);
-}
-
-
 const char *
 svn_utf__cstring_from_utf8_fuzzy(const char *src,
                                  apr_pool_t *pool,

Propchange: subversion/branches/log-addressing/subversion/libsvn_subr/utf8proc.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/log-addressing/subversion/libsvn_subr/utf8proc/utf8proc.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/log-addressing/subversion/libsvn_subr/utf8proc/utf8proc.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/log-addressing/subversion/libsvn_subr/utf8proc/utf8proc_data.c
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/log-addressing/subversion/libsvn_subr/win32_crashrpt.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_subr/win32_crashrpt.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_subr/win32_crashrpt.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_subr/win32_crashrpt.c Tue Oct 15 23:44:41 2013
@@ -427,13 +427,15 @@ write_var_values(PSYMBOL_INFO sym_info, 
 
       format_value(value_str, sym_info->ModBase, sym_info->TypeIndex,
                    (void *)var_data);
-      fprintf(log_file, "%s=%s", sym_info->Name, value_str);
+      fprintf(log_file, "%.*s=%s", (int)sym_info->NameLen, sym_info->Name,
+              value_str);
     }
   if (!log_params && sym_info->Flags & SYMFLAG_LOCAL)
     {
       format_value(value_str, sym_info->ModBase, sym_info->TypeIndex,
                    (void *)var_data);
-      fprintf(log_file, "        %s = %s\n", sym_info->Name, value_str);
+      fprintf(log_file, "        %.*s = %s\n", (int)sym_info->NameLen,
+              sym_info->Name, value_str);
     }
 
   return TRUE;
@@ -466,8 +468,10 @@ write_function_detail(STACKFRAME64 stack
   if (SymFromAddr_(proc, stack_frame.AddrPC.Offset, &func_disp, pIHS))
     {
       fprintf(log_file,
-                    "#%d  0x%08I64x in %.200s(",
-                    nr_of_frame, stack_frame.AddrPC.Offset, pIHS->Name);
+                    "#%d  0x%08I64x in %.*s(",
+                    nr_of_frame, stack_frame.AddrPC.Offset,
+                    pIHS->NameLen > 200 ? 200 : (int)pIHS->NameLen,
+                    pIHS->Name);
 
       /* restrict symbol enumeration to this frame only */
       ih_stack_frame.InstructionOffset = stack_frame.AddrPC.Offset;

Modified: subversion/branches/log-addressing/subversion/libsvn_wc/old-and-busted.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_wc/old-and-busted.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_wc/old-and-busted.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_wc/old-and-busted.c Tue Oct 15 23:44:41 2013
@@ -811,11 +811,15 @@ atts_to_entry(svn_wc_entry_t **new_entry
 
      ### not used by loggy; no need to set MODIFY_FLAGS  */
   entry->url = extract_string(atts, ENTRIES_ATTR_URL, pool);
+  if (entry->url)
+    entry->url = svn_uri_canonicalize(entry->url, pool);
 
   /* Set up repository root.  Make sure it is a prefix of url.
 
      ### not used by loggy; no need to set MODIFY_FLAGS  */
   entry->repos = extract_string(atts, ENTRIES_ATTR_REPOS, pool);
+  if (entry->repos)
+    entry->repos = svn_uri_canonicalize(entry->repos, pool);
 
   if (entry->url && entry->repos
       && !svn_uri__is_ancestor(entry->repos, entry->url))

Modified: subversion/branches/log-addressing/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_wc/update_editor.c?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/log-addressing/subversion/libsvn_wc/update_editor.c Tue Oct 15 23:44:41 2013
@@ -3010,18 +3010,55 @@ absent_node(const char *path,
       kind = svn_node_unknown;
     }
 
-  if (status == svn_wc__db_status_normal
-      && kind == svn_node_dir)
+  if (status == svn_wc__db_status_normal)
     {
-      /* We found an obstructing working copy!
+      svn_boolean_t wcroot;
+      /* We found an obstructing working copy or a file external! */
 
-         We can do two things now:
-            1) notify the user, record a skip, etc.
-            2) Just record the absent node in BASE in the parent
-               working copy.
+      SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
+                                   scratch_pool));
 
-         As option 2 happens to be exactly what we do anyway, lets do that.
-      */
+      if (wcroot)
+        {
+          /*
+             We have an obstructing working copy; possibly a directory external
+
+             We can do two things now:
+             1) notify the user, record a skip, etc.
+             2) Just record the absent node in BASE in the parent
+                working copy.
+
+             As option 2 happens to be exactly what we do anyway, fall through.
+           */
+        }
+      else
+        {
+          /* The server asks us to replace a file external
+             (Existing BASE node; not reported by the working copy crawler or
+              there would have been a delete_entry() call.
+
+             There is no way we can store this state in the working copy as
+             the BASE layer is already filled.
+
+             We could error out, but that is not helping anybody; the user is not
+             even seeing with what the file external would be replaced, so let's
+             report a skip and continue the update.
+           */
+
+          if (eb->notify_func)
+            {
+              svn_wc_notify_t *notify;
+              notify = svn_wc_create_notify(
+                                    local_abspath,
+                                    svn_wc_notify_update_skip_obstruction,
+                                    scratch_pool);
+
+              eb->notify_func(eb->notify_baton, notify, scratch_pool);
+            }
+
+          svn_pool_destroy(scratch_pool);
+          return SVN_NO_ERROR;
+        }
     }
   else if (status == svn_wc__db_status_not_present
            || status == svn_wc__db_status_server_excluded

Modified: subversion/branches/log-addressing/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/branches/log-addressing/subversion/libsvn_wc/wc-queries.sql?rev=1532597&r1=1532596&r2=1532597&view=diff
==============================================================================
--- subversion/branches/log-addressing/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/log-addressing/subversion/libsvn_wc/wc-queries.sql Tue Oct 15 23:44:41 2013
@@ -1571,7 +1571,7 @@ WHERE wc_id = ?1
   AND moved_to IS NOT NULL
 
 -- STMT_SELECT_MOVED_OUTSIDE
-SELECT local_relpath, moved_to FROM nodes
+SELECT local_relpath, moved_to, op_depth FROM nodes
 WHERE wc_id = ?1
   AND (local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(local_relpath, ?2))
   AND op_depth >= ?3