diff options
Diffstat (limited to 'flow/gsl/gslfilehash.c')
-rw-r--r-- | flow/gsl/gslfilehash.c | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/flow/gsl/gslfilehash.c b/flow/gsl/gslfilehash.c new file mode 100644 index 0000000..ac4c066 --- /dev/null +++ b/flow/gsl/gslfilehash.c @@ -0,0 +1,464 @@ +/* GSL - Generic Sound Layer + * Copyright (C) 2002 Tim Janik + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ +#include "gslfilehash.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + + +/* macros */ +#if (GLIB_SIZEOF_LONG > 4) +#define HASH_LONG(l) (l + (l >> 32)) +#else +#define HASH_LONG(l) (l) +#endif + + +/* --- variables --- */ +static GslMutex fdpool_mutex = { 0, }; +static GHashTable *hfile_ht = NULL; + + +/* --- functions --- */ +static guint +hfile_hash (gconstpointer key) +{ + const GslHFile *hfile = key; + guint h; + + h = HASH_LONG (hfile->mtime); + h ^= g_str_hash (hfile->file_name); + h ^= HASH_LONG (hfile->n_bytes); + + return h; +} + +static gboolean +hfile_equals (gconstpointer key1, + gconstpointer key2) +{ + const GslHFile *hfile1 = key1; + const GslHFile *hfile2 = key2; + + return (hfile1->mtime == hfile2->mtime && + hfile1->n_bytes == hfile2->n_bytes && + strcmp (hfile1->file_name, hfile2->file_name) == 0); +} + +void +_gsl_init_fd_pool (void) +{ + g_assert (hfile_ht == NULL); + + gsl_mutex_init (&fdpool_mutex); + hfile_ht = g_hash_table_new (hfile_hash, hfile_equals); +} + +static gboolean +stat_fd (gint fd, + GTime *mtime, + GslLong *n_bytes) +{ + struct stat statbuf = { 0, }; + + if (fstat (fd, &statbuf) < 0) + return FALSE; /* have errno */ + if (mtime) + *mtime = statbuf.st_mtime; + if (n_bytes) + *n_bytes = statbuf.st_size; + return TRUE; +} + +static gboolean +stat_file (const gchar *file_name, + GTime *mtime, + GslLong *n_bytes) +{ + struct stat statbuf = { 0, }; + + if (stat (file_name, &statbuf) < 0) + return FALSE; /* have errno */ + if (mtime) + *mtime = statbuf.st_mtime; + if (n_bytes) + *n_bytes = statbuf.st_size; + return TRUE; +} + +/** + * gsl_hfile_open + * @file_name: name of the file to open + * @RETURNS: a new opened #GslHFile or NULL if an error occoured (errno set) + * + * Open a file for reading and return the associated GSL hashed file. + * The motivation for using a #GslHFile over normal unix file + * descriptors is to reduce the amount of opened unix file descriptors and + * to ensure thread safety upon reading offset relative byte blocks. + * Multiple open #GslHFiles with equal file names will share a + * single unix file descriptor as long as the file wasn't modified meanwhile. + * This function is MT-safe and may be called from any thread. + */ +GslHFile* +gsl_hfile_open (const gchar *file_name) +{ + GslHFile key, *hfile; + gint ret_errno; + + errno = EFAULT; + g_return_val_if_fail (file_name != NULL, NULL); + + key.file_name = (gchar*) file_name; + if (!stat_file (file_name, &key.mtime, &key.n_bytes)) + return NULL; /* errno from stat() */ + + GSL_SYNC_LOCK (&fdpool_mutex); + hfile = g_hash_table_lookup (hfile_ht, &key); + if (hfile) + { + GSL_SYNC_LOCK (&hfile->mutex); + hfile->ocount++; + GSL_SYNC_UNLOCK (&hfile->mutex); + ret_errno = 0; + } + else + { + gint fd; + + fd = open (file_name, O_RDONLY | O_NOCTTY, 0); + if (fd >= 0) + { + hfile = gsl_new_struct0 (GslHFile, 1); + hfile->file_name = g_strdup (file_name); + hfile->mtime = key.mtime; + hfile->n_bytes = key.n_bytes; + hfile->cpos = 0; + hfile->fd = fd; + hfile->ocount = 1; + gsl_mutex_init (&hfile->mutex); + g_hash_table_insert (hfile_ht, hfile, hfile); + ret_errno = 0; + } + else + ret_errno = errno; + } + GSL_SYNC_UNLOCK (&fdpool_mutex); + + errno = ret_errno; + return hfile; +} + +/** + * gsl_hfile_close + * @hfile: valid #GslHFile + * + * Close and destroy a #GslHFile. + * This function is MT-safe and may be called from any thread. + */ +void +gsl_hfile_close (GslHFile *hfile) +{ + gboolean destroy = FALSE; + + g_return_if_fail (hfile != NULL); + g_return_if_fail (hfile->ocount > 0); + + GSL_SYNC_LOCK (&fdpool_mutex); + GSL_SYNC_LOCK (&hfile->mutex); + if (hfile->ocount > 1) + hfile->ocount--; + else + { + if (!g_hash_table_remove (hfile_ht, hfile)) + g_warning ("%s: failed to unlink hashed file (%p)", + G_STRLOC, hfile); + else + { + hfile->ocount = 0; + destroy = TRUE; + } + } + GSL_SYNC_UNLOCK (&hfile->mutex); + GSL_SYNC_UNLOCK (&fdpool_mutex); + + if (destroy) + { + gsl_mutex_destroy (&hfile->mutex); + close (hfile->fd); + g_free (hfile->file_name); + gsl_delete_struct (GslHFile, hfile); + } + errno = 0; +} + +/** + * gsl_hfile_pread + * @hfile: valid GslHFile + * @offset: offset in bytes within 0 and file end + * @n_bytes: number of bytes to read + * @bytes: buffer to store read bytes + * @error_p: pointer to GslErrorType location + * @RETURNS: amount of bytes read or -1 if an error occoured (errno set) + * + * Read a block of bytes from a GslHFile. + * This function is MT-safe and may be called from any thread. + */ +GslLong +gsl_hfile_pread (GslHFile *hfile, + GslLong offset, + GslLong n_bytes, + gpointer bytes) +{ + GslLong ret_bytes = -1; + gint ret_errno; + + errno = EFAULT; + g_return_val_if_fail (hfile != NULL, -1); + g_return_val_if_fail (hfile->ocount > 0, -1); + g_return_val_if_fail (offset >= 0, -1); + if (offset >= hfile->n_bytes || n_bytes < 1) + { + errno = 0; + return 0; + } + g_return_val_if_fail (bytes != NULL, -1); + + GSL_SYNC_LOCK (&hfile->mutex); + if (hfile->ocount) + { + if (hfile->cpos != offset) + { + hfile->cpos = lseek (hfile->fd, offset, SEEK_SET); + if (hfile->cpos < 0 && errno != EINVAL) + { + ret_errno = errno; + GSL_SYNC_UNLOCK (&hfile->mutex); + errno = ret_errno; + return -1; + } + } + if (hfile->cpos == offset) + { + do + ret_bytes = read (hfile->fd, bytes, n_bytes); + while (ret_bytes < 0 && errno == EINTR); + if (ret_bytes < 0) + { + ret_errno = errno; + ret_bytes = -1; + } + else + { + ret_errno = 0; + hfile->cpos += ret_bytes; + } + } + else /* this should only happen if the file changed since open() */ + { + hfile->cpos = -1; + if (offset + n_bytes > hfile->n_bytes) + n_bytes = hfile->n_bytes - offset; + memset (bytes, 0, n_bytes); + ret_bytes = n_bytes; + ret_errno = 0; + } + } + else + ret_errno = EFAULT; + GSL_SYNC_UNLOCK (&hfile->mutex); + + errno = ret_errno; + return ret_bytes; +} + +/** + * gsl_rfile_open + * @file_name: name of the file to open + * @RETURNS: a new opened #GslRFile or NULL if an error occoured (errno set) + * + * Open a file for reading and create a GSL read only file handle for it. + * The motivation for using a #GslRFile over normal unix files + * is to reduce the amount of opened unix file descriptors by using + * a #GslHFile for the actual IO. + */ +GslRFile* +gsl_rfile_open (const gchar *file_name) +{ + GslHFile *hfile = gsl_hfile_open (file_name); + GslRFile *rfile; + + if (!hfile) + rfile = NULL; + else + { + rfile = gsl_new_struct0 (GslRFile, 1); + rfile->hfile = hfile; + rfile->offset = 0; + } + return rfile; +} + +/** + * gsl_rfile_name + * @rfile: valid #GslRFile + * @RETURNS: the file name used to open this file + * + * Retrive the file name used to open @rfile. + */ +gchar* +gsl_rfile_name (GslRFile *rfile) +{ + errno = EFAULT; + g_return_val_if_fail (rfile != NULL, NULL); + + errno = 0; + return rfile->hfile->file_name; +} + +/** + * gsl_rfile_seek_set + * @rfile: valid #GslRFile + * @offset: new seek position within 0 and gsl_rfile_length()+1 + * @RETURNS: resulting position within 0 and gsl_rfile_length()+1 + * + * Set the current #GslRFile seek position. + */ +GslLong +gsl_rfile_seek_set (GslRFile *rfile, + GslLong offset) +{ + GslLong l; + + errno = EFAULT; + g_return_val_if_fail (rfile != NULL, 0); + + l = rfile->hfile->n_bytes; + rfile->offset = CLAMP (offset, 0, l); + + errno = 0; + return rfile->offset; +} + +/** + * gsl_rfile_position + * @rfile: valid #GslRFile + * @RETURNS: current position within 0 and gsl_rfile_length() + * + * Retrive the current #GslRFile seek position. + */ +GslLong +gsl_rfile_position (GslRFile *rfile) +{ + errno = EFAULT; + g_return_val_if_fail (rfile != NULL, 0); + + errno = 0; + return rfile->offset; +} + +/** + * gsl_rfile_length + * @rfile: valid #GslRFile + * @RETURNS: total length of the #GslRFile in bytes + * + * Retrive the file length of @rfile in bytes. + */ +GslLong +gsl_rfile_length (GslRFile *rfile) +{ + GslLong l; + + errno = EFAULT; + g_return_val_if_fail (rfile != NULL, 0); + + l = rfile->hfile->n_bytes; + + errno = 0; + return l; +} + +/** + * gsl_rfile_pread + * @rfile: valid GslRFile + * @offset: offset in bytes within 0 and gsl_rfile_length() + * @n_bytes: number of bytes to read + * @bytes: buffer to store read bytes + * @error_p: pointer to GslErrorType location + * @RETURNS: amount of bytes read or -1 if an error occoured (errno set) + * + * Read a block of bytes from a GslRFile at a specified position. + */ +GslLong +gsl_rfile_pread (GslRFile *rfile, + GslLong offset, + GslLong n_bytes, + gpointer bytes) +{ + errno = EFAULT; + g_return_val_if_fail (rfile != NULL, -1); + + return gsl_hfile_pread (rfile->hfile, offset, n_bytes, bytes); +} + +/** + * gsl_rfile_read + * @rfile: valid GslRFile + * @n_bytes: number of bytes to read + * @bytes: buffer to store read bytes + * @error_p: pointer to GslErrorType location + * @RETURNS: amount of bytes read or -1 if an error occoured (errno set) + * + * Read a block of bytes from a GslRFile from the current seek position + * and advance the seek position. + */ +GslLong +gsl_rfile_read (GslRFile *rfile, + GslLong n_bytes, + gpointer bytes) +{ + GslLong l; + + errno = EFAULT; + g_return_val_if_fail (rfile != NULL, -1); + + l = gsl_hfile_pread (rfile->hfile, rfile->offset, n_bytes, bytes); + if (l > 0) + rfile->offset += l; + return l; +} + +/** + * gsl_rfile_close + * @rfile: valid #GslRFile + * + * Close and destroy a #GslRFile. + */ +void +gsl_rfile_close (GslRFile *rfile) +{ + errno = EFAULT; + g_return_if_fail (rfile != NULL); + + gsl_hfile_close (rfile->hfile); + gsl_delete_struct (GslRFile, rfile); + errno = 0; +} |