#include "svn_dirent_uri.h"
#include "svn_fs.h"
#include "svn_pools.h"
#include "svn_sorts.h"
#include "private/svn_cache.h"
#include "private/svn_sorts_private.h"
#include "private/svn_string_private.h"
#include "private/svn_fs_fs_private.h"
#include "index.h"
#include "pack.h"
#include "rev_file.h"
#include "util.h"
#include "fs_fs.h"
#include "cached_data.h"
#include "low_level.h"
#include "../libsvn_fs/fs-loader.h"
#include "svn_private_config.h"
typedef enum rep_kind_t
{
unused_rep,
dir_property_rep,
file_property_rep,
dir_rep,
file_rep
} rep_kind_t;
typedef struct rep_stats_t
{
apr_uint64_t item_index;
apr_uint64_t size;
apr_uint64_t expanded_size;
svn_revnum_t revision;
apr_uint32_t ref_count;
apr_uint16_t header_size;
char kind;
apr_byte_t chain_length;
} rep_stats_t;
typedef struct rep_ref_t
{
svn_revnum_t revision;
apr_uint64_t item_index;
svn_revnum_t base_revision;
apr_uint64_t base_item_index;
apr_uint16_t header_size;
} rep_ref_t;
typedef struct revision_info_t
{
svn_revnum_t revision;
apr_off_t offset;
apr_uint64_t changes_len;
apr_uint64_t change_count;
apr_off_t end;
apr_uint64_t dir_noderev_count;
apr_uint64_t file_noderev_count;
apr_uint64_t dir_noderev_size;
apr_uint64_t file_noderev_size;
apr_array_header_t *representations;
svn_fs_fs__revision_file_t *rev_file;
} revision_info_t;
typedef struct query_t
{
svn_fs_t *fs;
svn_revnum_t head;
int shard_size;
svn_revnum_t min_unpacked_rev;
apr_array_header_t *revisions;
rep_stats_t *null_base;
svn_fs_fs__stats_t *stats;
svn_fs_progress_notify_func_t progress_func;
void *progress_baton;
svn_cancel_func_t cancel_func;
void *cancel_baton;
} query_t;
static void
initialize_largest_changes(svn_fs_fs__stats_t *stats,
apr_size_t count,
apr_pool_t *result_pool)
{
apr_size_t i;
stats->largest_changes = apr_pcalloc(result_pool,
sizeof(*stats->largest_changes));
stats->largest_changes->count = count;
stats->largest_changes->min_size = 1;
stats->largest_changes->changes
= apr_palloc(result_pool, count * sizeof(*stats->largest_changes->changes));
for (i = 0; i < count; ++i)
stats->largest_changes->changes[i]
= apr_palloc(result_pool, sizeof(**stats->largest_changes->changes));
for (i = 0; i < count; ++i)
{
stats->largest_changes->changes[i]->size = 0;
stats->largest_changes->changes[i]->revision = SVN_INVALID_REVNUM;
stats->largest_changes->changes[i]->path
= svn_stringbuf_create_ensure(1024, result_pool);
}
}
static void
add_to_histogram(svn_fs_fs__histogram_t *histogram,
apr_int64_t size)
{
apr_int64_t shift = 0;
while (((apr_int64_t)(1) << shift) <= size)
shift++;
histogram->total.count++;
histogram->total.sum += size;
histogram->lines[(apr_size_t)shift].count++;
histogram->lines[(apr_size_t)shift].sum += size;
}
static void
add_change(svn_fs_fs__stats_t *stats,
apr_uint64_t rep_size,
apr_uint64_t expanded_size,
svn_revnum_t revision,
const char *path,
rep_kind_t kind,
svn_boolean_t plain_added)
{
if (rep_size >= stats->largest_changes->min_size)
{
apr_size_t i;
svn_fs_fs__largest_changes_t *largest_changes = stats->largest_changes;
svn_fs_fs__large_change_info_t *info
= largest_changes->changes[largest_changes->count - 1];
info->size = rep_size;
info->revision = revision;
svn_stringbuf_set(info->path, path);
for (i = largest_changes->count - 1; i > 0; --i)
if (largest_changes->changes[i-1]->size >= rep_size)
break;
else
largest_changes->changes[i] = largest_changes->changes[i-1];
largest_changes->changes[i] = info;
largest_changes->min_size
= largest_changes->changes[largest_changes->count-1]->size;
}
add_to_histogram(&stats->rep_size_histogram, rep_size);
add_to_histogram(&stats->node_size_histogram, expanded_size);
if (plain_added)
{
add_to_histogram(&stats->added_rep_size_histogram, rep_size);
add_to_histogram(&stats->added_node_size_histogram, expanded_size);
}
switch (kind)
{
case unused_rep:
add_to_histogram(&stats->unused_rep_histogram, rep_size);
break;
case dir_property_rep:
add_to_histogram(&stats->dir_prop_rep_histogram, rep_size);
add_to_histogram(&stats->dir_prop_histogram, expanded_size);
break;
case file_property_rep:
add_to_histogram(&stats->file_prop_rep_histogram, rep_size);
add_to_histogram(&stats->file_prop_histogram, expanded_size);
break;
case dir_rep:
add_to_histogram(&stats->dir_rep_histogram, rep_size);
add_to_histogram(&stats->dir_histogram, expanded_size);
break;
case file_rep:
add_to_histogram(&stats->file_rep_histogram, rep_size);
add_to_histogram(&stats->file_histogram, expanded_size);
break;
}
if (kind == file_rep)
{
svn_fs_fs__extension_info_t *info;
const char * file_name = strrchr(path, '/');
const char * extension = file_name ? strrchr(file_name, '.') : NULL;
if (extension == NULL || extension == file_name + 1)
extension = "(none)";
info = apr_hash_get(stats->by_extension, extension, APR_HASH_KEY_STRING);
if (info == NULL)
{
apr_pool_t *pool = apr_hash_pool_get(stats->by_extension);
info = apr_pcalloc(pool, sizeof(*info));
info->extension = apr_pstrdup(pool, extension);
apr_hash_set(stats->by_extension, info->extension,
APR_HASH_KEY_STRING, info);
}
add_to_histogram(&info->node_histogram, expanded_size);
add_to_histogram(&info->rep_histogram, rep_size);
}
}
static int
compare_representation_item_index(const void *data, const void *key)
{
apr_uint64_t lhs = (*(const rep_stats_t *const *)data)->item_index;
apr_uint64_t rhs = *(const apr_uint64_t *)key;
if (lhs < rhs)
return -1;
return (lhs > rhs ? 1 : 0);
}
static rep_stats_t *
find_representation(int *idx,
query_t *query,
revision_info_t **revision_info,
svn_revnum_t revision,
apr_uint64_t item_index)
{
revision_info_t *info;
*idx = -1;
info = revision_info ? *revision_info : NULL;
if (info == NULL || info->revision != revision)
{
info = APR_ARRAY_IDX(query->revisions, revision, revision_info_t*);
if (revision_info)
*revision_info = info;
}
if (info == NULL)
return NULL;
*idx = svn_sort__bsearch_lower_bound(info->representations,
&item_index,
compare_representation_item_index);
if (*idx < info->representations->nelts)
{
rep_stats_t *result
= APR_ARRAY_IDX(info->representations, *idx, rep_stats_t *);
if (result->item_index == item_index)
return result;
}
return NULL;
}
static svn_error_t *
parse_representation(rep_stats_t **representation,
query_t *query,
representation_t *rep,
revision_info_t *revision_info,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
rep_stats_t *result;
int idx;
result = find_representation(&idx, query, &revision_info, rep->revision,
rep->item_index);
if (!result)
{
result = apr_pcalloc(result_pool, sizeof(*result));
result->revision = rep->revision;
result->expanded_size = rep->expanded_size;
result->item_index = rep->item_index;
result->size = rep->size;
if (!svn_fs_fs__use_log_addressing(query->fs))
{
svn_fs_fs__rep_header_t *header;
apr_off_t offset = revision_info->offset
+ (apr_off_t)rep->item_index;
SVN_ERR_ASSERT(revision_info->rev_file);
SVN_ERR(svn_io_file_seek(revision_info->rev_file->file, APR_SET,
&offset, scratch_pool));
SVN_ERR(svn_fs_fs__read_rep_header(&header,
revision_info->rev_file->stream,
scratch_pool, scratch_pool));
result->header_size = header->header_size;
if (header->type == svn_fs_fs__rep_delta)
{
int base_idx;
rep_stats_t *base_rep
= find_representation(&base_idx, query, NULL,
header->base_revision,
header->base_item_index);
result->chain_length = 1 + MIN(base_rep->chain_length,
(apr_byte_t)0xfe);
}
else
{
result->chain_length = 1;
}
}
svn_sort__array_insert(revision_info->representations, &result, idx);
}
*representation = result;
return SVN_NO_ERROR;
}
static svn_error_t *
read_noderev(query_t *query,
svn_stringbuf_t *noderev_str,
revision_info_t *revision_info,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
static svn_error_t *
read_phsy_noderev(svn_stringbuf_t **noderev,
query_t *query,
apr_off_t offset,
revision_info_t *revision_info,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *noderev_str = svn_stringbuf_create_empty(result_pool);
svn_stringbuf_t *line;
svn_boolean_t eof;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
SVN_ERR_ASSERT(revision_info->rev_file);
offset += revision_info->offset;
SVN_ERR(svn_io_file_seek(revision_info->rev_file->file, APR_SET,
&offset, scratch_pool));
do
{
svn_pool_clear(iterpool);
SVN_ERR(svn_stream_readline(revision_info->rev_file->stream, &line,
"\n", &eof, iterpool));
svn_stringbuf_appendstr(noderev_str, line);
svn_stringbuf_appendbyte(noderev_str, '\n');
}
while (line->len > 0 && !eof);
*noderev = noderev_str;
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
parse_dir(query_t *query,
node_revision_t *noderev,
revision_info_t *revision_info,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
apr_array_header_t *entries;
SVN_ERR(svn_fs_fs__rep_contents_dir(&entries, query->fs, noderev,
scratch_pool, scratch_pool));
for (i = 0; i < entries->nelts; ++i)
{
svn_fs_dirent_t *dirent = APR_ARRAY_IDX(entries, i, svn_fs_dirent_t *);
if (svn_fs_fs__id_rev(dirent->id) == revision_info->revision)
{
svn_stringbuf_t *noderev_str;
svn_pool_clear(iterpool);
SVN_ERR(read_phsy_noderev(&noderev_str, query,
svn_fs_fs__id_item(dirent->id),
revision_info, iterpool, iterpool));
SVN_ERR(read_noderev(query, noderev_str, revision_info,
result_pool, iterpool));
}
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
read_noderev(query_t *query,
svn_stringbuf_t *noderev_str,
revision_info_t *revision_info,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
rep_stats_t *text = NULL;
rep_stats_t *props = NULL;
node_revision_t *noderev;
svn_stream_t *stream = svn_stream_from_stringbuf(noderev_str, scratch_pool);
SVN_ERR(svn_fs_fs__read_noderev(&noderev, stream, scratch_pool,
scratch_pool));
SVN_ERR(svn_fs_fs__fixup_expanded_size(query->fs, noderev->data_rep,
scratch_pool));
SVN_ERR(svn_fs_fs__fixup_expanded_size(query->fs, noderev->prop_rep,
scratch_pool));
if (noderev->data_rep)
{
SVN_ERR(parse_representation(&text, query,
noderev->data_rep, revision_info,
result_pool, scratch_pool));
if (++text->ref_count == 1)
text->kind = noderev->kind == svn_node_dir ? dir_rep : file_rep;
}
if (noderev->prop_rep)
{
SVN_ERR(parse_representation(&props, query,
noderev->prop_rep, revision_info,
result_pool, scratch_pool));
if (++props->ref_count == 1)
props->kind = noderev->kind == svn_node_dir ? dir_property_rep
: file_property_rep;
}
if (text && text->ref_count == 1)
add_change(query->stats, text->size, text->expanded_size, text->revision,
noderev->created_path, text->kind, !noderev->predecessor_id);
if (props && props->ref_count == 1)
add_change(query->stats, props->size, props->expanded_size,
props->revision, noderev->created_path, props->kind,
!noderev->predecessor_id);
if ( noderev->kind == svn_node_dir && text && text->ref_count == 1
&& !svn_fs_fs__use_log_addressing(query->fs))
SVN_ERR(parse_dir(query, noderev, revision_info, result_pool,
scratch_pool));
if (noderev->kind == svn_node_dir)
{
revision_info->dir_noderev_size += noderev_str->len;
revision_info->dir_noderev_count++;
}
else
{
revision_info->file_noderev_size += noderev_str->len;
revision_info->file_noderev_count++;
}
return SVN_NO_ERROR;
}
static svn_error_t *
get_phys_change_count(query_t *query,
revision_info_t *revision_info,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_fs_fs__changes_context_t *context;
SVN_ERR(svn_fs_fs__create_changes_context(&context, query->fs,
revision_info->revision,
scratch_pool));
revision_info->change_count = 0;
while (!context->eol)
{
apr_array_header_t *changes;
svn_pool_clear(iterpool);
SVN_ERR(svn_fs_fs__get_changes(&changes, context, iterpool, iterpool));
revision_info->change_count = changes->nelts;
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
read_phys_revision(query_t *query,
revision_info_t *info,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
char buf[64];
apr_off_t root_node_offset;
apr_off_t changes_offset;
svn_stringbuf_t *trailer;
svn_stringbuf_t *noderev_str;
apr_off_t start = MAX(info->offset, info->end - sizeof(buf));
apr_size_t len = (apr_size_t)(info->end - start);
SVN_ERR(svn_io_file_seek(info->rev_file->file, APR_SET, &start,
scratch_pool));
SVN_ERR(svn_io_file_read_full2(info->rev_file->file, buf, len, NULL, NULL,
scratch_pool));
trailer = svn_stringbuf_ncreate(buf, len, scratch_pool);
SVN_ERR(svn_fs_fs__parse_revision_trailer(&root_node_offset,
&changes_offset, trailer,
info->revision));
SVN_ERR(get_phys_change_count(query, info, scratch_pool));
trailer = svn_fs_fs__unparse_revision_trailer(root_node_offset,
changes_offset,
scratch_pool);
info->changes_len = info->end - info->offset - changes_offset
- trailer->len;
SVN_ERR(read_phsy_noderev(&noderev_str, query, root_node_offset, info,
scratch_pool, scratch_pool));
SVN_ERR(read_noderev(query, noderev_str, info, result_pool, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
read_phys_pack_file(query_t *query,
svn_revnum_t base,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
svn_filesize_t file_size = 0;
svn_fs_fs__revision_file_t *rev_file;
SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, base,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_file_size_get(&file_size, rev_file->file, scratch_pool));
for (i = 0; i < query->shard_size; ++i)
{
revision_info_t *info;
if (query->cancel_func)
SVN_ERR(query->cancel_func(query->cancel_baton));
info = apr_pcalloc(result_pool, sizeof(*info));
info->representations = apr_array_make(result_pool, 4,
sizeof(rep_stats_t*));
info->rev_file = rev_file;
info->revision = base + i;
SVN_ERR(svn_fs_fs__get_packed_offset(&info->offset, query->fs, base + i,
iterpool));
if (i + 1 == query->shard_size)
info->end = file_size;
else
SVN_ERR(svn_fs_fs__get_packed_offset(&info->end, query->fs,
base + i + 1, iterpool));
SVN_ERR(read_phys_revision(query, info, result_pool, iterpool));
info->representations = apr_array_copy(result_pool,
info->representations);
info->rev_file = NULL;
APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info;
svn_pool_clear(iterpool);
}
SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
if (query->progress_func)
query->progress_func(base, query->progress_baton, scratch_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
read_phys_revision_file(query_t *query,
svn_revnum_t revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
revision_info_t *info = apr_pcalloc(result_pool, sizeof(*info));
svn_filesize_t file_size = 0;
svn_fs_fs__revision_file_t *rev_file;
if (query->cancel_func)
SVN_ERR(query->cancel_func(query->cancel_baton));
SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, revision,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_file_size_get(&file_size, rev_file->file, scratch_pool));
info->representations = apr_array_make(result_pool, 4, sizeof(rep_stats_t*));
info->rev_file = rev_file;
info->revision = revision;
info->offset = 0;
info->end = file_size;
SVN_ERR(read_phys_revision(query, info, result_pool, scratch_pool));
SVN_ERR(svn_fs_fs__close_revision_file(rev_file));
info->rev_file = NULL;
APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info;
if (query->progress_func)
{
if (query->shard_size && (revision % query->shard_size == 0))
query->progress_func(revision, query->progress_baton, scratch_pool);
if (!query->shard_size && (revision % 1000 == 0))
query->progress_func(revision, query->progress_baton, scratch_pool);
}
return SVN_NO_ERROR;
}
static apr_uint64_t
get_log_change_count(const char *changes,
apr_size_t len)
{
apr_size_t lines = 0;
const char *end = changes + len;
for (; changes < end; ++changes)
if (*changes == '\n')
++lines;
return lines / 2;
}
static svn_error_t *
read_item(svn_stringbuf_t **contents,
svn_fs_fs__revision_file_t *rev_file,
svn_fs_fs__p2l_entry_t *entry,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stringbuf_t *item = svn_stringbuf_create_ensure(entry->size,
result_pool);
item->len = entry->size;
item->data[item->len] = 0;
SVN_ERR(svn_io_file_aligned_seek(rev_file->file, rev_file->block_size,
NULL, entry->offset, scratch_pool));
SVN_ERR(svn_io_file_read_full2(rev_file->file, item->data, item->len,
NULL, NULL, scratch_pool));
*contents = item;
return SVN_NO_ERROR;
}
static int
compare_representation_refs(const void *lhs, const void *rhs)
{
svn_revnum_t lhs_rev = (*(const rep_ref_t *const *)lhs)->revision;
svn_revnum_t rhs_rev = (*(const rep_ref_t *const *)rhs)->revision;
if (lhs_rev < rhs_rev)
return -1;
return (lhs_rev > rhs_rev ? 1 : 0);
}
static svn_error_t *
resolve_representation_refs(query_t *query,
apr_array_header_t *rep_refs)
{
int i;
svn_sort__array(rep_refs, compare_representation_refs);
for (i = 0; i < rep_refs->nelts; ++i)
{
int idx;
rep_ref_t *ref = APR_ARRAY_IDX(rep_refs, i, rep_ref_t *);
rep_stats_t *rep = find_representation(&idx, query, NULL,
ref->revision, ref->item_index);
SVN_ERR_ASSERT(rep);
SVN_ERR_ASSERT(!rep->chain_length);
rep->header_size = ref->header_size;
if (ref->base_revision == SVN_INVALID_REVNUM)
{
rep->chain_length = 1;
}
else
{
rep_stats_t *base;
base = find_representation(&idx, query, NULL, ref->base_revision,
ref->base_item_index);
SVN_ERR_ASSERT(base);
SVN_ERR_ASSERT(base->chain_length);
rep->chain_length = 1 + MIN(base->chain_length, (apr_byte_t)0xfe);
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
read_log_rev_or_packfile(query_t *query,
svn_revnum_t base,
int count,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
fs_fs_data_t *ffd = query->fs->fsap_data;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
apr_off_t max_offset;
apr_off_t offset = 0;
int i;
svn_fs_fs__revision_file_t *rev_file;
apr_array_header_t *rep_refs = apr_array_make(scratch_pool, 64,
sizeof(rep_ref_t *));
for (i = 0; i < count; ++i)
{
revision_info_t *info = apr_pcalloc(result_pool, sizeof(*info));
info->representations = apr_array_make(result_pool, 4,
sizeof(rep_stats_t*));
info->revision = base + i;
APR_ARRAY_PUSH(query->revisions, revision_info_t*) = info;
}
SVN_ERR(svn_fs_fs__open_pack_or_rev_file(&rev_file, query->fs, base,
scratch_pool, iterpool));
SVN_ERR(svn_fs_fs__p2l_get_max_offset(&max_offset, query->fs, rev_file,
base, scratch_pool));
APR_ARRAY_IDX(query->revisions, base, revision_info_t*)->end = max_offset;
for (offset = 0; offset < max_offset; )
{
apr_array_header_t *entries;
svn_pool_clear(iterpool);
if (query->cancel_func)
SVN_ERR(query->cancel_func(query->cancel_baton));
SVN_ERR(svn_fs_fs__p2l_index_lookup(&entries, query->fs, rev_file, base,
offset, ffd->p2l_page_size,
iterpool, iterpool));
for (i = 0; i < entries->nelts; ++i)
{
svn_stringbuf_t *item;
revision_info_t *info;
svn_fs_fs__p2l_entry_t *entry
= &APR_ARRAY_IDX(entries, i, svn_fs_fs__p2l_entry_t);
if (i == 0 && entry->offset < offset)
continue;
if (entry->size == 0)
continue;
info = APR_ARRAY_IDX(query->revisions, entry->item.revision,
revision_info_t*);
if (entry->type == SVN_FS_FS__ITEM_TYPE_NODEREV)
{
SVN_ERR(read_item(&item, rev_file, entry, iterpool, iterpool));
SVN_ERR(read_noderev(query, item, info, result_pool, iterpool));
}
else if (entry->type == SVN_FS_FS__ITEM_TYPE_CHANGES)
{
SVN_ERR(read_item(&item, rev_file, entry, iterpool, iterpool));
info->change_count
= get_log_change_count(item->data + 0, item->len);
info->changes_len += entry->size;
}
else if ( (entry->type == SVN_FS_FS__ITEM_TYPE_FILE_REP)
|| (entry->type == SVN_FS_FS__ITEM_TYPE_DIR_REP)
|| (entry->type == SVN_FS_FS__ITEM_TYPE_FILE_PROPS)
|| (entry->type == SVN_FS_FS__ITEM_TYPE_DIR_PROPS))
{
svn_fs_fs__rep_header_t *header;
rep_ref_t *ref = apr_pcalloc(scratch_pool, sizeof(*ref));
SVN_ERR(svn_io_file_aligned_seek(rev_file->file,
rev_file->block_size,
NULL, entry->offset,
iterpool));
SVN_ERR(svn_fs_fs__read_rep_header(&header,
rev_file->stream,
iterpool, iterpool));
ref->header_size = header->header_size;
ref->revision = entry->item.revision;
ref->item_index = entry->item.number;
if (header->type == svn_fs_fs__rep_delta)
{
ref->base_item_index = header->base_item_index;
ref->base_revision = header->base_revision;
}
else
{
ref->base_item_index = SVN_FS_FS__ITEM_INDEX_UNUSED;
ref->base_revision = SVN_INVALID_REVNUM;
}
APR_ARRAY_PUSH(rep_refs, rep_ref_t *) = ref;
}
offset += entry->size;
}
}
SVN_ERR(resolve_representation_refs(query, rep_refs));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static svn_error_t *
read_log_pack_file(query_t *query,
svn_revnum_t base,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR(read_log_rev_or_packfile(query, base, query->shard_size,
result_pool, scratch_pool));
if (query->progress_func)
query->progress_func(base, query->progress_baton, scratch_pool);
return SVN_NO_ERROR;
}
static svn_error_t *
read_log_revision_file(query_t *query,
svn_revnum_t revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
SVN_ERR(read_log_rev_or_packfile(query, revision, 1,
result_pool, scratch_pool));
if (query->progress_func)
{
if (query->shard_size && (revision % query->shard_size == 0))
query->progress_func(revision, query->progress_baton, scratch_pool);
if (!query->shard_size && (revision % 1000 == 0))
query->progress_func(revision, query->progress_baton, scratch_pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
read_revisions(query_t *query,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_revnum_t revision;
for ( revision = 0
; revision < query->min_unpacked_rev
; revision += query->shard_size)
{
svn_pool_clear(iterpool);
if (svn_fs_fs__use_log_addressing(query->fs))
SVN_ERR(read_log_pack_file(query, revision, result_pool, iterpool));
else
SVN_ERR(read_phys_pack_file(query, revision, result_pool, iterpool));
}
for ( ; revision <= query->head; ++revision)
{
svn_pool_clear(iterpool);
if (svn_fs_fs__use_log_addressing(query->fs))
SVN_ERR(read_log_revision_file(query, revision, result_pool,
iterpool));
else
SVN_ERR(read_phys_revision_file(query, revision, result_pool,
iterpool));
}
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static void
add_rep_pack_stats(svn_fs_fs__rep_pack_stats_t *stats,
rep_stats_t *rep)
{
stats->count++;
stats->packed_size += rep->size;
stats->expanded_size += rep->expanded_size;
stats->overhead_size += rep->header_size + 7 ;
}
static void
add_rep_stats(svn_fs_fs__representation_stats_t *stats,
rep_stats_t *rep)
{
add_rep_pack_stats(&stats->total, rep);
if (rep->ref_count == 1)
add_rep_pack_stats(&stats->uniques, rep);
else
add_rep_pack_stats(&stats->shared, rep);
stats->references += rep->ref_count;
stats->expanded_size += rep->ref_count * rep->expanded_size;
stats->chain_len += rep->chain_length;
}
static void
aggregate_stats(const apr_array_header_t *revisions,
svn_fs_fs__stats_t *stats)
{
int i, k;
stats->revision_count = revisions->nelts;
for (i = 0; i < revisions->nelts; ++i)
{
revision_info_t *revision = APR_ARRAY_IDX(revisions, i,
revision_info_t *);
stats->change_count += revision->change_count;
stats->change_len += revision->changes_len;
stats->total_size += revision->end - revision->offset;
stats->dir_node_stats.count += revision->dir_noderev_count;
stats->dir_node_stats.size += revision->dir_noderev_size;
stats->file_node_stats.count += revision->file_noderev_count;
stats->file_node_stats.size += revision->file_noderev_size;
stats->total_node_stats.count += revision->dir_noderev_count
+ revision->file_noderev_count;
stats->total_node_stats.size += revision->dir_noderev_size
+ revision->file_noderev_size;
for (k = 0; k < revision->representations->nelts; ++k)
{
rep_stats_t *rep = APR_ARRAY_IDX(revision->representations, k,
rep_stats_t *);
switch(rep->kind)
{
case file_rep:
add_rep_stats(&stats->file_rep_stats, rep);
break;
case dir_rep:
add_rep_stats(&stats->dir_rep_stats, rep);
break;
case file_property_rep:
add_rep_stats(&stats->file_prop_rep_stats, rep);
break;
case dir_property_rep:
add_rep_stats(&stats->dir_prop_rep_stats, rep);
break;
default:
break;
}
add_rep_stats(&stats->total_rep_stats, rep);
}
}
}
static svn_fs_fs__stats_t *
create_stats(apr_pool_t *result_pool)
{
svn_fs_fs__stats_t *stats = apr_pcalloc(result_pool, sizeof(*stats));
initialize_largest_changes(stats, 64, result_pool);
stats->by_extension = apr_hash_make(result_pool);
return stats;
}
static svn_error_t *
create_query(query_t **query,
svn_fs_t *fs,
svn_fs_fs__stats_t *stats,
svn_fs_progress_notify_func_t progress_func,
void *progress_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
*query = apr_pcalloc(result_pool, sizeof(**query));
(*query)->shard_size = svn_fs_fs__shard_size(fs);
SVN_ERR(svn_fs_fs__youngest_rev(&(*query)->head, fs, scratch_pool));
SVN_ERR(svn_fs_fs__min_unpacked_rev(&(*query)->min_unpacked_rev, fs,
scratch_pool));
(*query)->revisions = apr_array_make(result_pool, (int) (*query)->head + 1,
sizeof(revision_info_t *));
(*query)->null_base = apr_pcalloc(result_pool,
sizeof(*(*query)->null_base));
(*query)->fs = fs;
(*query)->stats = stats;
(*query)->progress_func = progress_func;
(*query)->progress_baton = progress_baton;
(*query)->cancel_func = cancel_func;
(*query)->cancel_baton = cancel_baton;
return SVN_NO_ERROR;
}
svn_error_t *
svn_fs_fs__get_stats(svn_fs_fs__stats_t **stats,
svn_fs_t *fs,
svn_fs_progress_notify_func_t progress_func,
void *progress_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
query_t *query;
*stats = create_stats(result_pool);
SVN_ERR(create_query(&query, fs, *stats, progress_func, progress_baton,
cancel_func, cancel_baton, scratch_pool,
scratch_pool));
SVN_ERR(read_revisions(query, scratch_pool, scratch_pool));
aggregate_stats(query->revisions, *stats);
return SVN_NO_ERROR;
}