--- /dev/null
+/* $Id$
+ * glib r/w GIOChannel buffered-over r/o GIOChannel for libcaptive
+ * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
+ *
+ * This program 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; exactly version 2 of June 1991 is required
+ *
+ * This program 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 "config.h"
+
+#include "giochannel-blind.h"
+#include "reactos/internal/mm.h" /* for PAGE_SIZE */
+#include <glib/ghash.h>
+#include <glib/gmessages.h>
+#include "captive/macros.h"
+#include "captive/storage.h"
+
+
+#define GIOCHANNEL_BLIND_BLOCK_SIZE (PAGE_SIZE)
+
+/* FIXME: fill 'err' */
+
+struct captive_giochannel_blind {
+ GIOChannel iochannel;
+ GIOChannel *giochannel_ro;
+ guint64 offset; /* gint64 range */
+ guint64 size;
+ GHashTable *buffer_hash; /* (guint64 *) -> (guint8[GIOCHANNEL_BLIND_BLOCK_SIZE]) */
+ };
+
+
+G_LOCK_DEFINE_STATIC(giochannel_blind_funcs);
+static GIOFuncs giochannel_blind_funcs;
+
+
+static guint captive_giochannel_blind_hash_func(const guint64 *key)
+{
+ g_return_val_if_fail(key!=NULL,0);
+
+ return (*key)^((*key)>>23);
+}
+
+static gboolean captive_giochannel_blind_equal_func(const guint64 *ap,const guint64 *bp)
+{
+ g_return_val_if_fail(ap!=NULL,FALSE);
+ g_return_val_if_fail(bp!=NULL,FALSE);
+
+ return (*ap)==(*bp);
+}
+
+static void captive_giochannel_blind_key_destroy_func(guint64 *key)
+{
+ g_return_if_fail(key!=NULL);
+
+ g_free(key);
+}
+
+static void captive_giochannel_blind_value_destroy_func(void *data)
+{
+ g_return_if_fail(data!=NULL);
+
+ g_free(data);
+}
+
+
+static gboolean validate_giochannel_blind(struct captive_giochannel_blind *giochannel_blind)
+{
+ g_return_val_if_fail(giochannel_blind!=NULL,FALSE);
+ g_return_val_if_fail(giochannel_blind->giochannel_ro!=NULL,FALSE);
+ g_return_val_if_fail((gint64)giochannel_blind->offset>=0,FALSE); /* gint64 overflow stored in guint64 */
+ g_return_val_if_fail(giochannel_blind->buffer_hash!=NULL,FALSE);
+
+ return TRUE;
+}
+
+
+static GIOStatus captive_giochannel_blind_io_read
+ (GIOChannel *channel,gchar *buf,gsize count,gsize *bytes_read,GError **err)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
+gpointer data;
+guint64 window_bottom,window_top,window_now;
+guint64 transfer_bottom,transfer_top;
+GIOStatus errgiostatus;
+guint64 maxread; /* maximum offset of end of data we successfuly read */
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+ g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
+ g_return_val_if_fail(bytes_read!=NULL,G_IO_STATUS_ERROR);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read(offset=0x%llX,count=0x%lX)",G_STRLOC,
+ giochannel_blind->offset,(gulong)count);
+
+ window_bottom=CAPTIVE_ROUND_DOWN(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
+ window_top=CAPTIVE_ROUND_UP(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
+ maxread=giochannel_blind->offset;
+
+ for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
+gsize bytes_read;
+
+ transfer_bottom=MAX(window_now,giochannel_blind->offset);
+ transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
+ if ((data=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now))) {
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read-from-memcpy(window_now=0x%llX,dest=buf+0x%lX,src=data+0x%lX,n=0x%lX)",
+ G_STRLOC,
+ (guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(gulong)(transfer_bottom-window_now),
+ (gulong)(transfer_top-transfer_bottom));
+ memcpy(
+ buf+transfer_bottom-giochannel_blind->offset, /* dest */
+ data+transfer_bottom-window_now, /* src */
+ transfer_top-transfer_bottom); /* n */
+ maxread=transfer_top;
+ continue;
+ }
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: read-from-io(window_now=0x%llX,buf=buf+0x%lX,seek=0x%llX,count=0x%lX)",G_STRLOC,
+ (guint64)window_now,(gulong)(transfer_bottom-giochannel_blind->offset),(guint64)transfer_bottom,
+ (gulong)(transfer_top-transfer_bottom));
+ errgiostatus=g_io_channel_seek_position(
+ giochannel_blind->giochannel_ro, /* channel */
+ transfer_bottom, /* offset */
+ G_SEEK_SET, /* type */
+ err); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
+ errgiostatus=g_io_channel_read_chars(
+ giochannel_blind->giochannel_ro, /* channel */
+ buf+transfer_bottom-giochannel_blind->offset, /* buf */
+ transfer_top-transfer_bottom, /* count */
+ &bytes_read, /* bytes_read */
+ err); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
+ g_return_val_if_fail(bytes_read<=(transfer_top-transfer_bottom),G_IO_STATUS_ERROR);
+ g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
+ maxread=transfer_bottom+bytes_read;
+ if (bytes_read==transfer_top-transfer_bottom)
+ g_return_val_if_fail(transfer_bottom+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
+ else
+ break;
+ }
+
+ *bytes_read=maxread-giochannel_blind->offset;
+ giochannel_blind->offset=maxread;
+ return (*bytes_read == 0 ? G_IO_STATUS_EOF : G_IO_STATUS_NORMAL);
+}
+
+
+static GIOStatus captive_giochannel_blind_io_write
+ (GIOChannel *channel,const gchar *buf,gsize count,gsize *bytes_written,GError **err)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
+gpointer data;
+guint64 window_bottom,window_top,window_now;
+guint64 transfer_bottom,transfer_top;
+GIOStatus errgiostatus;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+ g_return_val_if_fail(buf!=NULL,G_IO_STATUS_ERROR);
+ g_return_val_if_fail(bytes_written!=NULL,G_IO_STATUS_ERROR);
+
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write(offset=0x%llX,count=0x%lX)",G_STRLOC,
+ giochannel_blind->offset,(gulong)count);
+
+ g_return_val_if_fail(giochannel_blind->offset+count<=giochannel_blind->size,G_IO_STATUS_ERROR);
+
+ window_bottom=CAPTIVE_ROUND_DOWN(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
+ window_top=CAPTIVE_ROUND_UP(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
+
+ for (window_now=window_bottom;window_now<window_top;window_now+=GIOCHANNEL_BLIND_BLOCK_SIZE) {
+gsize bytes_read;
+
+ transfer_bottom=MAX(window_now,giochannel_blind->offset);
+ transfer_top=MIN(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE,giochannel_blind->offset+count);
+ if (!(data=g_hash_table_lookup(giochannel_blind->buffer_hash,&window_now))) {
+guint64 *keyp;
+
+ data=g_malloc(GIOCHANNEL_BLIND_BLOCK_SIZE);
+ captive_new(keyp);
+ *keyp=window_now;
+ g_hash_table_insert(
+ giochannel_blind->buffer_hash, /* hash_table */
+ keyp, /* key */
+ data); /* value */
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-new-mem(window_now=0x%llX)",G_STRLOC,
+ (guint64)window_now);
+
+ /* Missing lower part of buffer? */
+ if (transfer_bottom>window_now) {
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-mem-read-lower(seek=0x%llX,count=0x%lX)",G_STRLOC,
+ (guint64)window_now,(gulong)(transfer_bottom-window_now));
+ errgiostatus=g_io_channel_seek_position(
+ giochannel_blind->giochannel_ro, /* channel */
+ window_now, /* offset */
+ G_SEEK_SET, /* type */
+ err); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
+ errgiostatus=g_io_channel_read_chars(
+ giochannel_blind->giochannel_ro, /* channel */
+ data, /* buf */
+ transfer_bottom-window_now, /* count */
+ &bytes_read, /* bytes_read */
+ err); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
+ g_return_val_if_fail(bytes_read==(transfer_bottom-window_now),G_IO_STATUS_ERROR);
+ }
+
+ /* Missing upper part of buffer? */
+ if (transfer_top<window_now+GIOCHANNEL_BLIND_BLOCK_SIZE) {
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-mem-read-upper(buf=buf+0x%lX,seek=0x%llX,count=0x%lX)",G_STRLOC,
+ (gulong)(transfer_top-window_now),(guint64)transfer_top,
+ (gulong)(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top));
+ errgiostatus=g_io_channel_seek_position(
+ giochannel_blind->giochannel_ro, /* channel */
+ transfer_top, /* offset */
+ G_SEEK_SET, /* type */
+ err); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
+ errgiostatus=g_io_channel_read_chars(
+ giochannel_blind->giochannel_ro, /* channel */
+ data+transfer_top-window_now, /* buf */
+ window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top, /* count */
+ &bytes_read, /* bytes_read */
+ err); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL || errgiostatus==G_IO_STATUS_EOF,errgiostatus);
+ g_return_val_if_fail(bytes_read<=(window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top),G_IO_STATUS_ERROR);
+ g_return_val_if_fail((errgiostatus==G_IO_STATUS_EOF)==(bytes_read==0),G_IO_STATUS_ERROR);
+ if (bytes_read==window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top)
+ g_return_val_if_fail(transfer_top+bytes_read<=giochannel_blind->size,G_IO_STATUS_ERROR);
+ else
+ g_return_val_if_fail(transfer_top+bytes_read==giochannel_blind->size,G_IO_STATUS_ERROR); /* EOF hit */
+ }
+
+ }
+ g_log(G_LOG_DOMAIN,G_LOG_LEVEL_DEBUG,"%s: write-by-memcpy(window_now=0x%llX,dest=data+0x%lX,src=buf+0x%lX,n=0x%lX)",
+ G_STRLOC,
+ (guint64)window_now,(gulong)(transfer_bottom-window_now),(gulong)(transfer_bottom-giochannel_blind->offset),
+ (gulong)(transfer_top-transfer_bottom));
+ memcpy(
+ ((char *)data)+transfer_bottom-window_now, /* dest */
+ buf+transfer_bottom-giochannel_blind->offset, /* src */
+ transfer_top-transfer_bottom); /* n */
+ }
+
+ *bytes_written=count;
+ giochannel_blind->offset+=(*bytes_written);
+ return G_IO_STATUS_NORMAL;
+}
+
+
+static GIOStatus captive_giochannel_blind_io_seek(GIOChannel *channel,gint64 offset,GSeekType type,GError **err)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+
+ switch (type) {
+ case G_SEEK_CUR: giochannel_blind->offset+= offset; break;
+ case G_SEEK_SET: giochannel_blind->offset = offset; break;
+ case G_SEEK_END: giochannel_blind->offset =giochannel_blind->size+offset; break;
+ default: g_return_val_if_reached(G_IO_STATUS_ERROR);
+ }
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR); /* 'offset' overflow? */
+
+ return G_IO_STATUS_NORMAL;
+}
+
+
+static GIOStatus captive_giochannel_blind_io_close(GIOChannel *channel,GError **err)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
+GIOStatus erriostatus;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+
+ /* We are not authorized to destroy 'giochannel_blind->giochannel_ro'. */
+ erriostatus=g_io_channel_flush(
+ giochannel_blind->giochannel_ro, /* channel */
+ NULL); /* error */
+ g_assert(erriostatus==G_IO_STATUS_NORMAL);
+ giochannel_blind->giochannel_ro=NULL;
+
+ g_hash_table_destroy(giochannel_blind->buffer_hash);
+ giochannel_blind->buffer_hash=NULL;
+
+ return G_IO_STATUS_NORMAL;
+}
+
+
+static GSource* captive_giochannel_blind_io_create_watch(GIOChannel *channel,GIOCondition condition)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),NULL);
+
+ g_return_val_if_reached(NULL); /* FIXME: NOT IMPLEMENTED YET */
+}
+
+
+static void captive_giochannel_blind_io_free(GIOChannel *channel)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
+
+ /* After captive_giochannel_blind_io_close() 'giochannel_blind'
+ * may be no longer valid for validate_giochannel_blind(giochannel_blind).
+ */
+ g_return_if_fail(giochannel_blind!=NULL);
+
+ g_assert(giochannel_blind->giochannel_ro==NULL);
+ g_assert(giochannel_blind->buffer_hash==NULL);
+
+ g_free(giochannel_blind);
+}
+
+
+static GIOStatus captive_giochannel_blind_io_set_flags(GIOChannel *channel,GIOFlags flags,GError **err)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+
+ return g_io_channel_set_flags(giochannel_blind->giochannel_ro,(flags&~G_IO_FLAG_IS_WRITEABLE),err);
+}
+
+
+static GIOFlags captive_giochannel_blind_io_get_flags(GIOChannel *channel)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)channel;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),0);
+
+ return g_io_channel_get_flags(giochannel_blind->giochannel_ro) | G_IO_FLAG_IS_WRITEABLE;
+}
+
+
+struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_ro)
+{
+struct captive_giochannel_blind *giochannel_blind;
+GIOStatus erriostatus;
+
+ g_return_val_if_fail(giochannel_ro!=NULL,NULL);
+
+ G_LOCK(giochannel_blind_funcs);
+ giochannel_blind_funcs.io_read =captive_giochannel_blind_io_read;
+ giochannel_blind_funcs.io_write =captive_giochannel_blind_io_write;
+ giochannel_blind_funcs.io_seek =captive_giochannel_blind_io_seek;
+ giochannel_blind_funcs.io_close =captive_giochannel_blind_io_close;
+ giochannel_blind_funcs.io_create_watch=captive_giochannel_blind_io_create_watch;
+ giochannel_blind_funcs.io_free =captive_giochannel_blind_io_free;
+ giochannel_blind_funcs.io_set_flags =captive_giochannel_blind_io_set_flags;
+ giochannel_blind_funcs.io_get_flags =captive_giochannel_blind_io_get_flags;
+ G_UNLOCK(giochannel_blind_funcs);
+
+ erriostatus=g_io_channel_set_encoding(giochannel_ro,
+ NULL, /* encoding; force binary data */
+ NULL); /* error */
+ g_assert(erriostatus==G_IO_STATUS_NORMAL);
+
+ captive_new(giochannel_blind);
+ g_assert(G_STRUCT_OFFSET(struct captive_giochannel_blind,iochannel)==0); /* safely re-type-able */
+ g_io_channel_init(&giochannel_blind->iochannel);
+ giochannel_blind->iochannel.funcs=&giochannel_blind_funcs;
+ giochannel_blind->iochannel.is_seekable=TRUE;
+ giochannel_blind->iochannel.is_readable=TRUE;
+ giochannel_blind->iochannel.is_writeable=TRUE;
+ giochannel_blind->iochannel.close_on_unref=TRUE; /* run g_io_channel_shutdown() flush on last unref */
+ giochannel_blind->giochannel_ro=giochannel_ro;
+ giochannel_blind->offset=0;
+ giochannel_blind->size=captive_giochannel_size(giochannel_ro);
+ giochannel_blind->buffer_hash=g_hash_table_new_full(
+ (GHashFunc)captive_giochannel_blind_hash_func, /* hash_func */
+ (GEqualFunc)captive_giochannel_blind_equal_func, /* key_equal_func */
+ (GDestroyNotify)captive_giochannel_blind_key_destroy_func, /* key_destroy_func */
+ (GDestroyNotify)captive_giochannel_blind_value_destroy_func); /* value_destroy_func */
+
+ return giochannel_blind;
+}