Optimize searches for directory structures by keeping a pointer to struct directory in struct name.

* src/common.h (struct name): New member `directory' replaces
dir_contents. Rearrange members.
(rebase_directory): Change signature.
(scan_directory): Change signature.
(name_fill_directory)
(directory_contents, safe_directory_contents): New prototypes.
(append_incremental_renames): Change signature.
(replace_prefix): New proto.
* src/compare.c (diff_dumpdir): Use directory_contents + scan_directory.
* src/create.c
* src/incremen.c (replace_prefix): Move to misc.c
(rebase_directory): Rewrite.
(scan_directory): Return pointer to struct directory.
(directory_contents, safe_directory_contents): New functions.
(get_directory_contents): Remove.
(name_fill_directory): New function.
(append_incremental_renames): Rewrite. This also fixes a memory leak.
* src/names.c (name_gather, addname): Reflect changes in struct name.
(add_hierarchy_to_namelist): Rewrite using name_fill_directory and
directory_contents.
(rebase_child_list): Update call to rebase_directory.
(collect_and_sort_names): Optimize

* src/misc.c (replace_prefix): New function.
* src/names.c (add_hierarchy_to_namelist): Use new get_directory_contents.

* tests/incr05.at: New test case.
* tests/incr06.at: New test case.
* tests/Makefile.am, test/testsuite.at: Add incr05.at and incr06.at.

* doc/Makefile.am (check-options): Improve rule.
* doc/tar.texi, NEWS: Update.
This commit is contained in:
Sergey Poznyakoff
2009-08-08 13:16:53 +03:00
parent df59690240
commit 2b1bffbad6
13 changed files with 353 additions and 148 deletions

View File

@@ -320,26 +320,32 @@ GLOBAL const char **archive_name_cursor;
/* Output index file name. */
GLOBAL char const *index_file_name;
/* Opaque structure for keeping directory meta-data */
struct directory;
/* Structure for keeping track of filenames and lists thereof. */
struct name
{
struct name *next; /* Link to the next element */
struct name *prev; /* Link to the previous element */
char *name; /* File name or globbing pattern */
size_t length; /* cached strlen (name) */
int matching_flags; /* wildcard flags if name is a pattern */
int change_dir; /* Number of the directory to change to.
Set with the -C option. */
uintmax_t found_count; /* number of times a matching file has
been found */
int matching_flags; /* this name is a regexp, not literal */
size_t length; /* cached strlen(name) */
char *name;
/* The following members are used for incremental dumps only */
char const *dir_contents; /* directory contents */
/* The following members are used for incremental dumps only,
if this struct name represents a directory;
see incremen.c */
struct directory *directory;/* directory meta-data and contents */
struct name *parent; /* pointer to the parent hierarchy */
struct name *child; /* pointer to the first child */
struct name *sibling; /* pointer to the next sibling */
char *caname; /* canonical name */
char *caname; /* canonical name */
};
/* Obnoxious test to see if dimwit is trying to dump the archive. */
@@ -505,16 +511,21 @@ char *dumpdir_locate (dumpdir_t dump, const char *name);
char *dumpdir_next (dumpdir_iter_t itr);
char *dumpdir_first (dumpdir_t dump, int all, dumpdir_iter_t *pitr);
const char *scan_directory (char *dir_name, dev_t device, bool cmdline);
const char *append_incremental_renames (const char *dump);
struct directory *scan_directory (char *dir, dev_t device, bool cmdline);
void name_fill_directory (struct name *name, dev_t device, bool cmdline);
const char *directory_contents (struct directory *dir);
const char *safe_directory_contents (struct directory *dir);
void rebase_directory (struct directory *dir,
const char *samp, size_t slen,
const char *repl, size_t rlen);
void append_incremental_renames (struct directory *dir);
void read_directory_file (void);
void write_directory_file (void);
void purge_directory (char const *directory_name);
void list_dumpdir (char *buffer, size_t size);
void update_parent_directory (const char *name);
void rebase_directory (const char *name, size_t old_prefix_len,
const char *old_prefix,
const char *new_prefix);
size_t dumpdir_size (const char *p);
bool is_dumpdir (struct tar_stat_info *stat_info);
@@ -558,7 +569,7 @@ off_t off_from_header (const char *buf, size_t size);
size_t size_from_header (const char *buf, size_t size);
time_t time_from_header (const char *buf, size_t size);
uid_t uid_from_header (const char *buf, size_t size);
uintmax_t uintmax_from_header (const char * buf, size_t size);
uintmax_t uintmax_from_header (const char *buf, size_t size);
void list_archive (void);
void print_for_mkdir (char *dirname, int length, mode_t mode);
@@ -578,6 +589,8 @@ char *quote_copy_string (const char *str);
int unquote_string (char *str);
char *zap_slashes (char *name);
char *normalize_filename (const char *name);
void replace_prefix (char **pname, const char *samp, size_t slen,
const char *repl, size_t rlen);
void code_ns_fraction (int ns, char *p);
char const *code_timespec (struct timespec ts, char *sbuf);

View File

@@ -380,7 +380,8 @@ diff_dumpdir (void)
else
dev = stat_data.st_dev;
dumpdir_buffer = scan_directory (current_stat_info.file_name, dev, false);
dumpdir_buffer = directory_contents
(scan_directory (current_stat_info.file_name, dev, false));
if (dumpdir_buffer)
{

View File

@@ -1116,11 +1116,12 @@ dump_dir0 (char *directory,
if (!incremental_option)
finish_header (st, blk, block_ordinal);
else if (gnu_list_name->dir_contents)
else if (gnu_list_name->directory)
{
if (archive_format == POSIX_FORMAT)
{
xheader_store ("GNU.dumpdir", st, gnu_list_name->dir_contents);
xheader_store ("GNU.dumpdir", st,
safe_directory_contents (gnu_list_name->directory));
finish_header (st, blk, block_ordinal);
}
else
@@ -1132,11 +1133,8 @@ dump_dir0 (char *directory,
const char *buffer, *p_buffer;
block_ordinal = current_block_ordinal ();
buffer = gnu_list_name->dir_contents;
if (buffer)
totsize = dumpdir_size (buffer);
else
totsize = 0;
buffer = safe_directory_contents (gnu_list_name->directory);
totsize = dumpdir_size (buffer);
OFF_TO_CHARS (totsize, blk->header.size);
finish_header (st, blk, block_ordinal);
p_buffer = buffer;
@@ -1304,7 +1302,7 @@ create_archive (void)
memcpy (buffer, p, plen);
if (! ISSLASH (buffer[plen - 1]))
buffer[plen++] = DIRECTORY_SEPARATOR;
q = gnu_list_name->dir_contents;
q = directory_contents (gnu_list_name->directory);
if (q)
while (*q)
{

View File

@@ -290,24 +290,6 @@ attach_directory (const char *name)
}
static void
replace_prefix (char **pname, const char *samp, size_t slen,
const char *repl, size_t rlen)
{
char *name = *pname;
size_t nlen = strlen (name);
if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
{
if (rlen > slen)
{
name = xrealloc (name, nlen - slen + rlen + 1);
*pname = name;
}
memmove (name + rlen, name + slen, nlen - slen + 1);
memcpy (name, repl, rlen);
}
}
void
dirlist_replace_prefix (const char *pref, const char *repl)
{
@@ -389,18 +371,15 @@ remove_directory (const char *caname)
}
#endif
/* Find a directory entry for NAME. If first OLD_PREFIX_LEN
bytes of its name match OLD_PREFIX, replace them with
NEW_PREFIX. */
/* If first OLD_PREFIX_LEN bytes of DIR->NAME name match OLD_PREFIX,
replace them with NEW_PREFIX. */
void
rebase_directory (const char *name, size_t old_prefix_len,
const char *old_prefix,
const char *new_prefix)
rebase_directory (struct directory *dir,
const char *old_prefix, size_t old_prefix_len,
const char *new_prefix, size_t new_prefix_len)
{
struct directory *dir = find_directory (name);
if (dir)
replace_prefix (&dir->name, old_prefix, old_prefix_len,
new_prefix, strlen (new_prefix));
replace_prefix (&dir->name, old_prefix, old_prefix_len,
new_prefix, new_prefix_len);
}
/* Return a directory entry for a given combination of device and inode
@@ -696,8 +675,10 @@ makedumpdir (struct directory *directory, const char *dir)
/* Recursively scan the given directory DIR.
DEVICE is the device number where DIR resides (for --one-file-system).
If CMDLINE is true, the directory name was explicitly listed in the
command line. */
const char *
command line.
Unless *PDIR is NULL, store there a pointer to the struct directory
describing DIR. */
struct directory *
scan_directory (char *dir, dev_t device, bool cmdline)
{
char *dirp = savedir (dir); /* for scanning directory */
@@ -730,7 +711,7 @@ scan_directory (char *dir, dev_t device, bool cmdline)
directory = procdir (name_buffer, &stat_data, device,
(cmdline ? PD_FORCE_INIT : 0),
&ch);
name_length = strlen (name_buffer);
if (! ISSLASH (name_buffer[name_length - 1]))
{
@@ -808,13 +789,30 @@ scan_directory (char *dir, dev_t device, bool cmdline)
if (dirp)
free (dirp);
return directory->dump ? directory->dump->contents : NULL;
return directory;
}
/* Return pointer to the contents of the directory DIR */
const char *
get_directory_contents (char *dir, dev_t device, bool force)
directory_contents (struct directory *dir)
{
return scan_directory (dir, device, force);
if (!dir)
return NULL;
return dir->dump ? dir->dump->contents : NULL;
}
/* A "safe" version of directory_contents, which never returns NULL. */
const char *
safe_directory_contents (struct directory *dir)
{
const char *ret = directory_contents (dir);
return ret ? ret : "\0\0\0\0";
}
void
name_fill_directory (struct name *name, dev_t device, bool cmdline)
{
name->directory = scan_directory (name->name, device, cmdline);
}
@@ -877,17 +875,19 @@ store_rename (struct directory *dir, struct obstack *stk)
}
}
const char *
append_incremental_renames (const char *dump)
void
append_incremental_renames (struct directory *dir)
{
struct obstack stk;
size_t size;
struct directory *dp;
const char *dump;
if (dirhead == NULL)
return dump;
return;
obstack_init (&stk);
dump = directory_contents (dir);
if (dump)
{
size = dumpdir_size (dump) - 1;
@@ -902,11 +902,10 @@ append_incremental_renames (const char *dump)
if (obstack_object_size (&stk) != size)
{
obstack_1grow (&stk, 0);
dump = obstack_finish (&stk);
dumpdir_free (dir->dump);
dir->dump = dumpdir_create (obstack_finish (&stk));
}
else
obstack_free (&stk, NULL);
return dump;
obstack_free (&stk, NULL);
}

View File

@@ -236,6 +236,25 @@ normalize_filename (const char *name)
return zap_slashes (canonicalize_filename_mode (name, CAN_MISSING));
}
void
replace_prefix (char **pname, const char *samp, size_t slen,
const char *repl, size_t rlen)
{
char *name = *pname;
size_t nlen = strlen (name);
if (nlen > slen && memcmp (name, samp, slen) == 0 && ISSLASH (name[slen]))
{
if (rlen > slen)
{
name = xrealloc (name, nlen - slen + rlen + 1);
*pname = name;
}
memmove (name + rlen, name + slen, nlen - slen + 1);
memcpy (name, repl, rlen);
}
}
/* Handling numbers. */
@@ -777,3 +796,4 @@ page_aligned_alloc (void **ptr, size_t size)
*ptr = xmalloc (size1);
return ptr_align (*ptr, alignment);
}

View File

@@ -418,7 +418,7 @@ name_gather (void)
buffer->next = 0;
buffer->found_count = 0;
buffer->matching_flags = matching_flags;
buffer->dir_contents = NULL;
buffer->directory = NULL;
buffer->parent = NULL;
namelist = buffer;
@@ -461,7 +461,7 @@ addname (char const *string, int change_dir, struct name *parent)
name->found_count = 0;
name->matching_flags = matching_flags;
name->change_dir = change_dir;
name->dir_contents = NULL;
name->directory = NULL;
name->parent = parent;
*nametail = name;
@@ -767,12 +767,11 @@ compare_names (struct name const *n1, struct name const *n2)
static void
add_hierarchy_to_namelist (struct name *name, dev_t device, bool cmdline)
{
char *file_name = name->name;
const char *buffer = scan_directory (file_name, device, cmdline);
const char *buffer;
if (! buffer)
name->dir_contents = "\0\0\0\0";
else
name_fill_directory (name, device, cmdline);
buffer = directory_contents (name->directory);
if (buffer)
{
struct name *child_head = NULL, *child_tail = NULL;
size_t name_length = name->length;
@@ -785,8 +784,7 @@ add_hierarchy_to_namelist (struct name *name, dev_t device, bool cmdline)
size_t string_length;
int change_dir = name->change_dir;
name->dir_contents = buffer;
strcpy (namebuf, file_name);
strcpy (namebuf, name->name);
if (! ISSLASH (namebuf[name_length - 1]))
{
namebuf[name_length++] = '/';
@@ -866,8 +864,9 @@ rebase_child_list (struct name *child, struct name *parent)
child->name = newp;
child->length = size;
rebase_directory (child->name, old_prefix_len, child->parent->name,
new_prefix);
rebase_directory (child->directory,
child->parent->name, old_prefix_len,
new_prefix, new_prefix_len);
}
}
@@ -915,7 +914,7 @@ collect_and_sort_names (void)
num_names = 0;
for (name = namelist; name; name = name->next, num_names++)
{
if (name->found_count || name->dir_contents)
if (name->found_count || name->directory)
continue;
if (name->matching_flags & EXCLUDE_WILDCARDS)
/* NOTE: EXCLUDE_ANCHORED is not relevant here */
@@ -990,7 +989,7 @@ collect_and_sort_names (void)
for (name = namelist; name && name->name[0] == 0; name++)
;
if (name)
name->dir_contents = append_incremental_renames (name->dir_contents);
append_incremental_renames (name->directory);
}
}