Skip to content

Commit

Permalink
add VipsStreamiu
Browse files Browse the repository at this point in the history
a streami subclass (u for user?) with actions signals you can connect to
to provide implementations of read and seek
  • Loading branch information
jcupitt committed Nov 21, 2019
1 parent 486b95e commit 857aafc
Show file tree
Hide file tree
Showing 9 changed files with 415 additions and 29 deletions.
2 changes: 1 addition & 1 deletion fuzz/test_fuzz.sh
Expand Up @@ -3,7 +3,7 @@
#set -x #set -x
set -e 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. # to disable the fast unwinder to get full stacktraces.
export ASAN_OPTIONS="fast_unwind_on_malloc=0:allocator_may_return_null=1" export ASAN_OPTIONS="fast_unwind_on_malloc=0:allocator_may_return_null=1"
export UBSAN_OPTIONS="print_stacktrace=1" export UBSAN_OPTIONS="print_stacktrace=1"
Expand Down
43 changes: 42 additions & 1 deletion libvips/include/vips/stream.h
Expand Up @@ -191,8 +191,11 @@ typedef struct _VipsStreamiClass {
* *
* Unseekable streams should always return -1. VipsStreami will then * Unseekable streams should always return -1. VipsStreami will then
* seek by _read()ing bytes into memory as required. * 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; } VipsStreamiClass;


Expand All @@ -217,6 +220,44 @@ size_t vips_streami_sniff_at_most( VipsStreami *streami,
unsigned char *vips_streami_sniff( VipsStreami *streami, size_t length ); unsigned char *vips_streami_sniff( VipsStreami *streami, size_t length );
gint64 vips_streami_size( VipsStreami *streami ); 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_TYPE_STREAMO (vips_streamo_get_type())
#define VIPS_STREAMO( obj ) \ #define VIPS_STREAMO( obj ) \
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \
Expand Down
5 changes: 3 additions & 2 deletions libvips/iofuncs/Makefile.am
Expand Up @@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libiofuncs.la
libiofuncs_la_SOURCES = \ libiofuncs_la_SOURCES = \
stream.c \ stream.c \
streami.c \ streami.c \
streamiu.c \
streamo.c \ streamo.c \
bufis.c \ bufis.c \
dbuf.c \ dbuf.c \
Expand Down Expand Up @@ -40,9 +41,9 @@ libiofuncs_la_SOURCES = \
system.c \ system.c \
buffer.c buffer.c


vipsmarshal.h: vipsmarshal.h: vipsmarshal.list
glib-genmarshal --prefix=vips --header vipsmarshal.list > vipsmarshal.h glib-genmarshal --prefix=vips --header vipsmarshal.list > vipsmarshal.h
vipsmarshal.c: vipsmarshal.c: vipsmarshal.list
echo "#include \"vipsmarshal.h\"" > vipsmarshal.c echo "#include \"vipsmarshal.h\"" > vipsmarshal.c
glib-genmarshal --prefix=vips --body vipsmarshal.list >> vipsmarshal.c glib-genmarshal --prefix=vips --body vipsmarshal.list >> vipsmarshal.c


Expand Down
2 changes: 2 additions & 0 deletions libvips/iofuncs/init.c
Expand Up @@ -331,6 +331,7 @@ vips_init( const char *argv0 )
extern GType sink_memory_thread_state_get_type( void ); extern GType sink_memory_thread_state_get_type( void );
extern GType render_thread_state_get_type( void ); extern GType render_thread_state_get_type( void );
extern GType vips_streami_get_type( void ); extern GType vips_streami_get_type( void );
extern GType vips_streamiu_get_type( void );
extern GType vips_streamo_get_type( void ); extern GType vips_streamo_get_type( void );


static gboolean started = FALSE; static gboolean started = FALSE;
Expand Down Expand Up @@ -447,6 +448,7 @@ vips_init( const char *argv0 )
(void) sink_memory_thread_state_get_type(); (void) sink_memory_thread_state_get_type();
(void) render_thread_state_get_type(); (void) render_thread_state_get_type();
(void) vips_streami_get_type(); (void) vips_streami_get_type();
(void) vips_streamiu_get_type();
(void) vips_streamo_get_type(); (void) vips_streamo_get_type();
vips__meta_init_types(); vips__meta_init_types();
vips__interpolate_init(); vips__interpolate_init();
Expand Down
4 changes: 0 additions & 4 deletions libvips/iofuncs/streami.c
Expand Up @@ -33,10 +33,7 @@


/* TODO /* TODO
* *
* - gaussblur is missing the vector path again argh
* - can we map and then close the fd? how about on Windows? * - 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.
*/ */


/* /*
Expand Down Expand Up @@ -280,7 +277,6 @@ vips_streami_read_real( VipsStreami *streami, void *data, size_t length )
} while( bytes_read < 0 && errno == EINTR ); } while( bytes_read < 0 && errno == EINTR );


return( bytes_read ); return( bytes_read );

} }


static gint64 static gint64
Expand Down
247 changes: 247 additions & 0 deletions 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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>

#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /*HAVE_UNISTD_H*/
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/debug.h>

#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 );
}

0 comments on commit 857aafc

Please sign in to comment.