From 857aafc8c26f5490d1059b3e2b3f27116ce13c30 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 21 Nov 2019 16:22:43 +0000 Subject: [PATCH] add VipsStreamiu a streami subclass (u for user?) with actions signals you can connect to to provide implementations of read and seek --- fuzz/test_fuzz.sh | 2 +- libvips/include/vips/stream.h | 43 +++++- libvips/iofuncs/Makefile.am | 5 +- libvips/iofuncs/init.c | 2 + libvips/iofuncs/streami.c | 4 - libvips/iofuncs/streamiu.c | 247 +++++++++++++++++++++++++++++++ libvips/iofuncs/vipsmarshal.c | 102 +++++++++++-- libvips/iofuncs/vipsmarshal.h | 35 ++++- libvips/iofuncs/vipsmarshal.list | 4 +- 9 files changed, 415 insertions(+), 29 deletions(-) create mode 100644 libvips/iofuncs/streamiu.c diff --git a/fuzz/test_fuzz.sh b/fuzz/test_fuzz.sh index 782b7d42..60d75a23 100755 --- a/fuzz/test_fuzz.sh +++ b/fuzz/test_fuzz.sh @@ -3,7 +3,7 @@ #set -x set -e -# Glib is build without -fno-omit-frame-pointer. We need +# Glib is built without -fno-omit-frame-pointer. We need # to disable the fast unwinder to get full stacktraces. export ASAN_OPTIONS="fast_unwind_on_malloc=0:allocator_may_return_null=1" export UBSAN_OPTIONS="print_stacktrace=1" diff --git a/libvips/include/vips/stream.h b/libvips/include/vips/stream.h index 7a617096..d6055718 100644 --- a/libvips/include/vips/stream.h +++ b/libvips/include/vips/stream.h @@ -191,8 +191,11 @@ typedef struct _VipsStreamiClass { * * Unseekable streams should always return -1. VipsStreami will then * seek by _read()ing bytes into memory as required. + * + * We have to use int64 rather than off_t, since we must work on + * Windows, where off_t can be 32-bits. */ - gint64 (*seek)( VipsStreami *, gint64 offset, int ); + gint64 (*seek)( VipsStreami *, gint64, int ); } VipsStreamiClass; @@ -217,6 +220,44 @@ size_t vips_streami_sniff_at_most( VipsStreami *streami, unsigned char *vips_streami_sniff( VipsStreami *streami, size_t length ); gint64 vips_streami_size( VipsStreami *streami ); +#define VIPS_TYPE_STREAMIU (vips_streamiu_get_type()) +#define VIPS_STREAMIU( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_STREAMIU, VipsStreamiu )) +#define VIPS_STREAMIU_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_STREAMIU, VipsStreamiuClass)) +#define VIPS_IS_STREAMIU( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_STREAMIU )) +#define VIPS_IS_STREAMIU_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_STREAMIU )) +#define VIPS_STREAMIU_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_STREAMIU, VipsStreamiuClass )) + +/* Subclass of streamiu with signals for handlers. This is supposed to be + * useful for language bindings. + */ +typedef struct _VipsStreamiu { + VipsStreami parent_object; + +} VipsStreamiu; + +typedef struct _VipsStreamiuClass { + VipsStreamiClass parent_class; + + /* The action signals clients can use to implement read and seek. + * We must use gint64 everywhere since there's no G_TYPE_SIZE. + */ + + gint64 (*read)( VipsStreamiu *, void *, gint64 ); + gint64 (*seek)( VipsStreamiu *, gint64, int ); + +} VipsStreamiuClass; + +GType vips_streamiu_get_type( void ); +VipsStreamiu *vips_streamiu_new( void ); + #define VIPS_TYPE_STREAMO (vips_streamo_get_type()) #define VIPS_STREAMO( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index f31007a6..76957617 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libiofuncs.la libiofuncs_la_SOURCES = \ stream.c \ streami.c \ + streamiu.c \ streamo.c \ bufis.c \ dbuf.c \ @@ -40,9 +41,9 @@ libiofuncs_la_SOURCES = \ system.c \ buffer.c -vipsmarshal.h: +vipsmarshal.h: vipsmarshal.list glib-genmarshal --prefix=vips --header vipsmarshal.list > vipsmarshal.h -vipsmarshal.c: +vipsmarshal.c: vipsmarshal.list echo "#include \"vipsmarshal.h\"" > vipsmarshal.c glib-genmarshal --prefix=vips --body vipsmarshal.list >> vipsmarshal.c diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index b3ec8ecf..4fc72969 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -331,6 +331,7 @@ vips_init( const char *argv0 ) extern GType sink_memory_thread_state_get_type( void ); extern GType render_thread_state_get_type( void ); extern GType vips_streami_get_type( void ); + extern GType vips_streamiu_get_type( void ); extern GType vips_streamo_get_type( void ); static gboolean started = FALSE; @@ -447,6 +448,7 @@ vips_init( const char *argv0 ) (void) sink_memory_thread_state_get_type(); (void) render_thread_state_get_type(); (void) vips_streami_get_type(); + (void) vips_streamiu_get_type(); (void) vips_streamo_get_type(); vips__meta_init_types(); vips__interpolate_init(); diff --git a/libvips/iofuncs/streami.c b/libvips/iofuncs/streami.c index 56232dcc..446153ef 100644 --- a/libvips/iofuncs/streami.c +++ b/libvips/iofuncs/streami.c @@ -33,10 +33,7 @@ /* TODO * - * - gaussblur is missing the vector path again argh * - can we map and then close the fd? how about on Windows? - * - make a subclass that lets you set vfuncs as params, inc. close(), - * is_pipe etc. */ /* @@ -280,7 +277,6 @@ vips_streami_read_real( VipsStreami *streami, void *data, size_t length ) } while( bytes_read < 0 && errno == EINTR ); return( bytes_read ); - } static gint64 diff --git a/libvips/iofuncs/streamiu.c b/libvips/iofuncs/streamiu.c new file mode 100644 index 00000000..053c6255 --- /dev/null +++ b/libvips/iofuncs/streamiu.c @@ -0,0 +1,247 @@ +/* A Streamiu subclass with signals you can easily hook up to other input + * sources. + * + * J.Cupitt, 21/11/19 + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* TODO + * + * - can we map and then close the fd? how about on Windows? + */ + +/* + */ +#define VIPS_DEBUG + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "vipsmarshal.h" + +G_DEFINE_TYPE( VipsStreamiu, vips_streamiu, VIPS_TYPE_STREAMI ); + +/* Our signals. + */ +enum { + SIG_SEEK, + SIG_READ, + SIG_LAST +}; + +static guint vips_streamiu_signals[SIG_LAST] = { 0 }; + +static void +vips_streamiu_finalize( GObject *gobject ) +{ + VipsStreamiu *streamiu = VIPS_STREAMIU( gobject ); + + VIPS_DEBUG_MSG( "vips_streamiu_finalize:\n" ); + + G_OBJECT_CLASS( vips_streamiu_parent_class )->finalize( gobject ); +} + +static int +vips_streamiu_build( VipsObject *object ) +{ + VipsStream *stream = VIPS_STREAM( object ); + VipsStreamiu *streamiu = VIPS_STREAMIU( object ); + VipsStreamiuClass *class = VIPS_STREAMIU_GET_CLASS( streamiu ); + + VIPS_DEBUG_MSG( "vips_streamiu_build: %p\n", streamiu ); + + if( VIPS_OBJECT_CLASS( vips_streamiu_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static ssize_t +vips_streamiu_read_real( VipsStreami *streami, + void *data, size_t length ) +{ + ssize_t size; + + VIPS_DEBUG_MSG( "vips_streamiu_read_real:\n" ); + + g_signal_emit( streami, vips_streamiu_signals[SIG_READ], 0, + data, length, &size ); + + VIPS_DEBUG_MSG( " %zd\n", size ); + + return( size ); +} + +static gint64 +vips_streamiu_seek_real( VipsStreami *streami, + gint64 offset, int whence ) +{ + gint64 new_position; + + VIPS_DEBUG_MSG( "vips_streamiu_seek_real:\n" ); + + g_signal_emit( streami, vips_streamiu_signals[SIG_SEEK], 0, + offset, whence, &new_position ); + + VIPS_DEBUG_MSG( " %zd\n", new_position ); + + return( new_position ); +} + +static gint64 +vips_streamiu_read_signal_real( VipsStreamiu *streamiu, + void *data, gint64 length ) +{ + VIPS_DEBUG_MSG( "vips_streamiu_read_signal_real:\n" ); + + return( 0 ); +} + +static gint64 +vips_streamiu_seek_signal_real( VipsStreamiu *streamiu, + gint64 offset, int whence ) +{ + VIPS_DEBUG_MSG( "vips_streamiu_seek_signal_real:\n" ); + + return( -1 ); +} + +static void +vips_streamiu_class_init( VipsStreamiuClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + VipsStreamiClass *streami_class = VIPS_STREAMI_CLASS( class ); + + gobject_class->finalize = vips_streamiu_finalize; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "streamiu"; + object_class->description = _( "input stream" ); + + object_class->build = vips_streamiu_build; + + streami_class->read = vips_streamiu_read_real; + streami_class->seek = vips_streamiu_seek_real; + + class->read = vips_streamiu_read_signal_real; + class->seek = vips_streamiu_seek_signal_real; + + /** + * VipsStreamiu::read: + * @streamiu: the stream being operated on + * @buffer: %gint64, seek offset + * @size: %gint, seek origin + * + * This signal is emitted to seek the stream. The handler should + * change the stream position appropriately. + * + * Returns: the new seek position. + */ + vips_streamiu_signals[SIG_READ] = g_signal_new( "read", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_ACTION, + G_STRUCT_OFFSET( VipsStreamiuClass, read ), + NULL, NULL, + vips_INT64__INT64_INT, + G_TYPE_INT64, 2, + G_TYPE_INT64, G_TYPE_INT ); + + /** + * VipsStreamiu::seek: + * @streamiu: the stream being operated on + * @offset: %gint64, seek offset + * @whence: %gint, seek origin + * + * This signal is emitted to seek the stream. The handler should + * change the stream position appropriately. + * + * The handler on an unseekable stream should always return -1. + * + * Returns: the new seek position. + */ + vips_streamiu_signals[SIG_SEEK] = g_signal_new( "seek", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_ACTION, + G_STRUCT_OFFSET( VipsStreamiuClass, seek ), + NULL, NULL, + vips_INT64__POINTER_INT64, + G_TYPE_INT64, 2, + G_TYPE_POINTER, G_TYPE_INT64 ); + +} + +static void +vips_streamiu_init( VipsStreamiu *streamiu ) +{ +} + +/** + * vips_streamiu_new: + * + * Create a #VipsStreamiu. Attach signals to implement read and seek. + * + * Returns: a new #VipsStreamiu + */ +VipsStreamiu * +vips_streamiu_new( void ) +{ + VipsStreamiu *streamiu; + + VIPS_DEBUG_MSG( "vips_streamiu_new:\n" ); + + streamiu = VIPS_STREAMIU( g_object_new( VIPS_TYPE_STREAMIU, NULL ) ); + + if( vips_object_build( VIPS_OBJECT( streamiu ) ) ) { + VIPS_UNREF( streamiu ); + return( NULL ); + } + + return( streamiu ); +} diff --git a/libvips/iofuncs/vipsmarshal.c b/libvips/iofuncs/vipsmarshal.c index 655ffb32..a84aea1c 100644 --- a/libvips/iofuncs/vipsmarshal.c +++ b/libvips/iofuncs/vipsmarshal.c @@ -1,7 +1,6 @@ #include "vipsmarshal.h" - -#include - +/* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ +#include #ifdef G_ENABLE_DEBUG #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) @@ -49,21 +48,20 @@ #define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer #endif /* !G_ENABLE_DEBUG */ - -/* INT:VOID (vipsmarshal.list:25) */ +/* INT: VOID (vipsmarshal.list:25) */ void vips_INT__VOID (GClosure *closure, - GValue *return_value G_GNUC_UNUSED, + GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { - typedef gint (*GMarshalFunc_INT__VOID) (gpointer data1, - gpointer data2); - register GMarshalFunc_INT__VOID callback; - register GCClosure *cc = (GCClosure*) closure; - register gpointer data1, data2; + typedef gint (*GMarshalFunc_INT__VOID) (gpointer data1, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_INT__VOID callback; gint v_return; g_return_if_fail (return_value != NULL); @@ -87,3 +85,85 @@ vips_INT__VOID (GClosure *closure, g_value_set_int (return_value, v_return); } +/* INT64: INT64, INT (vipsmarshal.list:26) */ +void +vips_INT64__INT64_INT (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gint64 (*GMarshalFunc_INT64__INT64_INT) (gpointer data1, + gint64 arg1, + gint arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_INT64__INT64_INT callback; + gint64 v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_INT64__INT64_INT) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_int64 (param_values + 1), + g_marshal_value_peek_int (param_values + 2), + data2); + + g_value_set_int64 (return_value, v_return); +} + +/* INT64: POINTER, INT64 (vipsmarshal.list:27) */ +void +vips_INT64__POINTER_INT64 (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef gint64 (*GMarshalFunc_INT64__POINTER_INT64) (gpointer data1, + gpointer arg1, + gint64 arg2, + gpointer data2); + GCClosure *cc = (GCClosure *) closure; + gpointer data1, data2; + GMarshalFunc_INT64__POINTER_INT64 callback; + gint64 v_return; + + g_return_if_fail (return_value != NULL); + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_INT64__POINTER_INT64) (marshal_data ? marshal_data : cc->callback); + + v_return = callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_int64 (param_values + 2), + data2); + + g_value_set_int64 (return_value, v_return); +} + diff --git a/libvips/iofuncs/vipsmarshal.h b/libvips/iofuncs/vipsmarshal.h index 2f3e711f..06f7d675 100644 --- a/libvips/iofuncs/vipsmarshal.h +++ b/libvips/iofuncs/vipsmarshal.h @@ -1,20 +1,39 @@ +/* This file is generated by glib-genmarshal, do not modify it. This code is licensed under the same license as the containing project. Note that it links to GLib, so must comply with the LGPL linking clauses. */ +#ifndef __VIPS_MARSHAL_H__ +#define __VIPS_MARSHAL_H__ -#ifndef __vips_MARSHAL_H__ -#define __vips_MARSHAL_H__ - -#include +#include G_BEGIN_DECLS -/* INT:VOID (vipsmarshal.list:25) */ -extern void vips_INT__VOID (GClosure *closure, +/* INT: VOID (vipsmarshal.list:25) */ +extern +void vips_INT__VOID (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +/* INT64: INT64, INT (vipsmarshal.list:26) */ +extern +void vips_INT64__INT64_INT (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data); +/* INT64: POINTER, INT64 (vipsmarshal.list:27) */ +extern +void vips_INT64__POINTER_INT64 (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + + G_END_DECLS -#endif /* __vips_MARSHAL_H__ */ - +#endif /* __VIPS_MARSHAL_H__ */ diff --git a/libvips/iofuncs/vipsmarshal.list b/libvips/iofuncs/vipsmarshal.list index 0ce2c772..016bfc2c 100644 --- a/libvips/iofuncs/vipsmarshal.list +++ b/libvips/iofuncs/vipsmarshal.list @@ -23,5 +23,5 @@ # BOOL deprecated alias for BOOLEAN INT: VOID - - +INT64: INT64, INT +INT64: POINTER, INT64