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,
- ¤t_path->node_id))
- break;
-
- APR_ARRAY_IDX(path_order, idx, path_order_t *) = NULL;
- node_part = get_item(context, ¤t_path->noderev_id, TRUE);
- rep_part = get_item(context, ¤t_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, ¤t_path->noderev_id, TRUE);
+ rep_part = get_item(context, ¤t_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,