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 2014/05/16 20:06:32 UTC

svn commit: r1595277 - in /subversion/trunk/subversion/libsvn_fs_x: ./ cached_data.c cached_data.h dag.c dag.h temp_serializer.c temp_serializer.h transaction.c tree.c

Author: stefan2
Date: Fri May 16 18:06:31 2014
New Revision: 1595277

URL: http://svn.apache.org/r1595277
Log:
Sync'ing FSX with FSFS:
Merge r1554711 from /subversion/libsvn_fs_fs into subversion/libsvn_fs_x.
Conflicts were due to name prefix differences (svn_fs_x vs svn_fs_fs).

This ports the "directories are arrays now" change.

Modified:
    subversion/trunk/subversion/libsvn_fs_x/   (props changed)
    subversion/trunk/subversion/libsvn_fs_x/cached_data.c
    subversion/trunk/subversion/libsvn_fs_x/cached_data.h
    subversion/trunk/subversion/libsvn_fs_x/dag.c
    subversion/trunk/subversion/libsvn_fs_x/dag.h
    subversion/trunk/subversion/libsvn_fs_x/temp_serializer.c
    subversion/trunk/subversion/libsvn_fs_x/temp_serializer.h
    subversion/trunk/subversion/libsvn_fs_x/transaction.c
    subversion/trunk/subversion/libsvn_fs_x/tree.c

Propchange: subversion/trunk/subversion/libsvn_fs_x/
------------------------------------------------------------------------------
  Merged /subversion/trunk/subversion/libsvn_fs_fs:r1554711

Modified: subversion/trunk/subversion/libsvn_fs_x/cached_data.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/cached_data.c?rev=1595277&r1=1595276&r2=1595277&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/cached_data.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/cached_data.c Fri May 16 18:06:31 2014
@@ -26,6 +26,11 @@
 
 #include "svn_hash.h"
 #include "svn_ctype.h"
+#include "svn_sorts.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_x.h"
@@ -1919,30 +1924,174 @@ svn_fs_x__get_file_delta_stream(svn_txde
   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_x__id_unparse(id, scratch_pool)->data);
+
+      if (strcmp(str, SVN_FS_X__KIND_FILE) == 0)
+        {
+          dirent->kind = svn_node_file;
+        }
+      else if (strcmp(str, SVN_FS_X__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_x__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_x__id_unparse(id, scratch_pool)->data);
+
+      dirent->id = svn_fs_x__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_x__is_revision(noderev->data_rep->id.change_set))
     {
       const char *filename
-        = svn_fs_x__path_txn_node_children(fs, noderev->id, pool);
+        = svn_fs_x__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)
@@ -1951,7 +2100,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;
       svn_stringbuf_t *text = svn_stringbuf_create_ensure(len, text_pool);
       text->len = len;
@@ -1963,7 +2112,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);
     }
@@ -1972,66 +2122,6 @@ get_dir_contents(apr_hash_t *entries,
 }
 
 
-/* Given a hash STR_ENTRIES with values as svn_string_t as specified
-   in an FSX 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_x__id_unparse(id, pool)->data);
-
-      if (strcmp(str, SVN_FS_X__KIND_FILE) == 0)
-        {
-          dirent->kind = svn_node_file;
-        }
-      else if (strcmp(str, SVN_FS_X__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_x__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_x__id_unparse(id, pool)->data);
-
-      dirent->id = svn_fs_x__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 pre-allocated *KEY.
  */
@@ -2068,39 +2158,47 @@ locate_dir_cache(svn_fs_t *fs,
 }
 
 svn_error_t *
-svn_fs_x__rep_contents_dir(apr_hash_t **entries_p,
+svn_fs_x__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)
 {
   svn_fs_x__id_part_t key;
-  apr_hash_t *unparsed_entries, *parsed_entries;
 
   /* find the cache we may use */
-  svn_cache__t *cache = locate_dir_cache(fs, &key, noderev, pool);
+  svn_cache__t *cache = locate_dir_cache(fs, &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_x__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_x__rep_contents_dir_entry(svn_fs_dirent_t **dirent,
                                  svn_fs_t *fs,
@@ -2129,18 +2227,18 @@ svn_fs_x__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_x__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_x__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);

Modified: subversion/trunk/subversion/libsvn_fs_x/cached_data.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/cached_data.h?rev=1595277&r1=1595276&r2=1595277&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/cached_data.h (original)
+++ subversion/trunk/subversion/libsvn_fs_x/cached_data.h Fri May 16 18:06:31 2014
@@ -122,15 +122,25 @@ svn_fs_x__get_file_delta_stream(svn_txde
                                 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_x__rep_contents_dir(apr_hash_t **entries_p,
+svn_fs_x__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_x__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/trunk/subversion/libsvn_fs_x/dag.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/dag.c?rev=1595277&r1=1595276&r2=1595277&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/dag.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/dag.c Fri May 16 18:06:31 2014
@@ -415,9 +415,9 @@ make_entry(dag_node_t **child_p,
 
 
 svn_error_t *
-svn_fs_x__dag_dir_entries(apr_hash_t **entries,
-                           dag_node_t *node,
-                           apr_pool_t *pool)
+svn_fs_x__dag_dir_entries(apr_array_header_t **entries,
+                          dag_node_t *node,
+                          apr_pool_t *pool)
 {
   node_revision_t *noderev;
 
@@ -427,7 +427,7 @@ svn_fs_x__dag_dir_entries(apr_hash_t **e
     return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
                             _("Can't get entries of non-directory"));
 
-  return svn_fs_x__rep_contents_dir(entries, node->fs, noderev, pool);
+  return svn_fs_x__rep_contents_dir(entries, node->fs, noderev, pool, pool);
 }
 
 svn_error_t *
@@ -856,22 +856,16 @@ svn_fs_x__dag_delete_if_mutable(svn_fs_t
   /* 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_x__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_x__dag_delete_if_mutable(fs, dirent->id, pool));
-            }
-        }
+        for (i = 0; i < entries->nelts; ++i)
+          SVN_ERR(svn_fs_x__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/trunk/subversion/libsvn_fs_x/dag.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/dag.h?rev=1595277&r1=1595276&r2=1595277&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/dag.h (original)
+++ subversion/trunk/subversion/libsvn_fs_x/dag.h Fri May 16 18:06:31 2014
@@ -264,11 +264,10 @@ svn_fs_x__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_x__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_x__dag_dir_entries(apr_array_header_t **entries_p,
                                        dag_node_t *node,
                                        apr_pool_t *pool);
 

Modified: subversion/trunk/subversion/libsvn_fs_x/temp_serializer.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/temp_serializer.c?rev=1595277&r1=1595276&r2=1595277&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/temp_serializer.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/temp_serializer.c Fri May 16 18:06:31 2014
@@ -24,16 +24,18 @@
 
 #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.
@@ -181,8 +183,8 @@ svn_fs_x__deserialize_apr_array(void *bu
   (*array)->pool = pool;
 }
 
-/* auxiliary 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;
@@ -205,14 +207,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
@@ -243,91 +238,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 auxiliary 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_x__id_deserialize(entry, (svn_fs_id_t **)&entry->id, pool);
 
       /* 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 */
@@ -754,7 +743,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;
 }
@@ -765,7 +754,7 @@ svn_fs_x__serialize_dir_entries(void **d
                                 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 */
@@ -781,10 +770,10 @@ svn_fs_x__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;
 }
@@ -857,22 +846,22 @@ svn_fs_x__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 */
@@ -912,14 +901,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_x__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_x__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_x__serialize_dir_entries(data, data_len, dir, pool);
 }
@@ -931,7 +939,7 @@ svn_fs_x__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;
@@ -941,23 +949,23 @@ svn_fs_x__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(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(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)
@@ -967,14 +975,14 @@ svn_fs_x__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;
@@ -986,27 +994,27 @@ svn_fs_x__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);
@@ -1020,10 +1028,10 @@ svn_fs_x__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(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/trunk/subversion/libsvn_fs_x/temp_serializer.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/temp_serializer.h?rev=1595277&r1=1595276&r2=1595277&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/temp_serializer.h (original)
+++ subversion/trunk/subversion/libsvn_fs_x/temp_serializer.h Fri May 16 18:06:31 2014
@@ -182,7 +182,7 @@ svn_fs_x__deserialize_node_revision(void
                                     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_x__serialize_dir_entries(void **data,
@@ -191,7 +191,7 @@ svn_fs_x__serialize_dir_entries(void **d
                                 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_x__deserialize_dir_entries(void **out,

Modified: subversion/trunk/subversion/libsvn_fs_x/transaction.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/transaction.c?rev=1595277&r1=1595276&r2=1595277&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/transaction.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/transaction.c Fri May 16 18:06:31 2014
@@ -782,50 +782,45 @@ store_sha1_rep_mapping(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
-
-static const char *
-unparse_dir_entry(svn_node_kind_t kind, const svn_fs_id_t *id,
+static svn_error_t *
+unparse_dir_entry(svn_fs_dirent_t *dirent,
+                  svn_stream_t *stream,
                   apr_pool_t *pool)
 {
-  return apr_psprintf(pool, "%s %s",
-                      (kind == svn_node_file) ? SVN_FS_X__KIND_FILE
-                                              : SVN_FS_X__KIND_DIR,
-                      svn_fs_x__id_unparse(id, pool)->data);
+  const char *val
+    = apr_psprintf(pool, "%s %s",
+                   (dirent->kind == svn_node_file) ? SVN_FS_X__KIND_FILE
+                                                   : SVN_FS_X__KIND_DIR,
+                   svn_fs_x__id_unparse(dirent->id, pool)->data);
+
+  SVN_ERR(svn_stream_printf(stream, pool, "K %" APR_SIZE_T_FMT "\n%s\n"
+                            "V %" APR_SIZE_T_FMT "\n%s\n",
+                            strlen(dirent->name), dirent->name,
+                            strlen(val), val));
+  return SVN_NO_ERROR;
 }
 
-/* Given a hash ENTRIES of dirent structions, return a hash in
-   *STR_ENTRIES_P, that has svn_string_t as the values in the format
-   specified by the fs_x directory contents file.  Perform
-   allocations in POOL. */
+/* Write the directory given as array of dirent structs in ENTRIES to STREAM.
+   Perform temporary allocations in POOL. */
 static svn_error_t *
-unparse_dir_entries(apr_hash_t **str_entries_p,
-                    apr_hash_t *entries,
+unparse_dir_entries(apr_array_header_t *entries,
+                    svn_stream_t *stream,
                     apr_pool_t *pool)
 {
-  apr_hash_index_t *hi;
-
-  /* For now, we use a our own hash function to ensure that we get a
-   * (largely) stable order when serializing the data.  It also gives
-   * us some performance improvement.
-   *
-   * ### TODO ###
-   * Use some sorted or other fixed order data container.
-   */
-  *str_entries_p = svn_hash__make(pool);
-
-  for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
+  apr_pool_t *iterpool = svn_pool_create(pool);
+  int i;
+  for (i = 0; i < entries->nelts; ++i)
     {
-      const void *key;
-      apr_ssize_t klen;
-      svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
-      const char *new_val;
-
-      apr_hash_this(hi, &key, &klen, NULL);
-      new_val = unparse_dir_entry(dirent->kind, dirent->id, pool);
-      apr_hash_set(*str_entries_p, key, klen,
-                   svn_string_create(new_val, pool));
+      svn_fs_dirent_t *dirent;
+
+      svn_pool_clear(iterpool);
+      dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
+      SVN_ERR(unparse_dir_entry(dirent, stream, iterpool));
     }
 
+  SVN_ERR(svn_stream_printf(stream, pool, "%s\n", SVN_HASH_TERMINATOR));
+
+  svn_pool_destroy(iterpool);
   return SVN_NO_ERROR;
 }
 
@@ -1693,18 +1688,17 @@ svn_fs_x__set_entry(svn_fs_t *fs,
 
   if (!rep || !svn_fs_x__is_txn(rep->id.change_set))
     {
-      apr_hash_t *entries;
+      apr_array_header_t *entries;
 
       /* Before we can modify the directory, we need to dump its old
          contents into a mutable representation file. */
       SVN_ERR(svn_fs_x__rep_contents_dir(&entries, fs, parent_noderev,
-                                         subpool));
-      SVN_ERR(unparse_dir_entries(&entries, entries, subpool));
+                                         subpool, subpool));
       SVN_ERR(svn_io_file_open(&file, filename,
                                APR_WRITE | APR_CREATE | APR_BUFFERED,
                                APR_OS_DEFAULT, pool));
       out = svn_stream_from_aprfile2(file, TRUE, pool);
-      SVN_ERR(svn_hash_write2(entries, out, SVN_HASH_TERMINATOR, subpool));
+      SVN_ERR(unparse_dir_entries(entries, out, subpool));
 
       svn_pool_clear(subpool);
 
@@ -1752,12 +1746,12 @@ svn_fs_x__set_entry(svn_fs_t *fs,
   /* Append an incremental hash entry for the entry change. */
   if (id)
     {
-      const char *val = unparse_dir_entry(kind, id, subpool);
+      svn_fs_dirent_t entry;
+      entry.name = name;
+      entry.id = id;
+      entry.kind = kind;
 
-      SVN_ERR(svn_stream_printf(out, subpool, "K %" APR_SIZE_T_FMT "\n%s\n"
-                                "V %" APR_SIZE_T_FMT "\n%s\n",
-                                strlen(name), name,
-                                strlen(val), val));
+      SVN_ERR(unparse_dir_entry(&entry, out, subpool));
     }
   else
     {
@@ -2410,8 +2404,8 @@ svn_fs_x__set_proplist(svn_fs_t *fs,
   return SVN_NO_ERROR;
 }
 
-/* This baton is used by the stream created for write_hash_rep. */
-struct write_hash_baton
+/* This baton is used by the stream created for write_container_rep. */
+struct write_container_baton
 {
   svn_stream_t *stream;
 
@@ -2421,15 +2415,15 @@ struct write_hash_baton
   svn_checksum_ctx_t *sha1_ctx;
 };
 
-/* The handler for the write_hash_rep stream.  BATON is a
-   write_hash_baton, DATA has the data to write and *LEN is the number
+/* The handler for the write_container_rep stream.  BATON is a
+   write_container_baton, DATA has the data to write and *LEN is the number
    of bytes to write. */
 static svn_error_t *
-write_hash_handler(void *baton,
-                   const char *data,
-                   apr_size_t *len)
+write_container_handler(void *baton,
+                        const char *data,
+                        apr_size_t *len)
 {
-  struct write_hash_baton *whb = baton;
+  struct write_container_baton *whb = baton;
 
   SVN_ERR(svn_checksum_update(whb->md5_ctx, data, *len));
   SVN_ERR(svn_checksum_update(whb->sha1_ctx, data, *len));
@@ -2440,25 +2434,64 @@ write_hash_handler(void *baton,
   return SVN_NO_ERROR;
 }
 
-/* Write out the hash HASH pertaining to the NODEREV in FS as a deltified
-   text representation to file FILE.  In the process, record the total size
-   and the md5 digest in REP.  If rep sharing has been enabled and REPS_HASH
-   is not NULL, it will be used in addition to the on-disk cache to find
-   earlier reps with the same content.  When such existing reps can be found,
-   we will truncate the one just written from the file and return the existing
-   rep.  If PROPS is set, assume that we want to a props representation as
-   the base for our delta.  Perform temporary allocations in POOL. */
-static svn_error_t *
-write_hash_delta_rep(representation_t *rep,
-                     apr_file_t *file,
-                     apr_hash_t *hash,
-                     svn_fs_t *fs,
-                     svn_fs_x__txn_id_t txn_id,
-                     node_revision_t *noderev,
-                     apr_hash_t *reps_hash,
-                     int item_type,
+/* Callback function type.  Write the data provided by BATON into STREAM. */
+typedef svn_error_t *
+(* collection_writer_t)(svn_stream_t *stream, void *baton, apr_pool_t *pool);
+
+/* Implement collection_writer_t writing the C string->svn_string_t hash
+   given as BATON. */
+static svn_error_t *
+write_hash_to_stream(svn_stream_t *stream,
+                     void *baton,
                      apr_pool_t *pool)
 {
+  apr_hash_t *hash = baton;
+  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
+
+  return SVN_NO_ERROR;
+}
+
+/* Implement collection_writer_t writing the svn_fs_dirent_t* array given
+   as BATON. */
+static svn_error_t *
+write_directory_to_stream(svn_stream_t *stream,
+                          void *baton,
+                          apr_pool_t *pool)
+{
+  apr_array_header_t *dir = baton;
+  SVN_ERR(unparse_dir_entries(dir, stream, pool));
+
+  return SVN_NO_ERROR;
+}
+
+
+/* Write out the COLLECTION pertaining to the NODEREV in FS as a deltified
+   text representation to file FILE using WRITER.  In the process, record the
+   total size and the md5 digest in REP and add the representation of type
+   ITEM_TYPE to the indexes if necessary.  If rep sharing has been enabled and
+   REPS_HASH is not NULL, it will be used in addition to the on-disk cache to
+   find earlier reps with the same content.  When such existing reps can be
+   found, we will truncate the one just written from the file and return the
+   existing rep.
+
+   If ITEM_TYPE is IS_PROPS equals SVN_FS_FS__ITEM_TYPE_*_PROPS, assume
+   that we want to a props representation as the base for our delta.
+   If FINAL_REVISION is not SVN_INVALID_REVNUM, use it to determine whether
+   to write to the proto-index files.  Perform temporary allocations in POOL.
+ */
+static svn_error_t *
+write_container_delta_rep(representation_t *rep,
+                          apr_file_t *file,
+                          void *collection,
+                          collection_writer_t writer,
+                          svn_fs_t *fs,
+                          svn_fs_x__txn_id_t txn_id,
+                          node_revision_t *noderev,
+                          apr_hash_t *reps_hash,
+                          int item_type,
+                          svn_revnum_t final_revision,
+                          apr_pool_t *pool)
+{
   svn_txdelta_window_handler_t diff_wh;
   void *diff_whb;
 
@@ -2473,7 +2506,7 @@ write_hash_delta_rep(representation_t *r
   apr_off_t delta_start = 0;
   apr_off_t offset = 0;
 
-  struct write_hash_baton *whb;
+  struct write_container_baton *whb;
   int diff_version = 1;
   svn_boolean_t is_props = (item_type == SVN_FS_X__ITEM_TYPE_FILE_PROPS)
                         || (item_type == SVN_FS_X__ITEM_TYPE_DIR_PROPS);
@@ -2517,9 +2550,9 @@ write_hash_delta_rep(representation_t *r
 
   /* serialize the hash */
   stream = svn_stream_create(whb, pool);
-  svn_stream_set_write(stream, write_hash_handler);
+  svn_stream_set_write(stream, write_container_handler);
 
-  SVN_ERR(svn_hash_write2(hash, stream, SVN_HASH_TERMINATOR, pool));
+  SVN_ERR(writer(stream, collection, pool));
   SVN_ERR(svn_stream_close(whb->stream));
 
   /* Store the results. */
@@ -2700,24 +2733,18 @@ write_final_rev(const svn_fs_id_t **new_
   if (noderev->kind == svn_node_dir)
     {
       apr_pool_t *subpool;
-      apr_hash_t *entries, *str_entries;
-      apr_array_header_t *sorted_entries;
+      apr_array_header_t *entries;
       int i;
 
       /* This is a directory.  Write out all the children first. */
       subpool = svn_pool_create(pool);
 
-      SVN_ERR(svn_fs_x__rep_contents_dir(&entries, fs, noderev, pool));
-      /* For the sake of the repository administrator sort the entries
-         so that the final file is deterministic and repeatable,
-         however the rest of the FSX code doesn't require any
-         particular order here. */
-      sorted_entries = svn_sort__hash(entries, svn_sort_compare_items_lexically,
-                                      pool);
-      for (i = 0; i < sorted_entries->nelts; ++i)
+      SVN_ERR(svn_fs_x__rep_contents_dir(&entries, fs, noderev, pool,
+                                         subpool));
+      for (i = 0; i < entries->nelts; ++i)
         {
-          svn_fs_dirent_t *dirent = APR_ARRAY_IDX(sorted_entries, i,
-                                                  svn_sort__item_t).value;
+          svn_fs_dirent_t *dirent
+            = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
 
           svn_pool_clear(subpool);
           SVN_ERR(write_final_rev(&new_id, file, rev, fs, dirent->id,
@@ -2732,13 +2759,13 @@ write_final_rev(const svn_fs_id_t **new_
           && ! svn_fs_x__is_revision(noderev->data_rep->id.change_set))
         {
           /* Write out the contents of this directory as a text rep. */
-          SVN_ERR(unparse_dir_entries(&str_entries, entries, pool));
           noderev->data_rep->id.change_set = change_set;
-
-          SVN_ERR(write_hash_delta_rep(noderev->data_rep, file,
-                                       str_entries, fs, txn_id, noderev,
-                                       NULL, SVN_FS_X__ITEM_TYPE_DIR_REP,
-                                       pool));
+          SVN_ERR(write_container_delta_rep(noderev->data_rep, file,
+                                            entries,
+                                            write_directory_to_stream,
+                                            fs, txn_id, noderev, NULL,
+                                            SVN_FS_X__ITEM_TYPE_DIR_REP,
+                                            rev, pool));
         }
     }
   else
@@ -2766,9 +2793,10 @@ write_final_rev(const svn_fs_id_t **new_
 
       noderev->prop_rep->id.change_set = change_set;
 
-      SVN_ERR(write_hash_delta_rep(noderev->prop_rep, file,
-                                   proplist, fs, txn_id, noderev,
-                                   reps_hash, item_type, pool));
+      SVN_ERR(write_container_delta_rep(noderev->prop_rep, file, proplist,
+                                        write_hash_to_stream, fs, txn_id,
+                                        noderev, reps_hash, item_type, rev,
+                                        pool));
     }
 
   /* Convert our temporary ID into a permanent revision one. */

Modified: subversion/trunk/subversion/libsvn_fs_x/tree.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_fs_x/tree.c?rev=1595277&r1=1595276&r2=1595277&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_fs_x/tree.c (original)
+++ subversion/trunk/subversion/libsvn_fs_x/tree.c Fri May 16 18:06:31 2014
@@ -49,6 +49,7 @@
 #include "svn_mergeinfo.h"
 #include "svn_fs.h"
 #include "svn_props.h"
+#include "svn_sorts.h"
 
 #include "fs.h"
 #include "dag.h"
@@ -1642,33 +1643,23 @@ compare_dir_structure(svn_boolean_t *cha
                       dag_node_t *rhs,
                       apr_pool_t *pool)
 {
-  apr_hash_t *lhs_entries;
-  apr_hash_t *rhs_entries;
-  apr_hash_index_t *hi;
+  apr_array_header_t *lhs_entries;
+  apr_array_header_t *rhs_entries;
+  int i;
 
   SVN_ERR(svn_fs_x__dag_dir_entries(&lhs_entries, lhs, pool));
   SVN_ERR(svn_fs_x__dag_dir_entries(&rhs_entries, rhs, pool));
 
-  /* different number of entries -> some addition / removal */
-  if (apr_hash_count(lhs_entries) != apr_hash_count(rhs_entries))
-    {
-      *changed = TRUE;
-      return SVN_NO_ERROR;
-    }
-
-  /* Since the number of dirents is the same, we simply need to do a 
-     one-sided comparison. */
-  for (hi = apr_hash_first(pool, lhs_entries); hi; hi = apr_hash_next(hi))
-    {
-      svn_fs_dirent_t *lhs_entry;
-      svn_fs_dirent_t *rhs_entry;
-      const char *name;
-      apr_ssize_t klen;
-
-      apr_hash_this(hi, (const void **)&name, &klen, (void **)&lhs_entry);
-      rhs_entry = apr_hash_get(rhs_entries, name, klen);
+  /* Since directories are sorted by name, we can simply compare their
+     entries one-by-one without binary lookup etc. */
+  for (i = 0; i < lhs_entries->nelts; ++i)
+    {
+      svn_fs_dirent_t *lhs_entry
+        = APR_ARRAY_IDX(lhs_entries, i, svn_fs_dirent_t *);
+      svn_fs_dirent_t *rhs_entry
+        = APR_ARRAY_IDX(rhs_entries, i, svn_fs_dirent_t *);
 
-      if (!rhs_entry
+      if (strcmp(lhs_entry->name, rhs_entry->name)
           || !svn_fs_x__id_part_eq(svn_fs_x__id_node_id(lhs_entry->id),
                                    svn_fs_x__id_node_id(rhs_entry->id))
           || !svn_fs_x__id_part_eq(svn_fs_x__id_copy_id(lhs_entry->id),
@@ -1716,8 +1707,8 @@ merge(svn_stringbuf_t *conflict_p,
       apr_pool_t *pool)
 {
   const svn_fs_id_t *source_id, *target_id, *ancestor_id;
-  apr_hash_t *s_entries, *t_entries, *a_entries;
-  apr_hash_index_t *hi;
+  apr_array_header_t *s_entries, *t_entries, *a_entries;
+  int i, s_idx = -1, t_idx = -1;
   svn_fs_t *fs;
   apr_pool_t *iterpool;
   apr_int64_t mergeinfo_increment = 0;
@@ -1883,27 +1874,19 @@ merge(svn_stringbuf_t *conflict_p,
 
   /* for each entry E in a_entries... */
   iterpool = svn_pool_create(pool);
-  for (hi = apr_hash_first(pool, a_entries);
-       hi;
-       hi = apr_hash_next(hi))
+  for (i = 0; i < a_entries->nelts; ++i)
     {
       svn_fs_dirent_t *s_entry, *t_entry, *a_entry;
-      const char *name;
-      apr_ssize_t klen;
-
       svn_pool_clear(iterpool);
 
-      name = svn__apr_hash_index_key(hi);
-      klen = svn__apr_hash_index_klen(hi);
-      a_entry = svn__apr_hash_index_val(hi);
-
-      s_entry = apr_hash_get(s_entries, name, klen);
-      t_entry = apr_hash_get(t_entries, name, klen);
+      a_entry = APR_ARRAY_IDX(a_entries, i, svn_fs_dirent_t *);
+      s_entry = svn_fs_x__find_dir_entry(s_entries, a_entry->name, &s_idx);
+      t_entry = svn_fs_x__find_dir_entry(t_entries, a_entry->name, &t_idx);
 
       /* No changes were made to this entry while the transaction was
          in progress, so do nothing to the target. */
       if (s_entry && svn_fs_x__id_eq(a_entry->id, s_entry->id))
-        goto end;
+        continue;
 
       /* A change was made to this entry while the transaction was in
          process, but the transaction did not touch this entry. */
@@ -1929,15 +1912,16 @@ merge(svn_stringbuf_t *conflict_p,
                                                         s_ent_node));
               mergeinfo_increment += mergeinfo_end;
 
-              SVN_ERR(svn_fs_x__dag_set_entry(target, name,
+              SVN_ERR(svn_fs_x__dag_set_entry(target, a_entry->name,
                                               s_entry->id,
                                               s_entry->kind,
                                               txn_id,
-                                              pool));
+                                              iterpool));
             }
           else
             {
-              SVN_ERR(svn_fs_x__dag_delete(target, name, txn_id, iterpool));
+              SVN_ERR(svn_fs_x__dag_delete(target, a_entry->name, txn_id,
+                                           iterpool));
             }
         }
 
@@ -2000,30 +1984,24 @@ merge(svn_stringbuf_t *conflict_p,
                         iterpool));
           mergeinfo_increment += sub_mergeinfo_increment;
         }
-
-      /* We've taken care of any possible implications E could have.
-         Remove it from source_entries, so it's easy later to loop
-         over all the source entries that didn't exist in
-         ancestor_entries. */
-    end:
-      apr_hash_set(s_entries, name, klen, NULL);
     }
 
   /* For each entry E in source but not in ancestor */
-  for (hi = apr_hash_first(pool, s_entries);
-       hi;
-       hi = apr_hash_next(hi))
-    {
-      svn_fs_dirent_t *s_entry, *t_entry;
-      const char *name = svn__apr_hash_index_key(hi);
-      apr_ssize_t klen = svn__apr_hash_index_klen(hi);
+  for (i = 0; i < s_entries->nelts; ++i)
+    {
+      svn_fs_dirent_t *a_entry, *s_entry, *t_entry;
       dag_node_t *s_ent_node;
       apr_int64_t mergeinfo_s;
 
       svn_pool_clear(iterpool);
 
-      s_entry = svn__apr_hash_index_val(hi);
-      t_entry = apr_hash_get(t_entries, name, klen);
+      s_entry = APR_ARRAY_IDX(s_entries, i, svn_fs_dirent_t *);
+      a_entry = svn_fs_x__find_dir_entry(a_entries, s_entry->name, &s_idx);
+      t_entry = svn_fs_x__find_dir_entry(t_entries, s_entry->name, &t_idx);
+
+      /* Process only entries in source that are NOT in ancestor. */
+      if (a_entry)
+        continue;
 
       /* If NAME exists in TARGET, declare a conflict. */
       if (t_entry)
@@ -2324,10 +2302,23 @@ x_dir_entries(apr_hash_t **table_p,
               apr_pool_t *pool)
 {
   dag_node_t *node;
+  apr_hash_t *hash = svn_hash__make(pool);
+  apr_array_header_t *table;
+  int i;
 
   /* Get the entries for this path in the caller's pool. */
   SVN_ERR(get_dag(&node, root, path, FALSE, pool));
-  return svn_fs_x__dag_dir_entries(table_p, node, pool);
+  SVN_ERR(svn_fs_x__dag_dir_entries(&table, node, pool));
+
+  /* Convert directory array to hash. */
+  for (i = 0; i < table->nelts; ++i)
+    {
+      svn_fs_dirent_t *entry = APR_ARRAY_IDX(table, i, svn_fs_dirent_t *);
+      svn_hash_sets(hash, entry->name, entry);
+    }
+
+  *table_p = hash;
+  return SVN_NO_ERROR;
 }
 
 static svn_error_t *
@@ -3819,17 +3810,14 @@ crawl_directory_dag_for_mergeinfo(svn_fs
                                   apr_pool_t *result_pool,
                                   apr_pool_t *scratch_pool)
 {
-  apr_hash_t *entries;
-  apr_hash_index_t *hi;
+  apr_array_header_t *entries;
+  int i;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
 
   SVN_ERR(svn_fs_x__dag_dir_entries(&entries, dir_dag, scratch_pool));
-
-  for (hi = apr_hash_first(scratch_pool, entries);
-       hi;
-       hi = apr_hash_next(hi))
+  for (i = 0; i < entries->nelts; ++i)
     {
-      svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
+      svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
       const char *kid_path;
       dag_node_t *kid_dag;
       svn_boolean_t has_mergeinfo, go_down;
@@ -4361,18 +4349,17 @@ verify_node(dag_node_t *node,
     }
   if (kind == svn_node_dir)
     {
-      apr_hash_t *entries;
-      apr_hash_index_t *hi;
+      apr_array_header_t *entries;
+      int i;
       apr_int64_t children_mergeinfo = 0;
 
       SVN_ERR(svn_fs_x__dag_dir_entries(&entries, node, pool));
 
       /* Compute CHILDREN_MERGEINFO. */
-      for (hi = apr_hash_first(pool, entries);
-           hi;
-           hi = apr_hash_next(hi))
+      for (i = 0; i < entries->nelts; ++i)
         {
-          svn_fs_dirent_t *dirent = svn__apr_hash_index_val(hi);
+          svn_fs_dirent_t *dirent
+            = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
           dag_node_t *child;
           apr_int64_t child_mergeinfo;