You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by br...@apache.org on 2014/01/09 10:31:15 UTC

svn commit: r1556765 [3/12] - in /subversion/branches/fsfs-ucsnorm: ./ contrib/server-side/fsfsfixer/fixer/ subversion/bindings/javahl/native/ subversion/bindings/javahl/native/jniwrapper/ subversion/bindings/javahl/src/org/apache/subversion/javahl/ su...

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_base/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_base/tree.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_base/tree.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_base/tree.c Thu Jan  9 09:31:10 2014
@@ -1028,6 +1028,38 @@ base_node_id(const svn_fs_id_t **id_p,
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+base_node_relation(svn_fs_node_relation_t *relation,
+                   svn_fs_root_t *root_a, const char *path_a,
+                   svn_fs_root_t *root_b, const char *path_b,
+                   apr_pool_t *pool)
+{
+  const svn_fs_id_t *id_a, *id_b;
+
+  /* Paths from different repository are never related. */
+  if (root_a->fs != root_b->fs)
+    {
+      *relation = svn_fs_node_unrelated;
+      return SVN_NO_ERROR;
+    }
+
+  /* Naive implementation. */
+  SVN_ERR(base_node_id(&id_a, root_a, path_a, pool));
+  SVN_ERR(base_node_id(&id_b, root_b, path_b, pool));
+
+  switch (svn_fs_base__id_compare(id_a, id_b))
+    {
+      case 0:  *relation = svn_fs_node_same;
+               break;
+      case 1:  *relation = svn_fs_node_common_anchestor;
+               break;
+      default: *relation = svn_fs_node_unrelated;
+               break;
+    }
+
+  return SVN_NO_ERROR;
+}
+
 
 struct node_created_rev_args {
   svn_revnum_t revision;
@@ -2578,7 +2610,6 @@ struct commit_args
 {
   svn_fs_txn_t *txn;
   svn_revnum_t new_rev;
-  svn_boolean_t set_timestamp;
 };
 
 
@@ -2638,7 +2669,7 @@ txn_body_commit(void *baton, trail_t *tr
 
   /* Else, commit the txn. */
   return svn_fs_base__dag_commit_txn(&(args->new_rev), txn, trail,
-                                     args->set_timestamp, trail->pool);
+                                     trail->pool);
 }
 
 
@@ -2648,7 +2679,6 @@ svn_error_t *
 svn_fs_base__commit_txn(const char **conflict_p,
                         svn_revnum_t *new_rev,
                         svn_fs_txn_t *txn,
-                        svn_boolean_t set_timestamp,
                         apr_pool_t *pool)
 {
   /* How do commits work in Subversion?
@@ -2752,7 +2782,6 @@ svn_fs_base__commit_txn(const char **con
 
       /* Try to commit. */
       commit_args.txn = txn;
-      commit_args.set_timestamp = set_timestamp;
       err = svn_fs_base__retry_txn(fs, txn_body_commit, &commit_args,
                                    FALSE, subpool);
       if (err && (err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE))
@@ -5403,6 +5432,7 @@ static root_vtable_t root_vtable = {
   base_check_path,
   base_node_history,
   base_node_id,
+  base_node_relation,
   base_node_created_rev,
   base_node_origin_rev,
   base_node_created_path,

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_base/tree.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_base/tree.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_base/tree.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_base/tree.h Thu Jan  9 09:31:10 2014
@@ -42,7 +42,6 @@ svn_error_t *svn_fs_base__deltify(svn_fs
 
 svn_error_t *svn_fs_base__commit_txn(const char **conflict_p,
                                      svn_revnum_t *new_rev, svn_fs_txn_t *txn,
-                                     svn_boolean_t set_timestamp,
                                      apr_pool_t *pool);
 
 svn_error_t *svn_fs_base__txn_root(svn_fs_root_t **root_p, svn_fs_txn_t *txn,

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/cached_data.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/cached_data.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/cached_data.c Thu Jan  9 09:31:10 2014
@@ -26,7 +26,11 @@
 
 #include "svn_hash.h"
 #include "svn_ctype.h"
+#include "svn_sorts.h"
+#include "private/svn_delta_private.h"
 #include "private/svn_io_private.h"
+#include "private/svn_sorts_private.h"
+#include "private/svn_subr_private.h"
 #include "private/svn_temp_serializer.h"
 
 #include "fs_fs.h"
@@ -83,10 +87,13 @@ dbg_log_access(svn_fs_t *fs,
   const char *type = types[item_type];
   const char *pack = "";
   apr_off_t offset;
+  svn_fs_fs__revision_file_t rev_file;
+
+  svn_fs_fs__init_revision_file(&rev_file, fs, revision, scratch_pool);
 
   /* determine rev / pack file offset */
-  SVN_ERR(svn_fs_fs__item_offset(&offset, fs, revision, NULL, item_index,
-                                 scratch_pool));
+  SVN_ERR(svn_fs_fs__item_offset(&offset, fs, &rev_file, revision, NULL,
+                                 item_index, scratch_pool));
 
   /* constructing the pack file description */
   if (revision < ffd->min_unpacked_rev)
@@ -148,8 +155,8 @@ dbg_log_access(svn_fs_t *fs,
   if (svn_fs_fs__use_log_addressing(fs, revision))
     {
       /* reverse index lookup: get item description in ENTRY */
-      SVN_ERR(svn_fs_fs__p2l_entry_lookup(&entry, fs, revision, offset,
-                                          scratch_pool));
+      SVN_ERR(svn_fs_fs__p2l_entry_lookup(&entry, fs, &rev_file, revision,
+                                          offset, scratch_pool));
       if (entry)
         {
           /* more details */
@@ -909,13 +916,20 @@ svn_fs_fs__check_rep(representation_t *r
 
 svn_error_t *
 svn_fs_fs__rep_chain_length(int *chain_length,
+                            int *shard_count,
                             representation_t *rep,
                             svn_fs_t *fs,
                             apr_pool_t *pool)
 {
-  int count = 0;
+  fs_fs_data_t *ffd = fs->fsap_data;
+  svn_revnum_t shard_size = ffd->max_files_per_dir
+                          ? ffd->max_files_per_dir
+                          : 1;
   apr_pool_t *sub_pool = svn_pool_create(pool);
   svn_boolean_t is_delta = FALSE;
+  int count = 0;
+  int shards = 1;
+  svn_revnum_t last_shard = rep->revision / shard_size;
   
   /* Check whether the length of the deltification chain is acceptable.
    * Otherwise, shared reps may form a non-skipping delta chain in
@@ -932,6 +946,12 @@ svn_fs_fs__rep_chain_length(int *chain_l
   do
     {
       rep_state_t *rep_state;
+      if (base_rep.revision / shard_size != last_shard)
+        {
+          last_shard = base_rep.revision / shard_size;
+          ++shards;
+        }
+
       SVN_ERR(create_rep_state_body(&rep_state,
                                     &header,
                                     &file_hint,
@@ -955,6 +975,7 @@ svn_fs_fs__rep_chain_length(int *chain_l
   while (is_delta && base_rep.revision);
 
   *chain_length = count;
+  *shard_count = shards;
   svn_pool_destroy(sub_pool);
 
   return SVN_NO_ERROR;
@@ -1809,6 +1830,23 @@ delta_read_md5_digest(void *baton)
   return drb->md5_digest;
 }
 
+/* Return a txdelta stream for on-disk representation REP_STATE
+ * of TARGET.  Allocate the result in POOL.
+ */
+static svn_txdelta_stream_t *
+get_storaged_delta_stream(rep_state_t *rep_state,
+                          node_revision_t *target,
+                          apr_pool_t *pool)
+{
+  /* Create the delta read baton. */
+  struct delta_read_baton *drb = apr_pcalloc(pool, sizeof(*drb));
+  drb->rs = rep_state;
+  memcpy(drb->md5_digest, target->data_rep->md5_digest,
+         sizeof(drb->md5_digest));
+  return svn_txdelta_stream_create(drb, delta_read_next_window,
+                                   delta_read_md5_digest, pool);
+}
+
 svn_error_t *
 svn_fs_fs__get_file_delta_stream(svn_txdelta_stream_t **stream_p,
                                  svn_fs_t *fs,
@@ -1817,33 +1855,44 @@ svn_fs_fs__get_file_delta_stream(svn_txd
                                  apr_pool_t *pool)
 {
   svn_stream_t *source_stream, *target_stream;
+  rep_state_t *rep_state;
+  svn_fs_fs__rep_header_t *rep_header;
 
-  /* Try a shortcut: if the target is stored as a delta against the source,
-     then just use that delta. */
-  if (source && source->data_rep && target->data_rep)
+  /* Try a shortcut: if the target is stored as a delta against the
+     source, then just use that delta. */
+  if (target->data_rep)
     {
-      rep_state_t *rep_state;
-      svn_fs_fs__rep_header_t *rep_header;
-
       /* Read target's base rep if any. */
       SVN_ERR(create_rep_state(&rep_state, &rep_header, NULL,
-                               target->data_rep, fs, pool));
-      /* If that matches source, then use this delta as is. */
-      if (rep_header->type == svn_fs_fs__rep_self_delta
-          || (rep_header->type == svn_fs_fs__rep_delta
+                                target->data_rep, fs, pool));
+
+      if (source && source->data_rep && target->data_rep)
+        {
+          /* If that matches source, then use this delta as is.
+             Note that we want an actual delta here.  E.g. a self-delta would
+             not be good enough. */
+          if (rep_header->type == svn_fs_fs__rep_delta
               && rep_header->base_revision == source->data_rep->revision
-              && rep_header->base_item_index == source->data_rep->item_index))
+              && rep_header->base_item_index == source->data_rep->item_index)
+            {
+              *stream_p = get_storaged_delta_stream(rep_state, target, pool);
+              return SVN_NO_ERROR;
+            }
+        }
+      else if (!source)
         {
-          /* Create the delta read baton. */
-          struct delta_read_baton *drb = apr_pcalloc(pool, sizeof(*drb));
-          drb->rs = rep_state;
-          memcpy(drb->md5_digest, target->data_rep->md5_digest,
-                 sizeof(drb->md5_digest));
-          *stream_p = svn_txdelta_stream_create(drb, delta_read_next_window,
-                                                delta_read_md5_digest, pool);
-          return SVN_NO_ERROR;
+          /* We want a self-delta. There is a fair chance that TARGET got
+             added in this revision and is already stored in the requested
+             format. */
+          if (rep_header->type == svn_fs_fs__rep_self_delta)
+            {
+              *stream_p = get_storaged_delta_stream(rep_state, target, pool);
+              return SVN_NO_ERROR;
+            }
         }
-      else if (rep_state->sfile->rfile)
+
+      /* Don't keep file handles open for longer than necessary. */
+      if (rep_state->sfile->rfile)
         {
           SVN_ERR(svn_fs_fs__close_revision_file(rep_state->sfile->rfile));
           rep_state->sfile->rfile = NULL;
@@ -1866,28 +1915,173 @@ svn_fs_fs__get_file_delta_stream(svn_txd
   return SVN_NO_ERROR;
 }
 
+/* Return TRUE when all svn_fs_dirent_t* in ENTRIES are already sorted
+   by their respective name. */
+static svn_boolean_t
+sorted(apr_array_header_t *entries)
+{
+  int i;
+
+  const svn_fs_dirent_t * const *dirents = (const void *)entries->elts;
+  for (i = 0; i < entries->nelts-1; ++i)
+    if (strcmp(dirents[i]->name, dirents[i+1]->name) > 0)
+      return FALSE;
+
+  return TRUE;
+}
+
+/* Compare the names of the two dirents given in **A and **B. */
+static int
+compare_dirents(const void *a, const void *b)
+{
+  const svn_fs_dirent_t *lhs = *((const svn_fs_dirent_t * const *) a);
+  const svn_fs_dirent_t *rhs = *((const svn_fs_dirent_t * const *) b);
+
+  return strcmp(lhs->name, rhs->name);
+}
+
+/* Compare the name of the dirents given in **A with the C string in *B. */
+static int
+compare_dirent_name(const void *a, const void *b)
+{
+  const svn_fs_dirent_t *lhs = *((const svn_fs_dirent_t * const *) a);
+  const char *rhs = b;
+
+  return strcmp(lhs->name, rhs);
+}
+
+/* Into ENTRIES, read all directories entries from the key-value text in
+ * STREAM.  If INCREMENTAL is TRUE, read until the end of the STREAM and
+ * update the data.  ID is provided for nicer error messages.
+ */
+static svn_error_t *
+read_dir_entries(apr_array_header_t *entries,
+                 svn_stream_t *stream,
+                 svn_boolean_t incremental,
+                 const svn_fs_id_t *id,
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
+{
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+  apr_hash_t *hash = incremental ? svn_hash__make(scratch_pool) : NULL;
+  const char *terminator = SVN_HASH_TERMINATOR;
+
+  /* Read until the terminator (non-incremental) or the end of STREAM
+     (incremental mode).  In the latter mode, we use a temporary HASH
+     to make updating and removing entries cheaper. */
+  while (1)
+    {
+      svn_hash__entry_t entry;
+      svn_fs_dirent_t *dirent;
+      char *str;
+
+      svn_pool_clear(iterpool);
+      SVN_ERR(svn_hash__read_entry(&entry, stream, terminator,
+                                   incremental, iterpool));
+
+      /* End of directory? */
+      if (entry.key == NULL)
+        {
+          /* In incremental mode, we skip the terminator and read the
+             increments following it until the end of the stream. */
+          if (incremental && terminator)
+            terminator = NULL;
+          else
+            break;
+        }
+
+      /* Deleted entry? */
+      if (entry.val == NULL)
+        {
+          /* We must be in incremental mode */
+          assert(hash);
+          apr_hash_set(hash, entry.key, entry.keylen, NULL);
+          continue;
+        }
+
+      /* Add a new directory entry. */
+      dirent = apr_pcalloc(result_pool, sizeof(*dirent));
+      dirent->name = apr_pstrmemdup(result_pool, entry.key, entry.keylen);
+
+      str = svn_cstring_tokenize(" ", &entry.val);
+      if (str == NULL)
+        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                           _("Directory entry corrupt in '%s'"),
+                           svn_fs_fs__id_unparse(id, scratch_pool)->data);
+
+      if (strcmp(str, SVN_FS_FS__KIND_FILE) == 0)
+        {
+          dirent->kind = svn_node_file;
+        }
+      else if (strcmp(str, SVN_FS_FS__KIND_DIR) == 0)
+        {
+          dirent->kind = svn_node_dir;
+        }
+      else
+        {
+          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                           _("Directory entry corrupt in '%s'"),
+                           svn_fs_fs__id_unparse(id, scratch_pool)->data);
+        }
+
+      str = svn_cstring_tokenize(" ", &entry.val);
+      if (str == NULL)
+        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
+                           _("Directory entry corrupt in '%s'"),
+                           svn_fs_fs__id_unparse(id, scratch_pool)->data);
+
+      dirent->id = svn_fs_fs__id_parse(str, strlen(str), result_pool);
+
+      /* In incremental mode, update the hash; otherwise, write to the
+       * final array. */
+      if (incremental)
+        apr_hash_set(hash, entry.key, entry.keylen, dirent);
+      else
+        APR_ARRAY_PUSH(entries, svn_fs_dirent_t *) = dirent;
+    }
+
+  /* Convert container to a sorted array. */
+  if (incremental)
+    {
+      apr_hash_index_t *hi;
+      for (hi = apr_hash_first(iterpool, hash); hi; hi = apr_hash_next(hi))
+        APR_ARRAY_PUSH(entries, svn_fs_dirent_t *)
+          = svn__apr_hash_index_val(hi);
+    }
+
+  if (!sorted(entries))
+    qsort(entries->elts, entries->nelts, entries->elt_size, compare_dirents);
+
+  svn_pool_destroy(iterpool);
+
+  return SVN_NO_ERROR;
+}
+
 /* Fetch the contents of a directory into ENTRIES.  Values are stored
    as filename to string mappings; further conversion is necessary to
    convert them into svn_fs_dirent_t values. */
 static svn_error_t *
-get_dir_contents(apr_hash_t *entries,
+get_dir_contents(apr_array_header_t **entries,
                  svn_fs_t *fs,
                  node_revision_t *noderev,
-                 apr_pool_t *pool)
+                 apr_pool_t *result_pool,
+                 apr_pool_t *scratch_pool)
 {
   svn_stream_t *contents;
 
+  *entries = apr_array_make(result_pool, 16, sizeof(svn_fs_dirent_t *));
   if (noderev->data_rep && svn_fs_fs__id_txn_used(&noderev->data_rep->txn_id))
     {
       const char *filename
-        = svn_fs_fs__path_txn_node_children(fs, noderev->id, pool);
+        = svn_fs_fs__path_txn_node_children(fs, noderev->id, scratch_pool);
 
       /* The representation is mutable.  Read the old directory
          contents from the mutable children file, followed by the
          changes we've made in this transaction. */
-      SVN_ERR(svn_stream_open_readonly(&contents, filename, pool, pool));
-      SVN_ERR(svn_hash_read2(entries, contents, SVN_HASH_TERMINATOR, pool));
-      SVN_ERR(svn_hash_read_incremental(entries, contents, NULL, pool));
+      SVN_ERR(svn_stream_open_readonly(&contents, filename, scratch_pool,
+                                       scratch_pool));
+      SVN_ERR(read_dir_entries(*entries, contents, TRUE,  noderev->id,
+                               result_pool, scratch_pool));
       SVN_ERR(svn_stream_close(contents));
     }
   else if (noderev->data_rep)
@@ -1896,7 +2090,7 @@ get_dir_contents(apr_hash_t *entries,
        * Also undeltify content before parsing it. Otherwise, we could only
        * parse it byte-by-byte.
        */
-      apr_pool_t *text_pool = svn_pool_create(pool);
+      apr_pool_t *text_pool = svn_pool_create(scratch_pool);
       apr_size_t len = noderev->data_rep->expanded_size
                      ? (apr_size_t)noderev->data_rep->expanded_size
                      : (apr_size_t)noderev->data_rep->size;
@@ -1910,7 +2104,8 @@ get_dir_contents(apr_hash_t *entries,
 
       /* de-serialize hash */
       contents = svn_stream_from_stringbuf(text, text_pool);
-      SVN_ERR(svn_hash_read2(entries, contents, SVN_HASH_TERMINATOR, pool));
+      SVN_ERR(read_dir_entries(*entries, contents, FALSE,  noderev->id,
+                               result_pool, scratch_pool));
 
       svn_pool_destroy(text_pool);
     }
@@ -1919,66 +2114,6 @@ get_dir_contents(apr_hash_t *entries,
 }
 
 
-/* Given a hash STR_ENTRIES with values as svn_string_t as specified
-   in an FSFS directory contents listing, return a hash of dirents in
-   *ENTRIES_P.  Use ID to generate more helpful error messages.
-   Perform allocations in POOL. */
-static svn_error_t *
-parse_dir_entries(apr_hash_t **entries_p,
-                  apr_hash_t *str_entries,
-                  const svn_fs_id_t *id,
-                  apr_pool_t *pool)
-{
-  apr_hash_index_t *hi;
-
-  *entries_p = apr_hash_make(pool);
-
-  /* Translate the string dir entries into real entries. */
-  for (hi = apr_hash_first(pool, str_entries); hi; hi = apr_hash_next(hi))
-    {
-      const char *name = svn__apr_hash_index_key(hi);
-      svn_string_t *str_val = svn__apr_hash_index_val(hi);
-      char *str, *last_str;
-      svn_fs_dirent_t *dirent = apr_pcalloc(pool, sizeof(*dirent));
-
-      last_str = apr_pstrdup(pool, str_val->data);
-      dirent->name = apr_pstrdup(pool, name);
-
-      str = svn_cstring_tokenize(" ", &last_str);
-      if (str == NULL)
-        return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                                 _("Directory entry corrupt in '%s'"),
-                                 svn_fs_fs__id_unparse(id, pool)->data);
-
-      if (strcmp(str, SVN_FS_FS__KIND_FILE) == 0)
-        {
-          dirent->kind = svn_node_file;
-        }
-      else if (strcmp(str, SVN_FS_FS__KIND_DIR) == 0)
-        {
-          dirent->kind = svn_node_dir;
-        }
-      else
-        {
-          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                                   _("Directory entry corrupt in '%s'"),
-                                   svn_fs_fs__id_unparse(id, pool)->data);
-        }
-
-      str = svn_cstring_tokenize(" ", &last_str);
-      if (str == NULL)
-          return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
-                                   _("Directory entry corrupt in '%s'"),
-                                   svn_fs_fs__id_unparse(id, pool)->data);
-
-      dirent->id = svn_fs_fs__id_parse(str, strlen(str), pool);
-
-      svn_hash_sets(*entries_p, dirent->name, dirent);
-    }
-
-  return SVN_NO_ERROR;
-}
-
 /* Return the cache object in FS responsible to storing the directory the
  * NODEREV plus the corresponding *KEY.  If no cache exists, return NULL.
  * PAIR_KEY must point to some key struct, which does not need to be
@@ -2019,40 +2154,49 @@ locate_dir_cache(svn_fs_t *fs,
 }
 
 svn_error_t *
-svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
+svn_fs_fs__rep_contents_dir(apr_array_header_t **entries_p,
                             svn_fs_t *fs,
                             node_revision_t *noderev,
-                            apr_pool_t *pool)
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool)
 {
   pair_cache_key_t pair_key = { 0 };
   const void *key;
-  apr_hash_t *unparsed_entries, *parsed_entries;
 
   /* find the cache we may use */
-  svn_cache__t *cache = locate_dir_cache(fs, &key, &pair_key, noderev, pool);
+  svn_cache__t *cache = locate_dir_cache(fs, &key, &pair_key, noderev,
+                                         scratch_pool);
   if (cache)
     {
       svn_boolean_t found;
 
-      SVN_ERR(svn_cache__get((void **)entries_p, &found, cache, key, pool));
+      SVN_ERR(svn_cache__get((void **)entries_p, &found, cache, key,
+                             result_pool));
       if (found)
         return SVN_NO_ERROR;
     }
 
-  /* Read in the directory hash. */
-  unparsed_entries = apr_hash_make(pool);
-  SVN_ERR(get_dir_contents(unparsed_entries, fs, noderev, pool));
-  SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries,
-                            noderev->id, pool));
+  /* Read in the directory contents. */
+  SVN_ERR(get_dir_contents(entries_p, fs, noderev, result_pool,
+                           scratch_pool));
 
   /* Update the cache, if we are to use one. */
   if (cache)
-    SVN_ERR(svn_cache__set(cache, key, parsed_entries, pool));
+    SVN_ERR(svn_cache__set(cache, key, *entries_p, scratch_pool));
 
-  *entries_p = parsed_entries;
   return SVN_NO_ERROR;
 }
 
+svn_fs_dirent_t *
+svn_fs_fs__find_dir_entry(apr_array_header_t *entries,
+                          const char *name,
+                          int *hint)
+{
+  svn_fs_dirent_t **result
+    = svn_sort__array_lookup(entries, name, hint, compare_dirent_name);
+  return result ? *result : NULL;
+}
+
 svn_error_t *
 svn_fs_fs__rep_contents_dir_entry(svn_fs_dirent_t **dirent,
                                   svn_fs_t *fs,
@@ -2083,18 +2227,18 @@ svn_fs_fs__rep_contents_dir_entry(svn_fs
   /* fetch data from disk if we did not find it in the cache */
   if (! found)
     {
-      apr_hash_t *entries;
+      apr_array_header_t *entries;
       svn_fs_dirent_t *entry;
       svn_fs_dirent_t *entry_copy = NULL;
 
       /* read the dir from the file system. It will probably be put it
          into the cache for faster lookup in future calls. */
       SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev,
-                                          scratch_pool));
+                                          scratch_pool, scratch_pool));
 
       /* find desired entry and return a copy in POOL, if found */
-      entry = svn_hash_gets(entries, name);
-      if (entry != NULL)
+      entry = svn_fs_fs__find_dir_entry(entries, name, NULL);
+      if (entry)
         {
           entry_copy = apr_palloc(result_pool, sizeof(*entry_copy));
           entry_copy->name = apr_pstrdup(result_pool, entry->name);
@@ -2169,7 +2313,7 @@ svn_fs_fs__get_changes(apr_array_header_
                        svn_revnum_t rev,
                        apr_pool_t *pool)
 {
-  apr_off_t changes_offset = -1;
+  apr_off_t changes_offset = SVN_FS_FS__ITEM_INDEX_CHANGES;
   svn_fs_fs__revision_file_t *revision_file;
   svn_boolean_t found;
   fs_fs_data_t *ffd = fs->fsap_data;
@@ -2285,12 +2429,29 @@ cache_windows(svn_filesize_t *fulltext_l
       svn_txdelta_window_t *window;
       apr_off_t start_offset = rs->start + rs->current;
       apr_off_t end_offset;
+      svn_boolean_t found = FALSE;
 
-      /* navigate to & read the current window */
+      /* We don't need to read the data again, if it is already in cache.
+       */
+      if (rs->window_cache)
+        {
+          window_cache_key_t key = {0};
+          SVN_ERR(svn_cache__has_key(&found, rs->window_cache,
+                                     get_window_key(&key, rs), pool));
+        }
+
+      /* navigate to the current window */
       SVN_ERR(rs_aligned_seek(rs, NULL, start_offset, pool));
-      SVN_ERR(svn_txdelta_read_svndiff_window(&window,
-                                              rs->sfile->rfile->stream,
-                                              rs->ver, pool));
+
+      /* Skip or actually read the window - depending on cache status. */
+      if (found)
+        SVN_ERR(svn_txdelta__read_svndiff_window_sizes(&window,
+                                                rs->sfile->rfile->stream,
+                                                rs->ver, pool));
+      else
+        SVN_ERR(svn_txdelta_read_svndiff_window(&window,
+                                                rs->sfile->rfile->stream,
+                                                rs->ver, pool));
 
       /* aggregate expanded window size */
       *fulltext_len += window->tview_len;
@@ -2304,7 +2465,8 @@ cache_windows(svn_filesize_t *fulltext_l
                                   "the end of the representation"));
 
       /* cache the window now */
-      SVN_ERR(set_cached_window(window, rs, pool));
+      if (!found)
+        SVN_ERR(set_cached_window(window, rs, pool));
 
       rs->chunk_index++;
     }

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/cached_data.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/cached_data.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/cached_data.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/cached_data.h Thu Jan  9 09:31:10 2014
@@ -58,9 +58,11 @@ svn_fs_fs__check_rep(representation_t *r
 
 /* Follow the representation delta chain in FS starting with REP.  The
    number of reps (including REP) in the chain will be returned in
-   *CHAIN_LENGTH.  Do any allocations in POOL. */
+   *CHAIN_LENGTH.  *SHARD_COUNT will be set to the number of shards
+   accessed.  Do any allocations in POOL. */
 svn_error_t *
 svn_fs_fs__rep_chain_length(int *chain_length,
+                            int *shard_count,
                             representation_t *rep,
                             svn_fs_t *fs,
                             apr_pool_t *pool);
@@ -98,15 +100,25 @@ svn_fs_fs__get_file_delta_stream(svn_txd
                                  node_revision_t *target,
                                  apr_pool_t *pool);
 
-/* Set *ENTRIES to an apr_hash_t of dirent structs that contain the
-   directory entries of node-revision NODEREV in filesystem FS.  The
-   returned table (and its keys and values) is allocated in POOL,
-   which is also used for temporary allocations. */
+/* Set *ENTRIES to an apr_array_header_t of dirent structs that contain
+   the directory entries of node-revision NODEREV in filesystem FS.  The
+   returned table is allocated in RESULT_POOL and entries are sorted
+   lexicographically.  SCRATCH_POOL is used for temporary allocations. */
 svn_error_t *
-svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
+svn_fs_fs__rep_contents_dir(apr_array_header_t **entries_p,
                             svn_fs_t *fs,
                             node_revision_t *noderev,
-                            apr_pool_t *pool);
+                            apr_pool_t *result_pool,
+                            apr_pool_t *scratch_pool);
+
+/* Return the directory entry from ENTRIES that matches NAME.  If no such
+   entry exists, return NULL.  If HINT is not NULL, set *HINT to the array
+   index of the entry returned.  Successive calls in a linear scan scenario
+   will be faster called with the same HINT variable. */
+svn_fs_dirent_t *
+svn_fs_fs__find_dir_entry(apr_array_header_t *entries,
+                          const char *name,
+                          int *hint);
 
 /* Set *DIRENT to the entry identified by NAME in the directory given
    by NODEREV in filesystem FS.  If no such entry exits, *DIRENT will

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/dag.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/dag.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/dag.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/dag.c Thu Jan  9 09:31:10 2014
@@ -415,7 +415,7 @@ make_entry(dag_node_t **child_p,
 
 
 svn_error_t *
-svn_fs_fs__dag_dir_entries(apr_hash_t **entries,
+svn_fs_fs__dag_dir_entries(apr_array_header_t **entries,
                            dag_node_t *node,
                            apr_pool_t *pool)
 {
@@ -427,7 +427,7 @@ svn_fs_fs__dag_dir_entries(apr_hash_t **
     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
                             _("Can't get entries of non-directory"));
 
-  return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool);
+  return svn_fs_fs__rep_contents_dir(entries, node->fs, noderev, pool, pool);
 }
 
 svn_error_t *
@@ -857,23 +857,16 @@ svn_fs_fs__dag_delete_if_mutable(svn_fs_
   /* Else it's mutable.  Recurse on directories... */
   if (node->kind == svn_node_dir)
     {
-      apr_hash_t *entries;
-      apr_hash_index_t *hi;
+      apr_array_header_t *entries;
+      int i;
 
-      /* Loop over hash entries */
+      /* Loop over directory entries */
       SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, node, pool));
       if (entries)
-        {
-          for (hi = apr_hash_first(pool, entries);
-               hi;
-               hi = apr_hash_next(hi))
-            {
-              svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
-
-              SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs, dirent->id,
-                                                       pool));
-            }
-        }
+        for (i = 0; i < entries->nelts; ++i)
+          SVN_ERR(svn_fs_fs__dag_delete_if_mutable(fs,
+                        APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *)->id,
+                        pool));
     }
 
   /* ... then delete the node itself, after deleting any mutable

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/dag.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/dag.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/dag.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/dag.h Thu Jan  9 09:31:10 2014
@@ -264,11 +264,10 @@ svn_fs_fs__dag_open(dag_node_t **child_p
                     apr_pool_t *scratch_pool);
 
 
-/* Set *ENTRIES_P to a hash table of NODE's entries.  The keys of the
-   table are entry names, and the values are svn_fs_dirent_t's.  The
-   returned table (and its keys and values) is allocated in POOL,
-   which is also used for temporary allocations. */
-svn_error_t *svn_fs_fs__dag_dir_entries(apr_hash_t **entries_p,
+/* Set *ENTRIES_P to an array of NODE's entries, sorted by entry names,
+   and the values are svn_fs_dirent_t's.  The returned table (and elements)
+   is allocated in POOL, which is also used for temporary allocations. */
+svn_error_t *svn_fs_fs__dag_dir_entries(apr_array_header_t **entries_p,
                                         dag_node_t *node,
                                         apr_pool_t *pool);
 

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/fs.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/fs.h Thu Jan  9 09:31:10 2014
@@ -81,6 +81,8 @@ extern "C" {
 /* Names of special files and file extensions for transactions */
 #define PATH_CHANGES       "changes"       /* Records changes made so far */
 #define PATH_TXN_PROPS     "props"         /* Transaction properties */
+#define PATH_TXN_PROPS_FINAL "props-final" /* Final transaction properties
+                                              before moving to revprops */
 #define PATH_NEXT_IDS      "next-ids"      /* Next temporary ID assignments */
 #define PATH_PREFIX_NODE   "node."         /* Prefix for node filename */
 #define PATH_EXT_TXN       ".txn"          /* Extension of txn dir */
@@ -285,23 +287,6 @@ typedef struct window_cache_key_t
   apr_uint64_t item_index;
 } window_cache_key_t;
 
-/* Structure used to propagate paths throught the FSFS implementation. */
-typedef struct fs_fs_path_t
-{
-  /* The original path, as found on disk or received by the API. */
-  const char *path;
-
-  /* The representation of PATH used for cache keys and
-     lookups. Depending on whether normalized lookups are enabled,
-     this will either be exactly the same pointer as PATH (i.e.,
-     normalization is disabled), or it will be a normalized
-     representation of PATH. The invariant is:
-
-         (path == keypath) ==> (normalized_lookup == false)
-  */
-  const char *keypath;
-}; fs_fs_path_t;
-
 /* Private (non-shared) FSFS-specific data for each svn_fs_t object.
    Any caches in here may be NULL. */
 typedef struct fs_fs_data_t

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/fs_fs.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/fs_fs.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/fs_fs.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/fs_fs.c Thu Jan  9 09:31:10 2014
@@ -487,11 +487,11 @@ read_config(fs_fs_data_t *ffd,
       SVN_ERR(svn_config_get_bool(ffd->config, &ffd->deltify_directories,
                                   CONFIG_SECTION_DELTIFICATION,
                                   CONFIG_OPTION_ENABLE_DIR_DELTIFICATION,
-                                  FALSE));
+                                  TRUE));
       SVN_ERR(svn_config_get_bool(ffd->config, &ffd->deltify_properties,
                                   CONFIG_SECTION_DELTIFICATION,
                                   CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION,
-                                  FALSE));
+                                  TRUE));
       SVN_ERR(svn_config_get_int64(ffd->config, &ffd->max_deltification_walk,
                                    CONFIG_SECTION_DELTIFICATION,
                                    CONFIG_OPTION_MAX_DELTIFICATION_WALK,
@@ -515,7 +515,7 @@ read_config(fs_fs_data_t *ffd,
       SVN_ERR(svn_config_get_bool(ffd->config, &ffd->compress_packed_revprops,
                                   CONFIG_SECTION_PACKED_REVPROPS,
                                   CONFIG_OPTION_COMPRESS_PACKED_REVPROPS,
-                                  FALSE));
+                                  TRUE));
       SVN_ERR(svn_config_get_int64(ffd->config, &ffd->revprop_pack_size,
                                    CONFIG_SECTION_PACKED_REVPROPS,
                                    CONFIG_OPTION_REVPROP_PACK_SIZE,
@@ -636,7 +636,7 @@ write_config(svn_fs_t *fs,
 "### In rarely read repositories, the I/O overhead may be significant as"    NL
 "### cache hit rates will most likely be low"                                NL
 "### directory deltification is disabled by default."                        NL
-"# " CONFIG_OPTION_ENABLE_DIR_DELTIFICATION " = false"                       NL
+"# " CONFIG_OPTION_ENABLE_DIR_DELTIFICATION " = true"                        NL
 "###"                                                                        NL
 "### The following parameter enables deltification for properties on files"  NL
 "### and directories.  Overall, this is a minor tuning option but can save"  NL
@@ -644,7 +644,7 @@ write_config(svn_fs_t *fs,
 "### properties.  You should not activate this if rep-sharing has been"      NL
 "### disabled because this may result in a net increase in repository size." NL
 "### property deltification is disabled by default."                         NL
-"# " CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION " = false"                     NL
+"# " CONFIG_OPTION_ENABLE_PROPS_DELTIFICATION " = true"                      NL
 "###"                                                                        NL
 "### During commit, the server may need to walk the whole change history of" NL
 "### of a given node to find a suitable deltification base.  This linear"    NL
@@ -692,7 +692,7 @@ write_config(svn_fs_t *fs,
 "### ineffective."                                                           NL
 "### revprop-pack-size is 64 kBytes by default for non-compressed revprop"   NL
 "### pack files and 256 kBytes when compression has been enabled."           NL
-"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 64"                                 NL
+"# " CONFIG_OPTION_REVPROP_PACK_SIZE " = 256"                                NL
 "###"                                                                        NL
 "### To save disk space, packed revprop files may be compressed.  Standard"  NL
 "### revprops tend to allow for very effective compression.  Reading and"    NL
@@ -700,7 +700,7 @@ write_config(svn_fs_t *fs,
 "### revprop caching enabled, the overhead can be offset by reduced I/O"     NL
 "### unless you often modify revprops after packing."                        NL
 "### Compressing packed revprops is disabled by default."                    NL
-"# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = false"                       NL
+"# " CONFIG_OPTION_COMPRESS_PACKED_REVPROPS " = true"                        NL
 ""                                                                           NL
 "[" CONFIG_SECTION_IO "]"                                                    NL
 "### Parameters in this section control the data access granularity in"      NL

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/index.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/index.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/index.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/index.c Thu Jan  9 09:31:10 2014
@@ -28,6 +28,7 @@
 
 #include "svn_private_config.h"
 
+#include "private/svn_sorts_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_temp_serializer.h"
 
@@ -533,6 +534,22 @@ svn_fs_fs__l2p_proto_index_add_entry(apr
                                                     pool));
 }
 
+static svn_error_t *
+index_create(apr_file_t **index_file, const char *file_name, apr_pool_t *pool)
+{
+  /* remove any old index file
+   * (it would probably be r/o and simply re-writing it would fail) */
+  SVN_ERR(svn_io_remove_file2(file_name, TRUE, pool));
+
+  /* We most likely own the write lock to the repo, so this should
+   * either just work or fail indicating a serious problem. */
+  SVN_ERR(svn_io_file_open(index_file, file_name,
+                           APR_WRITE | APR_CREATE | APR_BUFFERED,
+                           APR_OS_DEFAULT, pool));
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_fs_fs__l2p_index_create(svn_fs_t *fs,
                             const char *file_name,
@@ -632,9 +649,7 @@ svn_fs_fs__l2p_index_create(svn_fs_t *fs
   SVN_ERR(svn_io_file_close(proto_index, local_pool));
 
   /* create the target file */
-  SVN_ERR(svn_io_file_open(&index_file, file_name, APR_WRITE
-                           | APR_CREATE | APR_TRUNCATE | APR_BUFFERED,
-                           APR_OS_DEFAULT, local_pool));
+  SVN_ERR(index_create(&index_file, file_name, local_pool));
 
   /* write header info */
   SVN_ERR(svn_io_file_write_full(index_file, encoded,
@@ -700,6 +715,8 @@ retry_open_l2p_index(svn_fs_fs__revision
   /* reopen rep file if necessary */
   if (rev_file->file)
     SVN_ERR(svn_fs_fs__reopen_revision_file(rev_file, fs, revision));
+  else
+    rev_file->is_packed = svn_fs_fs__is_packed_rev(fs, revision);
 
   /* re-try opening this index (keep the p2l closed) */
   SVN_ERR(packed_stream_open(&rev_file->l2p_stream,
@@ -1482,13 +1499,22 @@ svn_fs_fs__item_offset(apr_off_t *absolu
       err = l2p_index_lookup(absolute_position, fs, rev_file, revision,
                              item_index, pool);
 
-      /* retry upon intermittent pack */
-      if (err && svn_fs_fs__is_packed_rev(fs, revision) != rev_file->is_packed)
+      if (err && !rev_file->is_packed)
         {
-          svn_error_clear(err);
-          SVN_ERR(retry_open_l2p_index(rev_file, fs, revision));
-          err = l2p_index_lookup(absolute_position, fs, rev_file, revision,
-                                 item_index, pool);
+          /* REVISION might have been packed in the meantime.
+             Refresh cache & retry. */
+          err = svn_error_compose_create(err,
+                                         svn_fs_fs__update_min_unpacked_rev
+                                            (fs, pool));
+
+          /* retry upon intermittent pack */
+          if (svn_fs_fs__is_packed_rev(fs, revision) != rev_file->is_packed)
+            {
+              svn_error_clear(err);
+              SVN_ERR(retry_open_l2p_index(rev_file, fs, revision));
+              err = l2p_index_lookup(absolute_position, fs, rev_file,
+                                     revision, item_index, pool);
+            }
         }
     }
   else if (rev_file->is_packed)
@@ -1672,9 +1698,7 @@ svn_fs_fs__p2l_index_create(svn_fs_t *fs
       = svn_spillbuf__get_size(buffer) - last_buffer_size;
 
   /* create the target file */
-  SVN_ERR(svn_io_file_open(&index_file, file_name, APR_WRITE
-                           | APR_CREATE | APR_TRUNCATE | APR_BUFFERED,
-                           APR_OS_DEFAULT, local_pool));
+  SVN_ERR(index_create(&index_file, file_name, local_pool));
 
   /* write the start revision, file size and page size */
   SVN_ERR(svn_io_file_write_full(index_file, encoded,
@@ -2297,25 +2321,17 @@ get_p2l_entry_from_cached_page(const voi
   /* resolve all pointer values of in-cache data */
   const apr_array_header_t *page = data;
   apr_array_header_t *entries = apr_pmemdup(pool, page, sizeof(*page));
-  int idx;
+  svn_fs_fs__p2l_entry_t *entry;
 
   entries->elts = (char *)svn_temp_deserializer__ptr(page,
                                      (const void *const *)&page->elts);
 
   /* search of the offset we want */
-  idx = svn_sort__bsearch_lower_bound(&offset, entries,
+  entry = svn_sort__array_lookup(entries, &offset, NULL,
       (int (*)(const void *, const void *))compare_p2l_entry_offsets);
 
   /* return it, if it is a perfect match */
-  if (idx < entries->nelts)
-    {
-      svn_fs_fs__p2l_entry_t *entry
-        = &APR_ARRAY_IDX(entries, idx, svn_fs_fs__p2l_entry_t);
-      if (entry->offset == offset)
-        return apr_pmemdup(pool, entry, sizeof(*entry));
-    }
-
-  return NULL;
+  return entry ? apr_pmemdup(pool, entry, sizeof(*entry)) : NULL;
 }
 
 /* Implements svn_cache__partial_getter_func_t for P2L index pages, copying
@@ -2362,25 +2378,14 @@ svn_fs_fs__p2l_entry_lookup(svn_fs_fs__p
                                  p2l_entry_lookup_func, &offset, pool));
   if (!is_cached)
     {
-      int idx;
-
       /* do a standard index lookup.  This is will automatically prefetch
        * data to speed up future lookups. */
       apr_array_header_t *entries;
       SVN_ERR(p2l_index_lookup(&entries, rev_file, fs, revision, offset, pool));
 
       /* Find the entry that we want. */
-      idx = svn_sort__bsearch_lower_bound(&offset, entries, 
+      *entry_p = svn_sort__array_lookup(entries, &offset, NULL,
           (int (*)(const void *, const void *))compare_p2l_entry_offsets);
-
-      /* return it, if it is a perfect match */
-      if (idx < entries->nelts)
-        {
-          svn_fs_fs__p2l_entry_t *entry
-            = &APR_ARRAY_IDX(entries, idx, svn_fs_fs__p2l_entry_t);
-          if (entry->offset == offset)
-            *entry_p = entry;
-        }
     }
 
   return SVN_NO_ERROR;

Propchange: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/index.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/index.h
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/low_level.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/low_level.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/low_level.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/low_level.c Thu Jan  9 09:31:10 2014
@@ -24,6 +24,7 @@
 #include "svn_hash.h"
 #include "svn_pools.h"
 #include "svn_sorts.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_string_private.h"
 #include "private/svn_subr_private.h"
 

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/pack.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/pack.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/pack.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/pack.c Thu Jan  9 09:31:10 2014
@@ -20,11 +20,13 @@
  * ====================================================================
  */
 #include <assert.h>
+#include <string.h>
 
 #include "svn_pools.h"
 #include "svn_dirent_uri.h"
 #include "svn_sorts.h"
 #include "private/svn_temp_serializer.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_subr_private.h"
 #include "private/svn_string_private.h"
 #include "private/svn_io_private.h"
@@ -72,7 +74,8 @@
  * - changed paths lists
  * - node property
  * - directory properties
- * - noderevs and representations, reverse lexical path order
+ * - noderevs and representations, lexical path order with special
+ *   treatment of "trunk" and "branches"
  *
  * Step 4 copies the items from the temporary buckets into the final
  * pack file and writes the temporary index files.
@@ -101,6 +104,9 @@ typedef struct path_order_t
   /* when this change happened */
   svn_revnum_t revision;
 
+  /* noderev predecessor count */
+  int predecessor_count;
+
   /* length of the expanded representation content */
   apr_int64_t expanded_size;
 
@@ -201,8 +207,9 @@ typedef struct pack_context_t
    * after each revision range.  Sorted by PATH, NODE_ID. */
   apr_array_header_t *path_order;
 
-  /* array of reference_t *.  Will be filled in phase 2 and be cleared
-   * after each revision range.  It will be sorted by the TO members. */
+  /* array of reference_t* linking representations to their delta bases.
+   * Will be filled in phase 2 and be cleared after each revision range.
+   * It will be sorted by the FROM members (for rep->base rep lookup). */
   apr_array_header_t *references;
 
   /* array of svn_fs_fs__p2l_entry_t*.  Will be filled in phase 2 and be
@@ -301,8 +308,10 @@ initialize_pack_context(pack_context_t *
 
   /* noderev and representation item bucket */
   context->rev_offsets = apr_array_make(pool, max_revs, sizeof(int));
-  context->path_order = apr_array_make(pool, max_items, sizeof(path_order_t *));
-  context->references = apr_array_make(pool, max_items, sizeof(reference_t *));
+  context->path_order = apr_array_make(pool, max_items,
+                                       sizeof(path_order_t *));
+  context->references = apr_array_make(pool, max_items,
+                                           sizeof(reference_t *));
   context->reps = apr_array_make(pool, max_items,
                                  sizeof(svn_fs_fs__p2l_entry_t *));
   SVN_ERR(svn_io_open_unique_file3(&context->reps_file, NULL, temp_dir,
@@ -658,6 +667,37 @@ svn_fs_fs__order_dir_entries(svn_fs_t *f
   return result;
 }
 
+/* Return a duplicate of the the ORIGINAL path and with special sub-strins
+ * (e.g. "trunk") modified in such a way that have a lower lexicographic
+ * value than any other "normal" file name.
+ */
+static const char *
+tweak_path_for_ordering(const char *original,
+                        apr_pool_t *pool)
+{
+  /* We may add further special cases as needed. */
+  enum {SPECIAL_COUNT = 2};
+  static const char *special[SPECIAL_COUNT] = {"trunk", "branch"};
+  char *pos;
+  char *path = apr_pstrdup(pool, original);
+  int i;
+
+  /* Replace the first char of any "special" sub-string we find by
+   * a control char, i.e. '\1' .. '\31'.  In the rare event that this
+   * would clash with existing paths, no data will be lost but merely
+   * the node ordering will be sub-optimal.
+   */
+  for (i = 0; i < SPECIAL_COUNT; ++i)
+    for (pos = strstr(path, special[i]);
+         pos;
+         pos = strstr(pos + 1, special[i]))
+      {
+        *pos = (char)(i + '\1');
+      }
+
+   return path;
+}
+
 /* Copy node revision item identified by ENTRY from the current position
  * in REV_FILE into CONTEXT->REPS_FILE.  Add all tracking into needed by
  * our placement algorithm to CONTEXT.  Use POOL for temporary allocations.
@@ -671,6 +711,7 @@ copy_node_to_temp(pack_context_t *contex
   path_order_t *path_order = apr_pcalloc(context->info_pool,
                                          sizeof(*path_order));
   node_revision_t *noderev;
+  const char *sort_path;
   svn_stream_t *stream;
   apr_off_t source_offset = entry->offset;
 
@@ -698,23 +739,20 @@ copy_node_to_temp(pack_context_t *contex
 
   if (noderev->data_rep && noderev->data_rep->revision >= context->start_rev)
     {
-      reference_t *reference = apr_pcalloc(context->info_pool,
-                                           sizeof(*reference));
-      reference->from = entry->item;
-      reference->to.revision = noderev->data_rep->revision;
-      reference->to.number = noderev->data_rep->item_index;
-      APR_ARRAY_PUSH(context->references, reference_t *) = reference;
-
-      path_order->rep_id = reference->to;
+      path_order->rep_id.revision = noderev->data_rep->revision;
+      path_order->rep_id.number = noderev->data_rep->item_index;
       path_order->expanded_size = noderev->data_rep->expanded_size
                                 ? noderev->data_rep->expanded_size
                                 : noderev->data_rep->size;
     }
 
-  path_order->path = svn_prefix_string__create(context->paths,
-                                               noderev->created_path);
+  /* Sort path is the key used for ordering noderevs and associated reps.
+   * It will not be stored in the final pack file. */
+  sort_path = tweak_path_for_ordering(noderev->created_path, pool);
+  path_order->path = svn_prefix_string__create(context->paths, sort_path);
   path_order->node_id = *svn_fs_fs__id_node_id(noderev->id);
   path_order->revision = svn_fs_fs__id_rev(noderev->id);
+  path_order->predecessor_count = noderev->predecessor_count;
   path_order->noderev_id = *svn_fs_fs__id_rev_item(noderev->id);
   APR_ARRAY_PUSH(context->path_order, path_order_t *) = path_order;
 
@@ -730,8 +768,8 @@ compare_path_order(const path_order_t * 
   const path_order_t * lhs = *lhs_p;
   const path_order_t * rhs = *rhs_p;
 
-  /* reverse lexicographic order on path and node (i.e. latest first) */
-  int diff = svn_prefix_string__compare(rhs->path, lhs->path);
+  /* lexicographic order on path and node (i.e. latest first) */
+  int diff = svn_prefix_string__compare(lhs->path, rhs->path);
   if (diff)
     return diff;
 
@@ -747,7 +785,7 @@ compare_path_order(const path_order_t * 
   return 0;
 }
 
-/* implements compare_fn_t.  Sort ascending by TO, FROM.
+/* implements compare_fn_t.  Sort ascendingly by FROM, TO.
  */
 static int
 compare_references(const reference_t * const * lhs_p,
@@ -756,8 +794,26 @@ compare_references(const reference_t * c
   const reference_t * lhs = *lhs_p;
   const reference_t * rhs = *rhs_p;
 
-  int diff = svn_fs_fs__id_part_compare(&lhs->to, &rhs->to);
-  return diff ? diff : svn_fs_fs__id_part_compare(&lhs->from, &rhs->from);
+  int diff = svn_fs_fs__id_part_compare(&lhs->from, &rhs->from);
+  return diff ? diff : svn_fs_fs__id_part_compare(&lhs->to, &rhs->to);
+}
+
+/* implements compare_fn_t.  Assume ascending order by FROM.
+ */
+static int
+compare_ref_to_item(const reference_t * const * lhs_p,
+                    const svn_fs_fs__id_part_t * rhs_p)
+{
+  return svn_fs_fs__id_part_compare(&(*lhs_p)->from, rhs_p);
+}
+
+/* Look for the least significant bit set in VALUE and return the smallest
+ * number with the same property, i.e. the largest power of 2 that is a
+ * factor in VALUE. */
+static int
+roundness(int value)
+{
+  return value ? value - (value & (value - 1)) : INT_MAX;
 }
 
 /* Order the data collected in CONTEXT such that we can place them in the
@@ -766,12 +822,140 @@ compare_references(const reference_t * c
 static void
 sort_reps(pack_context_t *context)
 {
+  apr_pool_t *temp_pool;
+  const path_order_t **temp, **path_order;
+  const svn_prefix_string__t *path;
+  int i, dest, count, best;
+  svn_fs_fs__id_part_t rep_id;
+  fs_fs_data_t *ffd = context->fs->fsap_data;
+
+  /* We will later assume that there is at least one node / path.
+   */
+  if (context->path_order->nelts == 0)
+    {
+      assert(context->references->nelts == 0);
+      return;
+    }
+
+  /* Sort containers by path and IDs, respectively.
+   */
   qsort(context->path_order->elts, context->path_order->nelts,
         context->path_order->elt_size,
         (int (*)(const void *, const void *))compare_path_order);
   qsort(context->references->elts, context->references->nelts,
         context->references->elt_size,
         (int (*)(const void *, const void *))compare_references);
+
+  /* Re-order noderevs like this:
+   *
+   * (1) Most likely to be referenced by future pack files, in path order.
+   * (2) highest revision rep per path + dependency chain
+   * (3) Remaining reps in path, rev order
+   *
+   * We simply pick & chose from the existing path, rev order.
+   */
+
+  temp_pool = svn_pool_create(context->info_pool);
+  count = context->path_order->nelts;
+  temp = apr_pcalloc(temp_pool, count * sizeof(*temp));
+  path_order = (void *)context->path_order->elts;
+
+  dest = 0;
+  path = path_order[0]->path;
+  best = 0;
+
+  /* (1) For each path, pick the "roundest" representation and put it in
+   * front of all other nodes in the pack file.  The "roundest" rep is
+   * the one most likely to be referenced from future pack files, i.e. we
+   * concentrate those potential "foreign link targets" in one section of
+   * the pack file.
+   *
+   * And we only apply this to reps outside the linear deltification
+   * sections because references *into* linear deltification ranges are
+   * much less likely.
+   */
+  for (i = 0; i < count; ++i)
+    {
+      /* Investigated all nodes for the current path? */
+      if (svn_prefix_string__compare(path, path_order[i]->path))
+        {
+          /* next path */
+          path = path_order[i]->path;
+          rep_id = path_order[i]->rep_id;
+
+          /* Pick roundest non-linear deltified node. */
+          if (roundness(path_order[best]->predecessor_count)
+              >= ffd->max_linear_deltification)
+            {
+              temp[dest++] = path_order[best];
+              path_order[best] = NULL;
+              best = i;
+            }
+        }
+
+      /* next entry */
+      if (  roundness(path_order[best]->predecessor_count)
+          < roundness(path_order[i]->predecessor_count))
+        best = i;
+    }
+
+  /* Treat the last path the same as all others. */
+  if (roundness(path_order[best]->predecessor_count)
+      >= ffd->max_linear_deltification)
+    {
+      temp[dest++] = path_order[best];
+      path_order[best] = NULL;
+    }
+
+  /* (2) For each (remaining) path, pick the nodes along the delta chain
+   * for the highest revision.  Due to our ordering, this is the first
+   * node we encounter for any path.
+   *
+   * Most references that don't hit a delta base picked in (1), will
+   * access HEAD of the respective path.  Keeping all its dependency chain
+   * in one place turns reconstruction into a linear scan of minimal length.
+   */
+  for (i = 0; i < count; ++i)
+    if (path_order[i])
+      {
+        /* New path? */
+        if (svn_prefix_string__compare(path, path_order[i]->path))
+          {
+            path = path_order[i]->path;
+            rep_id = path_order[i]->rep_id;
+          }
+
+        /* Pick nodes along the deltification chain.  Skip side-branches. */
+        if (svn_fs_fs__id_part_eq(&path_order[i]->rep_id, &rep_id))
+          {
+            reference_t **reference;
+
+            temp[dest++] = path_order[i];
+            path_order[i] = 0;
+
+            reference = svn_sort__array_lookup(context->references,
+                                               &rep_id, NULL,
+              (int (*)(const void *, const void *))compare_ref_to_item);
+            if (reference)
+              rep_id = (*reference)->to;
+          }
+      }
+
+  /* (3) All remaining nodes in path, rev order.  Linear deltification
+   * makes HEAD delta chains from (2) cover all or most of their deltas
+   * in a given pack file.  So, this is just a few remnants that we put
+   * at the end of the pack file.
+   */
+  for (i = 0; i < count; ++i)
+    if (path_order[i])
+      temp[dest++] = path_order[i];
+
+  /* We now know the final ordering. */
+  assert(dest == count);
+  for (i = 0; i < count; ++i)
+    path_order[i] = temp[i];
+  
+  svn_pool_destroy(temp_pool);
 }
 
 /* implements compare_fn_t. Place LHS before RHS, if the latter is older.
@@ -838,8 +1022,8 @@ auto_pad_block(pack_context_t *context,
       null_entry.offset = context->pack_offset;
       null_entry.size = padding;
       null_entry.type = SVN_FS_FS__ITEM_TYPE_UNUSED;
-      null_entry.item.number = SVN_INVALID_REVNUM;
-      null_entry.type = SVN_FS_FS__ITEM_INDEX_UNUSED;
+      null_entry.item.revision = SVN_INVALID_REVNUM;
+      null_entry.item.number = SVN_FS_FS__ITEM_INDEX_UNUSED;
       null_entry.fnv1_checksum = 0;
 
       SVN_ERR(write_null_bytes(context->pack_file, padding, pool));
@@ -851,6 +1035,48 @@ auto_pad_block(pack_context_t *context,
   return SVN_NO_ERROR;
 }
 
+/* Read the contents of ITEM, if not empty, from TEMP_FILE and write it
+ * to CONTEXT->PACK_FILE.  Use POOL for allocations.
+ */
+static svn_error_t *
+store_item(pack_context_t *context,
+           apr_file_t *temp_file,
+           svn_fs_fs__p2l_entry_t *item,
+           apr_pool_t *pool)
+{
+  apr_off_t safety_margin;
+
+  /* skip empty entries */
+  if (item->type == SVN_FS_FS__ITEM_TYPE_UNUSED)
+    return SVN_NO_ERROR;
+
+  /* If the next item does not fit into the current block, auto-pad it.
+      Take special care of textual noderevs since their parsers may
+      prefetch up to 80 bytes and we don't want them to cross block
+      boundaries. */
+  safety_margin = item->type == SVN_FS_FS__ITEM_TYPE_NODEREV
+                ? SVN__LINE_CHUNK_SIZE
+                : 0;
+  SVN_ERR(auto_pad_block(context, item->size + safety_margin, pool));
+
+  /* select the item in the source file and copy it into the target
+    * pack file */
+  SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &item->offset, pool));
+  SVN_ERR(copy_file_data(context, context->pack_file, temp_file,
+                         item->size, pool));
+
+  /* write index entry and update current position */
+  item->offset = context->pack_offset;
+  context->pack_offset += item->size;
+
+  SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry(context->proto_p2l_index,
+                                               item, pool));
+
+  APR_ARRAY_PUSH(context->reps, svn_fs_fs__p2l_entry_t *) = item;
+
+  return SVN_NO_ERROR;
+}
+
 /* Read the contents of the non-empty items in ITEMS from TEMP_FILE and
  * write them to CONTEXT->PACK_FILE.  Use POOL for allocations.
  */
@@ -866,40 +1092,10 @@ store_items(pack_context_t *context,
   /* copy all items in strict order */
   for (i = 0; i < items->nelts; ++i)
     {
-      apr_off_t safety_margin;
-
-      /* skip empty entries */
-      svn_fs_fs__p2l_entry_t *entry
-        = APR_ARRAY_IDX(items, i, svn_fs_fs__p2l_entry_t *);
-      if (entry->type == SVN_FS_FS__ITEM_TYPE_UNUSED)
-        continue;
-
       svn_pool_clear(iterpool);
-
-      /* If the next item does not fit into the current block, auto-pad it.
-         Take special care of textual noderevs since their parsers may
-         prefetch up to 80 bytes and we don't want them to cross block
-         boundaries. */
-      safety_margin = entry->type == SVN_FS_FS__ITEM_TYPE_NODEREV
-                    ? SVN__LINE_CHUNK_SIZE
-                    : 0;
-      SVN_ERR(auto_pad_block(context, entry->size + safety_margin, iterpool));
-
-      /* select the item in the source file and copy it into the target
-       * pack file */
-      SVN_ERR(svn_io_file_seek(temp_file, SEEK_SET, &entry->offset,
-                               iterpool));
-      SVN_ERR(copy_file_data(context, context->pack_file, temp_file,
-                             entry->size, iterpool));
-
-      /* write index entry and update current position */
-      entry->offset = context->pack_offset;
-      context->pack_offset += entry->size;
-
-      SVN_ERR(svn_fs_fs__p2l_proto_index_add_entry(
-                   context->proto_p2l_index, entry, iterpool));
-
-      APR_ARRAY_PUSH(context->reps, svn_fs_fs__p2l_entry_t *) = entry;
+      SVN_ERR(store_item(context, temp_file,
+                         APR_ARRAY_IDX(items, i, svn_fs_fs__p2l_entry_t *),
+                         iterpool));
     }
 
   svn_pool_destroy(iterpool);
@@ -907,113 +1103,6 @@ store_items(pack_context_t *context,
   return SVN_NO_ERROR;
 }
 
-/* implements compare_fn_t.  Sort ascending by TO.
- */
-static int
-compare_ref_to_item(const reference_t * const * lhs_p,
-                    const svn_fs_fs__id_part_t * rhs_p)
-{
-  return svn_fs_fs__id_part_compare(&(*lhs_p)->to, rhs_p);
-}
-
-/* Return the index of the first entry in CONTEXT->REFERENCES that
- * references ITEM if such entries exist.  All matching items will be
- * consecutive.
- */
-static int
-find_first_reference(pack_context_t *context,
-                     svn_fs_fs__p2l_entry_t *item)
-{
-  return svn_sort__bsearch_lower_bound(&item->item, context->references,
-                (int (*)(const void *, const void *))compare_ref_to_item);
-}
-
-/* Check whether entry number IDX in CONTEXT->REFERENCES references ITEM.
- */
-static svn_boolean_t
-is_reference_match(pack_context_t *context,
-                   int idx,
-                   svn_fs_fs__p2l_entry_t *item)
-{
-  reference_t *reference;
-  if (context->references->nelts <= idx)
-    return FALSE;
-
-  reference = APR_ARRAY_IDX(context->references, idx, reference_t *);
-  return svn_fs_fs__id_part_eq(&reference->to, &item->item);
-}
-
-/* Starting at IDX in CONTEXT->PATH_ORDER, select all representations and
- * noderevs that should be placed into the same container, respectively.
- * Append the svn_fs_fs__p2l_entry_t * of the representations that to
- * REP_PARTS and apend the svn_fs_fs__p2l_entry_t * of the noderevs
- * referencing those reps will to NODE_PARTS.
- *
- * Remove all returned items from the CONTEXT->REPS container and prevent
- * them from being placed a second time later on.  That also means that the
- * caller has to place all items returned.
- */
-static svn_error_t *
-select_reps(pack_context_t *context,
-            int idx,
-            apr_array_header_t *node_parts,
-            apr_array_header_t *rep_parts)
-{
-  apr_array_header_t *path_order = context->path_order;
-  path_order_t *start_path = APR_ARRAY_IDX(path_order, idx, path_order_t *);
-
-  svn_fs_fs__p2l_entry_t *node_part;
-  svn_fs_fs__p2l_entry_t *rep_part;
-  svn_fs_fs__p2l_entry_t *depending;
-  int i, k;
-
-  /* collect all path_order records as well as rep and noderev items
-   * that occupy the same path with the same node. */
-  for (; idx < path_order->nelts; ++idx)
-    {
-      path_order_t *current_path
-        = APR_ARRAY_IDX(path_order, idx, path_order_t *);
-
-      if (!svn_fs_fs__id_part_eq(&start_path->node_id,
-                                 &current_path->node_id))
-        break;
-
-      APR_ARRAY_IDX(path_order, idx, path_order_t *) = NULL;
-      node_part = get_item(context, &current_path->noderev_id, TRUE);
-      rep_part = get_item(context, &current_path->rep_id, TRUE);
-
-      if (node_part)
-        APR_ARRAY_PUSH(node_parts, svn_fs_fs__p2l_entry_t *) = node_part;
-      if (rep_part)
-        APR_ARRAY_PUSH(rep_parts, svn_fs_fs__p2l_entry_t *) = rep_part;
-    }
-
-  /* collect depending reps and noderevs that reference any of the collected
-   * reps */
-  for (i = 0; i < rep_parts->nelts; ++i)
-    {
-      rep_part = APR_ARRAY_IDX(rep_parts, i, svn_fs_fs__p2l_entry_t*);
-      for (k = find_first_reference(context, rep_part);
-           is_reference_match(context, k, rep_part);
-           ++k)
-        {
-          reference_t *reference
-            = APR_ARRAY_IDX(context->references, k, reference_t *);
-
-          depending = get_item(context, &reference->from, TRUE);
-          if (!depending)
-            continue;
-
-          if (depending->type == SVN_FS_FS__ITEM_TYPE_NODEREV)
-            APR_ARRAY_PUSH(node_parts, svn_fs_fs__p2l_entry_t *) = depending;
-          else
-            APR_ARRAY_PUSH(rep_parts, svn_fs_fs__p2l_entry_t *) = depending;
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
 /* Copy (append) the items identified by svn_fs_fs__p2l_entry_t * elements
  * in ENTRIES strictly in order from TEMP_FILE into CONTEXT->PACK_FILE.
  * Use POOL for temporary allocations.
@@ -1025,28 +1114,29 @@ copy_reps_from_temp(pack_context_t *cont
 {
   apr_pool_t *iterpool = svn_pool_create(pool);
   apr_array_header_t *path_order = context->path_order;
-  apr_array_header_t *node_parts = apr_array_make(pool, 16, sizeof(void*));
-  apr_array_header_t *rep_parts = apr_array_make(pool, 16, sizeof(void*));
+  apr_array_header_t *parts = apr_array_make(pool, 16, sizeof(void*));
   int i;
 
-  /* copy items in path order. Create block-sized containers. */
+  /* copy items in path order. */
   for (i = 0; i < path_order->nelts; ++i)
     {
-      if (APR_ARRAY_IDX(path_order, i, path_order_t *) == NULL)
-        continue;
+      path_order_t *current_path;
+      svn_fs_fs__p2l_entry_t *node_part;
+      svn_fs_fs__p2l_entry_t *rep_part;
 
       svn_pool_clear(iterpool);
 
-      /* Collect reps to combine and all noderevs referencing them */
-      SVN_ERR(select_reps(context, i, node_parts, rep_parts));
+      current_path = APR_ARRAY_IDX(path_order, i, path_order_t *);
+      node_part = get_item(context, &current_path->noderev_id, TRUE);
+      rep_part = get_item(context, &current_path->rep_id, TRUE);
+
+      if (node_part)
+        SVN_ERR(store_item(context, temp_file, node_part, iterpool));
+      if (rep_part)
+        SVN_ERR(store_item(context, temp_file, rep_part, iterpool));
 
-      /* store the noderevs container in front of the reps */
-      SVN_ERR(store_items(context, temp_file, node_parts, iterpool));
-      SVN_ERR(store_items(context, temp_file, rep_parts, iterpool));
-      
       /* processed all items */
-      apr_array_clear(node_parts);
-      apr_array_clear(rep_parts);
+      apr_array_clear(parts);
     }
 
   svn_pool_destroy(iterpool);

Propchange: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/rev_file.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/rev_file.h
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/structure
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/structure?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/structure (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/structure Thu Jan  9 09:31:10 2014
@@ -602,6 +602,7 @@ Transaction layout
 A transaction directory has the following layout:
 
   props                      Transaction props
+  props-final                Final transaction props (optional)
   next-ids                   Next temporary node-ID and copy-ID
   changes                    Changed-path information so far
   node.<nid>.<cid>           New node-rev data for node
@@ -629,9 +630,11 @@ they are received from the client.  To e
 writing to the file at a given time, the "rev-lock" file is locked for
 the duration of each write.
 
-The two kinds of props files are all in hash dump format.  The "props"
+The three kinds of props files are all in hash dump format.  The "props"
 file will always be present.  The "node.<nid>.<cid>.props" file will
-only be present if the node-rev properties have been changed.
+only be present if the node-rev properties have been changed.  The
+"props-final" only exists while converting the transaction into a revision.
+
 
 The <sha1> files have been introduced in FS format 6. Their content
 is that of text rep references: "<rev> <item_offset> <length> <size> <digest>"

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.c Thu Jan  9 09:31:10 2014
@@ -25,16 +25,18 @@
 #include "svn_private_config.h"
 #include "svn_pools.h"
 #include "svn_hash.h"
-
-#include "id.h"
+#include "svn_sorts.h"
 #include "svn_fs.h"
 
 #include "private/svn_fs_util.h"
+#include "private/svn_sorts_private.h"
 #include "private/svn_temp_serializer.h"
 #include "private/svn_subr_private.h"
 
+#include "id.h"
 #include "temp_serializer.h"
 #include "low_level.h"
+#include "cached_data.h"
 
 /* Utility to encode a signed NUMBER into a variable-length sequence of
  * 8-bit chars in KEY_BUFFER and return the last writen position.
@@ -145,8 +147,8 @@ serialize_representation(svn_temp_serial
                                 sizeof(*rep));
 }
 
-/* auxilliary structure representing the content of a directory hash */
-typedef struct hash_data_t
+/* auxilliary structure representing the content of a directory array */
+typedef struct dir_data_t
 {
   /* number of entries in the directory */
   apr_size_t count;
@@ -169,14 +171,7 @@ typedef struct hash_data_t
   /* size of the serialized entries and don't be too wasteful
    * (needed since the entries are no longer in sequence) */
   apr_uint32_t *lengths;
-} hash_data_t;
-
-static int
-compare_dirent_id_names(const void *lhs, const void *rhs)
-{
-  return strcmp((*(const svn_fs_dirent_t *const *)lhs)->name,
-                (*(const svn_fs_dirent_t *const *)rhs)->name);
-}
+} dir_data_t;
 
 /* Utility function to serialize the *ENTRY_P into a the given
  * serialization CONTEXT. Return the serialized size of the
@@ -207,91 +202,85 @@ serialize_dir_entry(svn_temp_serializer_
  * context to be returned. Allocation will be made form POOL.
  */
 static svn_temp_serializer__context_t *
-serialize_dir(apr_hash_t *entries, apr_pool_t *pool)
+serialize_dir(apr_array_header_t *entries, apr_pool_t *pool)
 {
-  hash_data_t hash_data;
-  apr_hash_index_t *hi;
+  dir_data_t dir_data;
   apr_size_t i = 0;
   svn_temp_serializer__context_t *context;
 
   /* calculate sizes */
-  apr_size_t count = apr_hash_count(entries);
+  apr_size_t count = entries->nelts;
   apr_size_t over_provision = 2 + count / 4;
   apr_size_t entries_len = (count + over_provision) * sizeof(svn_fs_dirent_t*);
   apr_size_t lengths_len = (count + over_provision) * sizeof(apr_uint32_t);
 
   /* copy the hash entries to an auxilliary struct of known layout */
-  hash_data.count = count;
-  hash_data.over_provision = over_provision;
-  hash_data.operations = 0;
-  hash_data.entries = apr_palloc(pool, entries_len);
-  hash_data.lengths = apr_palloc(pool, lengths_len);
-
-  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi), ++i)
-    hash_data.entries[i] = svn__apr_hash_index_val(hi);
-
-  /* sort entry index by ID name */
-  qsort(hash_data.entries,
-        count,
-        sizeof(*hash_data.entries),
-        compare_dirent_id_names);
+  dir_data.count = count;
+  dir_data.over_provision = over_provision;
+  dir_data.operations = 0;
+  dir_data.entries = apr_palloc(pool, entries_len);
+  dir_data.lengths = apr_palloc(pool, lengths_len);
+
+  for (i = 0; i < count; ++i)
+    dir_data.entries[i] = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
 
   /* Serialize that aux. structure into a new one. Also, provide a good
    * estimate for the size of the buffer that we will need. */
-  context = svn_temp_serializer__init(&hash_data,
-                                      sizeof(hash_data),
+  context = svn_temp_serializer__init(&dir_data,
+                                      sizeof(dir_data),
                                       50 + count * 200 + entries_len,
                                       pool);
 
   /* serialize entries references */
   svn_temp_serializer__push(context,
-                            (const void * const *)&hash_data.entries,
+                            (const void * const *)&dir_data.entries,
                             entries_len);
 
   /* serialize the individual entries and their sub-structures */
   for (i = 0; i < count; ++i)
     serialize_dir_entry(context,
-                        &hash_data.entries[i],
-                        &hash_data.lengths[i]);
+                        &dir_data.entries[i],
+                        &dir_data.lengths[i]);
 
   svn_temp_serializer__pop(context);
 
   /* serialize entries references */
   svn_temp_serializer__push(context,
-                            (const void * const *)&hash_data.lengths,
+                            (const void * const *)&dir_data.lengths,
                             lengths_len);
 
   return context;
 }
 
-/* Utility function to reconstruct a dir entries hash from serialized data
- * in BUFFER and HASH_DATA. Allocation will be made form POOL.
+/* Utility function to reconstruct a dir entries array from serialized data
+ * in BUFFER and DIR_DATA. Allocation will be made form POOL.
  */
-static apr_hash_t *
-deserialize_dir(void *buffer, hash_data_t *hash_data, apr_pool_t *pool)
+static apr_array_header_t *
+deserialize_dir(void *buffer, dir_data_t *dir_data, apr_pool_t *pool)
 {
-  apr_hash_t *result = svn_hash__make(pool);
+  apr_array_header_t *result
+    = apr_array_make(pool, dir_data->count, sizeof(svn_fs_dirent_t *));
   apr_size_t i;
   apr_size_t count;
   svn_fs_dirent_t *entry;
   svn_fs_dirent_t **entries;
 
   /* resolve the reference to the entries array */
-  svn_temp_deserializer__resolve(buffer, (void **)&hash_data->entries);
-  entries = hash_data->entries;
+  svn_temp_deserializer__resolve(buffer, (void **)&dir_data->entries);
+  entries = dir_data->entries;
 
   /* fixup the references within each entry and add it to the hash */
-  for (i = 0, count = hash_data->count; i < count; ++i)
+  for (i = 0, count = dir_data->count; i < count; ++i)
     {
       svn_temp_deserializer__resolve(entries, (void **)&entries[i]);
-      entry = hash_data->entries[i];
+      entry = dir_data->entries[i];
 
       /* pointer fixup */
       svn_temp_deserializer__resolve(entry, (void **)&entry->name);
       svn_fs_fs__id_deserialize(entry, (svn_fs_id_t **)&entry->id);
 
       /* add the entry to the hash */
-      svn_hash_sets(result, entry->name, entry);
+      APR_ARRAY_PUSH(result, svn_fs_dirent_t *) = entry;
     }
 
   /* return the now complete hash */
@@ -715,7 +704,7 @@ return_serialized_dir_context(svn_temp_s
 
   *data = serialized->data;
   *data_len = serialized->blocksize;
-  ((hash_data_t *)serialized->data)->len = serialized->len;
+  ((dir_data_t *)serialized->data)->len = serialized->len;
 
   return SVN_NO_ERROR;
 }
@@ -726,7 +715,7 @@ svn_fs_fs__serialize_dir_entries(void **
                                  void *in,
                                  apr_pool_t *pool)
 {
-  apr_hash_t *dir = in;
+  apr_array_header_t *dir = in;
 
   /* serialize the dir content into a new serialization context
    * and return the serialized data */
@@ -742,10 +731,10 @@ svn_fs_fs__deserialize_dir_entries(void 
                                    apr_pool_t *pool)
 {
   /* Copy the _full_ buffer as it also contains the sub-structures. */
-  hash_data_t *hash_data = (hash_data_t *)data;
+  dir_data_t *dir_data = (dir_data_t *)data;
 
   /* reconstruct the hash from the serialized data */
-  *out = deserialize_dir(hash_data, hash_data, pool);
+  *out = deserialize_dir(dir_data, dir_data, pool);
 
   return SVN_NO_ERROR;
 }
@@ -818,22 +807,22 @@ svn_fs_fs__extract_dir_entry(void **out,
                              void *baton,
                              apr_pool_t *pool)
 {
-  const hash_data_t *hash_data = data;
+  const dir_data_t *dir_data = data;
   const char* name = baton;
   svn_boolean_t found;
 
   /* resolve the reference to the entries array */
   const svn_fs_dirent_t * const *entries =
-    svn_temp_deserializer__ptr(data, (const void *const *)&hash_data->entries);
+    svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->entries);
 
   /* resolve the reference to the lengths array */
   const apr_uint32_t *lengths =
-    svn_temp_deserializer__ptr(data, (const void *const *)&hash_data->lengths);
+    svn_temp_deserializer__ptr(data, (const void *const *)&dir_data->lengths);
 
   /* binary search for the desired entry by name */
   apr_size_t pos = find_entry((svn_fs_dirent_t **)entries,
                               name,
-                              hash_data->count,
+                              dir_data->count,
                               &found);
 
   /* de-serialize that entry or return NULL, if no match has been found */
@@ -872,14 +861,33 @@ slowly_replace_dir_entry(void **data,
                          apr_pool_t *pool)
 {
   replace_baton_t *replace_baton = (replace_baton_t *)baton;
-  hash_data_t *hash_data = (hash_data_t *)*data;
-  apr_hash_t *dir;
+  dir_data_t *dir_data = (dir_data_t *)*data;
+  apr_array_header_t *dir;
+  int idx = -1;
+  svn_fs_dirent_t *entry;
 
   SVN_ERR(svn_fs_fs__deserialize_dir_entries((void **)&dir,
                                              *data,
-                                             hash_data->len,
+                                             dir_data->len,
                                              pool));
-  svn_hash_sets(dir, replace_baton->name, replace_baton->new_entry);
+
+  entry = svn_fs_fs__find_dir_entry(dir, replace_baton->name, &idx);
+
+  /* Replacement or removal? */
+  if (replace_baton->new_entry)
+    {
+      /* Replace ENTRY with / insert the NEW_ENTRY */
+      if (entry)
+        APR_ARRAY_IDX(dir, idx, svn_fs_dirent_t *) = replace_baton->new_entry;
+      else
+        svn_sort__array_insert(dir, &replace_baton->new_entry, idx);
+    }
+  else
+    {
+      /* Remove the old ENTRY. */
+      if (entry)
+        svn_sort__array_delete(dir, idx, 1);
+    }
 
   return svn_fs_fs__serialize_dir_entries(data, data_len, dir, pool);
 }
@@ -891,7 +899,7 @@ svn_fs_fs__replace_dir_entry(void **data
                              apr_pool_t *pool)
 {
   replace_baton_t *replace_baton = (replace_baton_t *)baton;
-  hash_data_t *hash_data = (hash_data_t *)*data;
+  dir_data_t *dir_data = (dir_data_t *)*data;
   svn_boolean_t found;
   svn_fs_dirent_t **entries;
   apr_uint32_t *lengths;
@@ -901,23 +909,23 @@ svn_fs_fs__replace_dir_entry(void **data
   svn_temp_serializer__context_t *context;
 
   /* after quite a number of operations, let's re-pack everything.
-   * This is to limit the number of vasted space as we cannot overwrite
+   * This is to limit the number of wasted space as we cannot overwrite
    * existing data but must always append. */
-  if (hash_data->operations > 2 + hash_data->count / 4)
+  if (dir_data->operations > 2 + dir_data->count / 4)
     return slowly_replace_dir_entry(data, data_len, baton, pool);
 
   /* resolve the reference to the entries array */
   entries = (svn_fs_dirent_t **)
-    svn_temp_deserializer__ptr((const char *)hash_data,
-                               (const void *const *)&hash_data->entries);
+    svn_temp_deserializer__ptr((const char *)dir_data,
+                               (const void *const *)&dir_data->entries);
 
   /* resolve the reference to the lengths array */
   lengths = (apr_uint32_t *)
-    svn_temp_deserializer__ptr((const char *)hash_data,
-                               (const void *const *)&hash_data->lengths);
+    svn_temp_deserializer__ptr((const char *)dir_data,
+                               (const void *const *)&dir_data->lengths);
 
   /* binary search for the desired entry by name */
-  pos = find_entry(entries, replace_baton->name, hash_data->count, &found);
+  pos = find_entry(entries, replace_baton->name, dir_data->count, &found);
 
   /* handle entry removal (if found at all) */
   if (replace_baton->new_entry == NULL)
@@ -927,14 +935,14 @@ svn_fs_fs__replace_dir_entry(void **data
           /* remove reference to the entry from the index */
           memmove(&entries[pos],
                   &entries[pos + 1],
-                  sizeof(entries[pos]) * (hash_data->count - pos));
+                  sizeof(entries[pos]) * (dir_data->count - pos));
           memmove(&lengths[pos],
                   &lengths[pos + 1],
-                  sizeof(lengths[pos]) * (hash_data->count - pos));
+                  sizeof(lengths[pos]) * (dir_data->count - pos));
 
-          hash_data->count--;
-          hash_data->over_provision++;
-          hash_data->operations++;
+          dir_data->count--;
+          dir_data->over_provision++;
+          dir_data->operations++;
         }
 
       return SVN_NO_ERROR;
@@ -946,27 +954,27 @@ svn_fs_fs__replace_dir_entry(void **data
       /* fallback to slow operation if there is no place left to insert an
        * new entry to index. That will automatically give add some spare
        * entries ("overprovision"). */
-      if (hash_data->over_provision == 0)
+      if (dir_data->over_provision == 0)
         return slowly_replace_dir_entry(data, data_len, baton, pool);
 
       /* make entries[index] available for pointing to the new entry */
       memmove(&entries[pos + 1],
               &entries[pos],
-              sizeof(entries[pos]) * (hash_data->count - pos));
+              sizeof(entries[pos]) * (dir_data->count - pos));
       memmove(&lengths[pos + 1],
               &lengths[pos],
-              sizeof(lengths[pos]) * (hash_data->count - pos));
+              sizeof(lengths[pos]) * (dir_data->count - pos));
 
-      hash_data->count++;
-      hash_data->over_provision--;
-      hash_data->operations++;
+      dir_data->count++;
+      dir_data->over_provision--;
+      dir_data->operations++;
     }
 
   /* de-serialize the new entry */
   entries[pos] = replace_baton->new_entry;
-  context = svn_temp_serializer__init_append(hash_data,
+  context = svn_temp_serializer__init_append(dir_data,
                                              entries,
-                                             hash_data->len,
+                                             dir_data->len,
                                              *data_len,
                                              pool);
   serialize_dir_entry(context, &entries[pos], &length);
@@ -980,10 +988,10 @@ svn_fs_fs__replace_dir_entry(void **data
    * pointer may no longer point to the entry in that buffer. Therefore,
    * re-map it again and store the length value after that. */
 
-  hash_data = (hash_data_t *)*data;
+  dir_data = (dir_data_t *)*data;
   lengths = (apr_uint32_t *)
-    svn_temp_deserializer__ptr((const char *)hash_data,
-                               (const void *const *)&hash_data->lengths);
+    svn_temp_deserializer__ptr((const char *)dir_data,
+                               (const void *const *)&dir_data->lengths);
   lengths[pos] = length;
 
   return SVN_NO_ERROR;

Modified: subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.h
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.h?rev=1556765&r1=1556764&r2=1556765&view=diff
==============================================================================
--- subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.h (original)
+++ subversion/branches/fsfs-ucsnorm/subversion/libsvn_fs_fs/temp_serializer.h Thu Jan  9 09:31:10 2014
@@ -159,7 +159,7 @@ svn_fs_fs__deserialize_node_revision(voi
                                      apr_pool_t *pool);
 
 /**
- * Implements #svn_cache__serialize_func_t for a directory contents hash
+ * Implements #svn_cache__serialize_func_t for a directory contents array
  */
 svn_error_t *
 svn_fs_fs__serialize_dir_entries(void **data,
@@ -168,7 +168,7 @@ svn_fs_fs__serialize_dir_entries(void **
                                  apr_pool_t *pool);
 
 /**
- * Implements #svn_cache__deserialize_func_t for a directory contents hash
+ * Implements #svn_cache__deserialize_func_t for a directory contents array
  */
 svn_error_t *
 svn_fs_fs__deserialize_dir_entries(void **out,