[gvfs] [FTP] rework directory listing
- From: Benjamin Otte <otte src gnome org>
- To: svn-commits-list gnome org
- Subject: [gvfs] [FTP] rework directory listing
- Date: Tue, 26 May 2009 06:16:16 -0400 (EDT)
commit c8e37497c132722a6d99a8c9511a90e90bcf3da9
Author: Benjamin Otte <otte gnome org>
Date: Tue May 26 12:09:27 2009 +0200
[FTP] rework directory listing
This is in preparation to both making directory listing caches simpler
and to prepare for a gnio switch.
The code gets rid of reading the directory listing line by line and
instead reads it as one big chunk and splits it into lines upon parsing.
---
daemon/gvfsbackendftp.c | 179 ++++++++++++++++++++++++-----------------------
1 files changed, 91 insertions(+), 88 deletions(-)
diff --git a/daemon/gvfsbackendftp.c b/daemon/gvfsbackendftp.c
index 0af9287..bb04134 100644
--- a/daemon/gvfsbackendftp.c
+++ b/daemon/gvfsbackendftp.c
@@ -79,6 +79,13 @@
typedef unsigned char FtpFile;
typedef struct _FtpConnection FtpConnection;
+typedef struct _FtpDirEntry FtpDirEntry;
+struct _FtpDirEntry {
+ gsize size;
+ gsize length;
+ gchar data[1];
+};
+
typedef struct FtpDirReader FtpDirReader;
struct FtpDirReader {
void (* init_data) (FtpConnection *conn,
@@ -1424,10 +1431,31 @@ g_vfs_backend_ftp_finalize (GObject *object)
}
static void
-list_free (gpointer list)
+ftp_dir_entry_free (gpointer entry)
+{
+ g_free (entry);
+}
+
+static FtpDirEntry *
+ftp_dir_entry_grow (FtpDirEntry *entry)
+{
+ entry = g_try_realloc (entry, sizeof (FtpDirEntry) + entry->size + 4096);
+ if (entry == NULL)
+ return NULL;
+ entry->size += 4096;
+ return entry;
+}
+
+static FtpDirEntry *
+ftp_dir_entry_new (void)
{
- g_list_foreach (list, (GFunc) g_free, NULL);
- g_list_free (list);
+ FtpDirEntry *entry;
+
+ entry = g_malloc (4096);
+ entry->size = 4096 - sizeof (FtpDirEntry);
+ entry->length = 0;
+
+ return entry;
}
static void
@@ -1439,7 +1467,7 @@ g_vfs_backend_ftp_init (GVfsBackendFtp *ftp)
ftp->directory_cache = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
- list_free);
+ ftp_dir_entry_free);
g_static_rw_lock_init (&ftp->directory_cache_lock);
ftp->dir_ops = &dir_default;
@@ -2026,46 +2054,38 @@ do_write (GVfsBackend *backend,
ftp_connection_pop_job (conn);
}
-static GList *
+static FtpDirEntry *
do_enumerate_directory (FtpConnection *conn)
{
- gsize size, n_bytes, bytes_read;
+ gsize n_bytes;
SoupSocketIOStatus status;
- gboolean got_boundary;
- char *name;
- GList *list = NULL;
+ FtpDirEntry *entry;
if (ftp_connection_in_error (conn))
return NULL;
- size = 128;
- bytes_read = 0;
- name = g_malloc (size);
+ entry = ftp_dir_entry_new ();
do
{
- if (bytes_read + 3 >= size)
- {
- if (size >= 16384)
+ if (entry->size - entry->length < 128)
+ {
+ entry = ftp_dir_entry_grow (entry);
+ if (entry == NULL)
{
- g_set_error_literal (&conn->error, G_IO_ERROR, G_IO_ERROR_FILENAME_TOO_LONG,
- _("filename too long"));
- break;
+ g_set_error_literal (&conn->error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Out of memory while reading directory contents"));
+ return NULL;
}
- size += 128;
- name = g_realloc (name, size);
}
- status = soup_socket_read_until (conn->data,
- name + bytes_read,
- size - bytes_read - 1,
- "\n",
- 1,
- &n_bytes,
- &got_boundary,
- conn->job->cancellable,
- &conn->error);
-
- bytes_read += n_bytes;
+ status = soup_socket_read (conn->data,
+ entry->data + entry->length,
+ entry->size - entry->length - 1,
+ &n_bytes,
+ conn->job->cancellable,
+ &conn->error);
+
+ entry->length += n_bytes;
switch (status)
{
case SOUP_SOCKET_EOF:
@@ -2075,18 +2095,10 @@ do_enumerate_directory (FtpConnection *conn)
status = SOUP_SOCKET_EOF;
break;
}
- if (got_boundary)
- {
- name[bytes_read - 1] = 0;
- if (bytes_read >= 2 && name[bytes_read - 2] == '\r')
- name[bytes_read - 2] = 0;
- DEBUG ("--- %s\n", name);
- list = g_list_prepend (list, g_strdup (name));
- bytes_read = 0;
- }
break;
case SOUP_SOCKET_ERROR:
- goto error2;
+ ftp_dir_entry_free (entry);
+ return NULL;
case SOUP_SOCKET_WOULD_BLOCK:
default:
g_assert_not_reached ();
@@ -2095,90 +2107,78 @@ do_enumerate_directory (FtpConnection *conn)
}
while (status == SOUP_SOCKET_OK);
- if (bytes_read)
- {
- name[bytes_read] = 0;
- DEBUG ("--- %s\n", name);
- list = g_list_prepend (list, name);
- }
- else
- g_free (name);
-
ftp_connection_close_data_connection (conn);
ftp_connection_receive (conn, 0);
if (ftp_connection_in_error (conn))
- goto error;
-
- return g_list_reverse (list);
+ {
+ ftp_dir_entry_free (entry);
+ return NULL;
+ }
+ /* null-terminate, just because */
+ entry->data[entry->length] = 0;
-error2:
- ftp_connection_close_data_connection (conn);
- ftp_connection_receive (conn, 0);
-error:
- g_list_foreach (list, (GFunc) g_free, NULL);
- g_list_free (list);
- return NULL;
+ return entry;
}
/* IMPORTANT: SUCK ALARM!
* locks ftp->directory_cache_lock but only iff it returns !NULL */
-static const GList *
+static const FtpDirEntry *
enumerate_directory (GVfsBackendFtp *ftp,
FtpConnection * conn,
const FtpFile * dir,
gboolean use_cache)
{
- GList *files;
+ FtpDirEntry *entry;
g_static_rw_lock_reader_lock (&ftp->directory_cache_lock);
do {
if (use_cache)
- files = g_hash_table_lookup (ftp->directory_cache, dir);
+ entry = g_hash_table_lookup (ftp->directory_cache, dir);
else
{
use_cache = TRUE;
- files = NULL;
+ entry = NULL;
}
- if (files == NULL)
+ if (entry == NULL)
{
g_static_rw_lock_reader_unlock (&ftp->directory_cache_lock);
ftp->dir_ops->init_data (conn, dir);
- files = do_enumerate_directory (conn);
- if (files == NULL)
- {
- return NULL;
- }
+ entry = do_enumerate_directory (conn);
+ if (entry == NULL)
+ return NULL;
g_static_rw_lock_writer_lock (&ftp->directory_cache_lock);
- g_hash_table_insert (ftp->directory_cache, g_strdup ((const char *) dir), files);
+ g_hash_table_insert (ftp->directory_cache, g_strdup ((const char *) dir), entry);
g_static_rw_lock_writer_unlock (&ftp->directory_cache_lock);
- files = NULL;
+ entry = NULL;
g_static_rw_lock_reader_lock (&ftp->directory_cache_lock);
}
- } while (files == NULL);
+ } while (entry == NULL);
- return files;
+ return entry;
}
static GFileInfo *
create_file_info_from_parent (GVfsBackendFtp *ftp, FtpConnection *conn,
const FtpFile *dir, const FtpFile *file, char **symlink)
{
- const GList *walk, *files;
GFileInfo *info = NULL;
gpointer iter;
+ const FtpDirEntry *entry;
+ const char *sol, *eol;
- files = enumerate_directory (ftp, conn, dir, TRUE);
- if (files == NULL)
+ entry = enumerate_directory (ftp, conn, dir, TRUE);
+ if (entry == NULL)
return NULL;
iter = ftp->dir_ops->iter_new (conn);
- for (walk = files; walk; walk = walk->next)
+ for (sol = eol = entry->data; eol; sol = eol + 1)
{
+ eol = memchr (sol, '\n', entry->length - (sol - entry->data));
info = ftp->dir_ops->iter_process (iter,
conn,
dir,
file,
- walk->data,
+ sol,
symlink);
if (info)
break;
@@ -2397,13 +2397,14 @@ do_enumerate (GVfsBackend *backend,
{
GVfsBackendFtp *ftp = G_VFS_BACKEND_FTP (backend);
FtpConnection *conn;
- const GList *walk, *files;
FtpFile *dir;
gpointer iter;
GSList *symlink_targets = NULL;
GSList *symlink_fileinfos = NULL;
GSList *twalk, *fwalk;
GFileInfo *info;
+ const FtpDirEntry *entry;
+ const char *sol, *eol;
conn = g_vfs_backend_ftp_pop_connection (ftp, G_VFS_JOB (job));
if (conn == NULL)
@@ -2414,21 +2415,23 @@ do_enumerate (GVfsBackend *backend,
*/
dir = ftp_filename_from_gvfs_path (conn, dirname);
- files = enumerate_directory (ftp, conn, dir, FALSE);
+ entry = enumerate_directory (ftp, conn, dir, FALSE);
if (ftp_connection_pop_job (conn))
{
ftp_connection_push_job (conn, G_VFS_JOB (job));
- if (files != NULL)
+ if (entry != NULL)
{
iter = ftp->dir_ops->iter_new (conn);
- for (walk = files; walk; walk = walk->next)
- {
+ for (sol = eol = entry->data; eol; sol = eol + 1)
+ {
char *symlink = NULL;
+
+ eol = memchr (sol, '\n', entry->length - (sol - entry->data));
info = ftp->dir_ops->iter_process (iter,
conn,
dir,
NULL,
- walk->data,
+ sol,
query_flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS ? NULL : &symlink);
if (symlink)
{
@@ -2462,7 +2465,7 @@ do_enumerate (GVfsBackend *backend,
ftp_connection_clear_error (conn);
}
else
- g_assert (files == NULL);
+ g_assert (entry == NULL);
g_vfs_backend_ftp_push_connection (ftp, conn);
g_free (dir);
@@ -2539,8 +2542,8 @@ do_delete (GVfsBackend *backend,
"RMD %s", file);
if (response == 550)
{
- const GList *files = enumerate_directory (ftp, conn, file, FALSE);
- if (files)
+ const FtpDirEntry *entry = enumerate_directory (ftp, conn, file, FALSE);
+ if (entry)
{
g_static_rw_lock_reader_unlock (&ftp->directory_cache_lock);
g_set_error_literal (&conn->error,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]