struct captive_giochannel_blind {
GIOChannel iochannel;
- GIOChannel *giochannel_ro;
+ GIOChannel *giochannel_orig;
guint64 offset; /* gint64 range */
guint64 size;
GHashTable *buffer_hash; /* (guint64 *) -> (struct blind_block *) (guint8[GIOCHANNEL_BLIND_BLOCK_SIZE]) */
};
struct blind_block {
+ guint64 offset;
gboolean was_read,was_written;
guint8 *data_written; /* [GIOCHANNEL_BLIND_BLOCK_SIZE] */
};
static GIOFuncs giochannel_blind_funcs;
-static guint captive_giochannel_blind_hash_func(const guint64 *key)
+static guint captive_giochannel_blind_hash_func(const guint64 *keyp)
{
- g_return_val_if_fail(key!=NULL,0);
+ g_return_val_if_fail(keyp!=NULL,0);
- return (*key)^((*key)>>23);
+ return (*keyp)^((*keyp)>>23);
}
static gboolean captive_giochannel_blind_equal_func(const guint64 *ap,const guint64 *bp)
return (*ap)==(*bp);
}
-static void captive_giochannel_blind_key_destroy_func(guint64 *key)
+static void captive_giochannel_blind_key_destroy_func(guint64 *keyp)
{
- g_return_if_fail(key!=NULL);
+ g_return_if_fail(keyp!=NULL);
- g_free(key);
+ g_free(keyp);
}
static void captive_giochannel_blind_value_destroy_func(struct blind_block *blind_block)
static gboolean validate_giochannel_blind(struct captive_giochannel_blind *giochannel_blind)
{
+ g_return_val_if_fail(giochannel_blind->iochannel.funcs==&giochannel_blind_funcs,FALSE);
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(giochannel_blind->giochannel_orig!=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);
(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 */
+ giochannel_blind->giochannel_orig, /* channel */
transfer_bottom, /* offset */
G_SEEK_SET, /* type */
err); /* error */
}
else {
errgiostatus=g_io_channel_read_chars(
- giochannel_blind->giochannel_ro, /* channel */
+ giochannel_blind->giochannel_orig, /* channel */
buf+transfer_bottom-giochannel_blind->offset, /* buf */
transfer_top-transfer_bottom, /* count */
&bytes_read, /* bytes_read */
guint64 *keyp;
captive_new(blind_block);
+ blind_block->offset=window_now;
blind_block->was_read=FALSE;
blind_block->was_written=FALSE;
blind_block->data_written=NULL;
g_return_val_if_fail(giochannel_blind->offset+count<=giochannel_blind->size,G_IO_STATUS_ERROR);
+ g_return_val_if_fail(giochannel_blind->iochannel.is_writeable==TRUE,G_IO_STATUS_ERROR);
+
window_bottom=CAPTIVE_ROUND_DOWN64(giochannel_blind->offset,GIOCHANNEL_BLIND_BLOCK_SIZE);
window_top=CAPTIVE_ROUND_UP64(giochannel_blind->offset+count,GIOCHANNEL_BLIND_BLOCK_SIZE);
guint64 *keyp;
captive_new(blind_block);
+ blind_block->offset=window_now;
blind_block->was_read=FALSE;
blind_block->was_written=FALSE;
captive_new(keyp);
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 */
+ giochannel_blind->giochannel_orig, /* 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 */
+ giochannel_blind->giochannel_orig, /* channel */
blind_block->data_written, /* buf */
transfer_bottom-window_now, /* count */
&bytes_read, /* bytes_read */
(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 */
+ giochannel_blind->giochannel_orig, /* 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 */
+ giochannel_blind->giochannel_orig, /* channel */
blind_block->data_written+transfer_top-window_now, /* buf */
window_now+GIOCHANNEL_BLIND_BLOCK_SIZE-transfer_top, /* count */
&bytes_read, /* bytes_read */
g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
- /* We are not authorized to destroy 'giochannel_blind->giochannel_ro'. */
+ /* We are not authorized to destroy 'giochannel_blind->giochannel_orig'. */
erriostatus=g_io_channel_flush(
- giochannel_blind->giochannel_ro, /* channel */
+ giochannel_blind->giochannel_orig, /* channel */
NULL); /* error */
g_assert(erriostatus==G_IO_STATUS_NORMAL);
- giochannel_blind->giochannel_ro=NULL;
+ giochannel_blind->giochannel_orig=NULL;
g_hash_table_destroy(giochannel_blind->buffer_hash);
giochannel_blind->buffer_hash=NULL;
*/
g_return_if_fail(giochannel_blind!=NULL);
- g_assert(giochannel_blind->giochannel_ro==NULL);
+ g_assert(giochannel_blind->giochannel_orig==NULL);
g_assert(giochannel_blind->buffer_hash==NULL);
g_free(giochannel_blind);
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);
+ return g_io_channel_set_flags(giochannel_blind->giochannel_orig,(flags&~G_IO_FLAG_IS_WRITEABLE),err);
}
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;
+ return g_io_channel_get_flags(giochannel_blind->giochannel_orig) | G_IO_FLAG_IS_WRITEABLE;
}
-struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_ro)
+struct captive_giochannel_blind *captive_giochannel_blind_new(GIOChannel *giochannel_orig,gboolean writeable)
{
struct captive_giochannel_blind *giochannel_blind;
GIOStatus erriostatus;
- g_return_val_if_fail(giochannel_ro!=NULL,NULL);
+ g_return_val_if_fail(giochannel_orig!=NULL,NULL);
G_LOCK(giochannel_blind_funcs);
giochannel_blind_funcs.io_read =captive_giochannel_blind_io_read;
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,
+ erriostatus=g_io_channel_set_encoding(giochannel_orig,
NULL, /* encoding; force binary data */
NULL); /* error */
g_assert(erriostatus==G_IO_STATUS_NORMAL);
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;
+ /* readonly captive_giochannel_blind can be used to track read access. */
+ giochannel_blind->iochannel.is_writeable=writeable;
giochannel_blind->iochannel.close_on_unref=TRUE; /* run g_io_channel_shutdown() flush on last unref */
- giochannel_blind->giochannel_ro=giochannel_ro;
+ giochannel_blind->giochannel_orig=giochannel_orig;
giochannel_blind->offset=0;
- giochannel_blind->size=captive_giochannel_size(giochannel_ro);
+ giochannel_blind->size=captive_giochannel_size(giochannel_orig);
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 */
*size_return=giochannel_blind->size;
return TRUE;
}
+
+
+static void captive_giochannel_blind_as_sorted_array_foreach
+ (const guint64 *keyp,const struct blind_block *blind_block,const struct blind_block ***rpp /* user_data */)
+{
+ g_return_if_fail(keyp!=NULL);
+ g_return_if_fail(blind_block!=NULL);
+ g_return_if_fail(rpp!=NULL);
+
+ if (!blind_block->data_written)
+ return;
+
+ *((*rpp)++)=blind_block;
+}
+
+static int captive_giochannel_blind_as_sorted_array_compat
+ (const struct blind_block *const *ap,const struct blind_block *const *bp)
+{
+ g_return_val_if_fail(ap!=NULL,0);
+ g_return_val_if_fail(*ap!=NULL,0);
+ g_return_val_if_fail(bp!=NULL,0);
+ g_return_val_if_fail(*bp!=NULL,0);
+
+ return ((*ap)->offset>(*bp)->offset) - ((*bp)->offset>(*ap)->offset);
+}
+
+static struct blind_block **captive_giochannel_blind_as_sorted_array(struct captive_giochannel_blind *giochannel_blind)
+{
+guint hash_size;
+struct blind_block **r,**rp;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+
+ hash_size=g_hash_table_size(giochannel_blind->buffer_hash);
+ captive_newn(r,hash_size+1);
+ rp=r;
+ g_hash_table_foreach(giochannel_blind->buffer_hash,(GHFunc)captive_giochannel_blind_as_sorted_array_foreach,&rp);
+ g_assert(rp<=r+hash_size);
+ *rp=NULL;
+ qsort(r,rp-r,sizeof(*r),(int (*)(const void *,const void *))captive_giochannel_blind_as_sorted_array_compat);
+
+ return r;
+}
+
+GIOStatus captive_giochannel_blind_commit(GIOChannel *giochannel)
+{
+struct captive_giochannel_blind *giochannel_blind=(struct captive_giochannel_blind *)giochannel;
+struct blind_block **blind_block_array,**blind_blockp;
+GIOStatus errgiostatus;
+
+ g_return_val_if_fail(validate_giochannel_blind(giochannel_blind),G_IO_STATUS_ERROR);
+
+ errgiostatus=g_io_channel_flush(
+ giochannel, /* channel */
+ NULL); /* error */
+ g_assert(errgiostatus==G_IO_STATUS_NORMAL);
+
+ blind_block_array=captive_giochannel_blind_as_sorted_array(giochannel_blind);
+
+ for (blind_blockp=blind_block_array;*blind_blockp;blind_blockp++) {
+struct blind_block *blind_block=*blind_blockp;
+gsize bytes_written;
+
+ g_assert(blind_block->data_written!=NULL);
+
+ errgiostatus=g_io_channel_seek_position(
+ giochannel_blind->giochannel_orig, /* channel */
+ blind_block->offset, /* offset */
+ G_SEEK_SET, /* type */
+ NULL); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
+ errgiostatus=g_io_channel_write_chars(
+ giochannel_blind->giochannel_orig, /* channel */
+ blind_block->data_written, /* buf */
+ GIOCHANNEL_BLIND_BLOCK_SIZE, /* count */
+ &bytes_written, /* bytes_written */
+ NULL); /* error */
+ g_return_val_if_fail(errgiostatus==G_IO_STATUS_NORMAL,errgiostatus);
+ g_return_val_if_fail(bytes_written==GIOCHANNEL_BLIND_BLOCK_SIZE,G_IO_STATUS_ERROR);
+
+ g_free(blind_block->data_written);
+ blind_block->data_written=NULL;
+ }
+
+ g_free(blind_block_array);
+
+ errgiostatus=g_io_channel_flush(
+ giochannel_blind->giochannel_orig, /* channel */
+ NULL); /* error */
+ g_assert(errgiostatus==G_IO_STATUS_NORMAL);
+
+ return G_IO_STATUS_NORMAL;
+}