Hi there,

this is a revised version of the patch posted here:
http://svn.haxx.se/dev/archive-2010-04/0107.shtml

Changes:

* use the new svn_cache__set_pinned function
* remove other changes that accidentally got added
 to the patch

-- Stefan^2.

[[[
Allow for cache entries to be access directly, i.e. without
prior copying. Use that to fix FSFS directory traversal
runtime from O(N^2) to O(n).

* subversion/libsvn_fs_fs/dag.c
 (dir_entry_id_from_node): call the new, efficient
 lookup function.
 (svn_fs_fs__dag_dir_entry): implement the new
 lookup function
 (svn_fs_fs__dag_dir_entries, svn_fs_fs__dag_delete):
 adapt to svn_fs_fs__rep_contents_dir signature change

* subversion/libsvn_fs_fs/dag.h
 (svn_fs_fs__dag_dir_entry): declare new function

* subversion/libsvn_fs_fs/fs_fs.c
 (svn_fs_fs__rep_contents_dir): add support for optionally
 returning a pinned cache reference instead of a copy.
 (svn_fs_fs__set_entry, write_final_rev): adapt to
 svn_fs_fs__rep_contents_dir signature change

* subversion/libsvn_fs_fs/fs_fs.h
 (svn_fs_fs__rep_contents_dir): add pin parameter

patch by stefanfuhrmann < at > alice-dsl.de
]]]

Index: subversion/libsvn_fs_fs/dag.c
===================================================================
--- subversion/libsvn_fs_fs/dag.c       (revision 937673)
+++ subversion/libsvn_fs_fs/dag.c       (working copy)
@@ -310,16 +310,10 @@
                        const char *name,
                        apr_pool_t *pool)
 {
-  apr_hash_t *entries;
   svn_fs_dirent_t *dirent;
   apr_pool_t *subpool = svn_pool_create(pool);
 
-  SVN_ERR(svn_fs_fs__dag_dir_entries(&entries, parent, subpool, pool));
-  if (entries)
-    dirent = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
-  else
-    dirent = NULL;
-
+  SVN_ERR(svn_fs_fs__dag_dir_entry(&dirent, parent, name, subpool, pool));
   *id_p = dirent ? svn_fs_fs__id_copy(dirent->id, pool) : NULL;
 
   svn_pool_destroy(subpool);
@@ -435,10 +429,56 @@
     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, NULL, pool);
 }
 
+svn_error_t *
+svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent,
+                         dag_node_t *node,
+                         const char* name,
+                         apr_pool_t *pool,
+                         apr_pool_t *node_pool)
+{
+  apr_hash_t *entries;
+  node_revision_t *noderev;
+  fs_fs_data_t *ffd;
+  svn_boolean_t pinned = TRUE;
+  svn_fs_dirent_t *entry;
 
+  SVN_ERR(get_node_revision(&noderev, node, node_pool));
+
+  if (noderev->kind != svn_node_dir)
+    return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+                            _("Can't get entries of non-directory"));
+
+  /* Get a dirent hash for this directory. */
+  SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, node->fs, noderev, 
+                                      &pinned, pool));
+
+  /* Find name in the ENTRIES hash. */
+  entry = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
+
+  /* Duplicate the result. */
+  if (entry)
+    {
+      svn_fs_dirent_t *new_entry = apr_palloc(pool, sizeof(*entry));
+      new_entry->name = apr_pstrdup(pool, entry->name);
+      new_entry->kind = entry->kind;
+      new_entry->id = svn_fs_fs__id_copy(entry->id, pool);
+
+      *dirent = new_entry;
+    }
+  else
+    *dirent = NULL;
+
+  /* Cleanup, if necessary */
+  ffd = node->fs->fsap_data;
+  return pinned
+      ? svn_cache__unpin((void **) entries, ffd->dir_cache, pool)
+      : SVN_NO_ERROR;
+}
+
+
 svn_error_t *
 svn_fs_fs__dag_set_entry(dag_node_t *node,
                          const char *entry_name,
@@ -769,7 +794,7 @@
   subpool = svn_pool_create(pool);
 
   /* Get a dirent hash for this directory. */
-  SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, parent_noderev, subpool));
+  SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, parent_noderev, NULL, 
subpool));
 
   /* Find name in the ENTRIES hash. */
   dirent = apr_hash_get(entries, name, APR_HASH_KEY_STRING);
Index: subversion/libsvn_fs_fs/dag.h
===================================================================
--- subversion/libsvn_fs_fs/dag.h       (revision 937673)
+++ subversion/libsvn_fs_fs/dag.h       (working copy)
@@ -304,6 +304,17 @@
                                         apr_pool_t *pool,
                                         apr_pool_t *node_pool);
 
+/* Fetches the NODE's entries and returns a copy of the entry selected
+   by the key value given in NAME and set *DIRENT to a copy of that
+   entry. If such entry was found, the copy will be allocated in POOL.
+   Otherwise, the *DIRENT will be set to NULL. NODE_POOL is used for
+   any allocation of memory that needs to live as long as NODE lives.
+ */
+svn_error_t * svn_fs_fs__dag_dir_entry(svn_fs_dirent_t **dirent,
+                                       dag_node_t *node,
+                                       const char* name,
+                                       apr_pool_t *pool,
+                                       apr_pool_t *node_pool);
 
 /* Set ENTRY_NAME in NODE to point to ID (with kind KIND), allocating
    from POOL.  NODE must be a mutable directory.  ID can refer to a
Index: subversion/libsvn_fs_fs/fs_fs.c
===================================================================
--- subversion/libsvn_fs_fs/fs_fs.c     (revision 937673)
+++ subversion/libsvn_fs_fs/fs_fs.c     (working copy)
@@ -3669,6 +3670,7 @@
 svn_fs_fs__rep_contents_dir(apr_hash_t **entries_p,
                             svn_fs_t *fs,
                             node_revision_t *noderev,
+                            svn_boolean_t* pin,
                             apr_pool_t *pool)
 {
   fs_fs_data_t *ffd = fs->fsap_data;
@@ -3682,8 +3684,15 @@
       svn_boolean_t found;
 
       unparsed_id = svn_fs_fs__id_unparse(noderev->id, pool)->data;
-      SVN_ERR(svn_cache__get((void **) entries_p, &found, ffd->dir_cache,
-                             unparsed_id, pool));
+
+      /* Cache lookup. Get a pinned reference if *pin has been set. */
+      if (pin && *pin)
+        SVN_ERR(svn_cache__get_pinned((void **) entries_p, &found, 
ffd->dir_cache,
+                                      unparsed_id, pool));
+      else
+        SVN_ERR(svn_cache__get((void **) entries_p, &found, ffd->dir_cache,
+                               unparsed_id, pool));
+
       if (found)
         return SVN_NO_ERROR;
     }
@@ -3694,8 +3703,20 @@
   SVN_ERR(parse_dir_entries(&parsed_entries, unparsed_entries, pool));
 
   /* If this is an immutable directory, let's cache the contents. */
-  if (! svn_fs_fs__id_txn_id(noderev->id))
-    SVN_ERR(svn_cache__set(ffd->dir_cache, unparsed_id, parsed_entries, pool));
+  if (svn_fs_fs__id_txn_id(noderev->id))
+    {
+      /* The result has not been pinned. */
+      if (pin)
+        *pin = FALSE;
+    }
+  else
+    {
+      if (pin && *pin)
+        return svn_cache__set_pinned(ffd->dir_cache, unparsed_id, 
parsed_entries, 
+                                     entries_p, pool);
+      else
+        SVN_ERR(svn_cache__set(ffd->dir_cache, unparsed_id, parsed_entries, 
pool));
+    }
 
   *entries_p = parsed_entries;
   return SVN_NO_ERROR;
@@ -4872,7 +4893,7 @@
         /* Before we can modify the directory, we need to dump its old
            contents into a mutable representation file. */
         SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, parent_noderev,
-                                            subpool));
+                                            NULL, subpool));
         SVN_ERR(unparse_dir_entries(&entries, entries, subpool));
         SVN_ERR(svn_io_file_open(&file, filename,
                                  APR_WRITE | APR_CREATE | APR_BUFFERED,
@@ -5523,7 +5544,7 @@
       /* This is a directory.  Write out all the children first. */
       subpool = svn_pool_create(pool);
 
-      SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, pool));
+      SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, fs, noderev, NULL, pool));
 
       for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
         {
Index: subversion/libsvn_fs_fs/fs_fs.h
===================================================================
--- subversion/libsvn_fs_fs/fs_fs.h     (revision 937673)
+++ subversion/libsvn_fs_fs/fs_fs.h     (working copy)
@@ -115,10 +115,16 @@
 /* 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. */
+   which is also used for temporary allocations. 
+   If *pin is TRUE, directory entries that have already been cached
+   will be returned as pinned references instead of copies. Otherwise, 
+   pin may be NULL. After the function returned, *pin will then indicate 
+   whether the cache lookup was successful and the caller must unpin 
+   the *entries explicitly, in that case. */
 svn_error_t *svn_fs_fs__rep_contents_dir(apr_hash_t **entries,
                                          svn_fs_t *fs,
                                          node_revision_t *noderev,
+                                         svn_boolean_t* pin,
                                          apr_pool_t *pool);
 
 /* Set *CONTENTS to be a readable svn_stream_t that receives the text

Reply via email to