diff --git a/ChangeLog b/ChangeLog index ce155f86..c52f5fa3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -22,7 +22,7 @@ - VipsMin stops search early if it can - C API supports optional output args - switch back to int-valued operations -- add the operation cache +- add the operation cache, various --vips-cache-* flags - fallback vips_init() - vips_tracked_malloc() tracks allocation size and can report total mem usage - cache limits, drop, init, flush plus command-line controls diff --git a/TODO b/TODO index 24cad13f..82687f3d 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,14 @@ -- try: +- print_usage could show more about pspecs, eg. for "in" we have: - $ vips copy x.jpg x.v + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsCopy, in ) ); - operation cache is printed twice ... once in atexit? + but only display: - jpegload is not added - - operation object print is unhelpful, we want a list of args, not a usage - message + in :: VipsImage (input) diff --git a/libvips/arithmetic/remainder.c b/libvips/arithmetic/remainder.c index ffe6fabe..c951f7ae 100644 --- a/libvips/arithmetic/remainder.c +++ b/libvips/arithmetic/remainder.c @@ -175,7 +175,7 @@ vips_remainder_class_init( VipsRemainderClass *class ) object_class->nickname = "remainder"; object_class->description = - _( "remainder after integer division for a pair of images" ); + _( "remainder after integer division of two images" ); object_class->build = vips_remainder_build; vips_arithmetic_set_format_table( aclass, vips_bandfmt_remainder ); @@ -324,7 +324,7 @@ vips_remainder_const_class_init( VipsRemainderConstClass *class ) object_class->nickname = "remainder_const"; object_class->description = - _( "remainder after integer division for an image " + _( "remainder after integer division of an image " "and a constant" ); object_class->build = vips_remainder_const_build; diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index 5754a254..b3130b25 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -80,6 +80,7 @@ extern char *vips__disc_threshold; extern char *vips__cache_max; extern char *vips__cache_max_mem; extern char *vips__cache_max_files; +extern gboolean vips__cache_print; typedef int (*im__fftproc_fn)( VipsImage *, VipsImage *, VipsImage * ); diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 39c189b8..37a97993 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -265,6 +265,10 @@ typedef void *(*VipsArgumentMapFn)( VipsObject *, GParamSpec *, VipsArgumentClass *, VipsArgumentInstance *, void *a, void *b ); void *vips_argument_map( VipsObject *object, VipsArgumentMapFn fn, void *a, void *b ); +typedef void *(*VipsArgumentClassMapFn)( VipsObjectClass *, GParamSpec *, + VipsArgumentClass *, void *a, void *b ); +void *vips_argument_class_map( VipsObjectClass *object_class, + VipsArgumentClassMapFn fn, void *a, void *b ); int vips_object_get_argument( VipsObject *object, const char *name, GParamSpec **pspec, VipsArgumentClass **argument_class, @@ -348,6 +352,7 @@ struct _VipsObjectClass { int (*build)( VipsObject *object ); /* Try to print something about the class, handy for help displays. + * Keep to one line. */ void (*print_class)( struct _VipsObjectClass *, VipsBuf * ); diff --git a/libvips/include/vips/operation.h b/libvips/include/vips/operation.h index 3ba62869..ba69a706 100644 --- a/libvips/include/vips/operation.h +++ b/libvips/include/vips/operation.h @@ -69,10 +69,15 @@ typedef struct _VipsOperation { typedef struct _VipsOperationClass { VipsObjectClass parent_class; + /* Print the usage message. + */ + void (*print_usage)( struct _VipsOperationClass *, VipsBuf * ); } VipsOperationClass; GType vips_operation_get_type( void ); +void vips_operation_class_print_usage( VipsOperationClass *operation_class ); + int vips_operation_call_valist( VipsOperation *operation, va_list ap ); VipsOperation *vips_operation_new( const char *name ); int vips_call( const char *operation_name, ... ); diff --git a/libvips/iofuncs/cache.c b/libvips/iofuncs/cache.c index 31e8379b..9ffbf3c1 100644 --- a/libvips/iofuncs/cache.c +++ b/libvips/iofuncs/cache.c @@ -46,8 +46,8 @@ */ /* - */ #define VIPS_DEBUG + */ #ifdef HAVE_CONFIG_H #include @@ -71,6 +71,7 @@ char *vips__cache_max = NULL; char *vips__cache_max_mem = NULL; char *vips__cache_max_files = NULL; +gboolean vips__cache_print = FALSE; /* Max number of cached operations. */ @@ -409,7 +410,6 @@ vips_cache_init( void ) } } -#ifdef VIPS_DEBUG static void vips_cache_print( void ) { @@ -419,11 +419,16 @@ vips_cache_print( void ) printf( "Operation cache:\n" ); g_hash_table_iter_init( &iter, vips_cache_table ); - while( g_hash_table_iter_next( &iter, &key, &value ) ) - vips_object_print( VIPS_OBJECT( key ) ); + while( g_hash_table_iter_next( &iter, &key, &value ) ) { + char str[32768]; + VipsBuf buf = VIPS_BUF_STATIC( str ); + + vips_object_to_string( VIPS_OBJECT( key ), &buf ); + + printf( "%p - %s\n", key, vips_buf_all( &buf ) ); + } } } -#endif /*VIPS_DEBUG*/ static void * vips_object_unref_arg( VipsObject *object, @@ -483,9 +488,8 @@ void vips_cache_drop_all( void ) { if( vips_cache_table ) { -#ifdef VIPS_DEBUG - vips_cache_print(); -#endif /*VIPS_DEBUG*/ + if( vips__cache_print ) + vips_cache_print(); /* We can't modify the hash in the callback from * g_hash_table_foreach() and friends. Repeatedly drop the @@ -501,6 +505,8 @@ vips_cache_drop_all( void ) vips_cache_drop( (VipsOperation *) key ); } + + VIPS_FREEF( g_hash_table_unref, vips_cache_table ); } } @@ -599,6 +605,8 @@ vips_cache_operation_build( VipsOperation **operation ) { VipsOperation *hit; + g_assert( VIPS_IS_OPERATION( *operation ) ); + VIPS_DEBUG_MSG( "vips_cache_operation_build: %p\n", *operation ); vips_cache_init(); diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index bf0b51f1..0e5d02f1 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -395,6 +395,9 @@ static GOptionEntry option_entries[] = { { "vips-cache-max-files", 'l', 0, G_OPTION_ARG_STRING, &vips__cache_max_files, N_( "allow at most N open files" ), "N" }, + { "vips-cache-print", 'r', 0, + G_OPTION_ARG_NONE, &vips__cache_print, + N_( "print operation cache on exit" ), NULL }, { NULL } }; diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index 80d6492a..23c36f6d 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -192,7 +192,7 @@ vips_object_build( VipsObject *object ) void vips_object_print_class( VipsObjectClass *class ) { - char str[1000]; + char str[2048]; VipsBuf buf = VIPS_BUF_STATIC( str ); class->print_class( class, &buf ); @@ -204,13 +204,9 @@ vips_object_print( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - /* This is used for printing image headers, so we may need lots of - * space. See header.c. - */ - char str[32768]; + char str[2048]; VipsBuf buf = VIPS_BUF_STATIC( str ); - vips_object_print_class( class ); class->print( object, &buf ); printf( "%s\n", vips_buf_all( &buf ) ); } @@ -324,6 +320,37 @@ vips_argument_map( VipsObject *object, return( NULL ); } +/* And loop over a class. Same as ^^, but with no VipsArgumentInstance. + */ +void * +vips_argument_class_map( VipsObjectClass *object_class, + VipsArgumentClassMapFn fn, void *a, void *b ) +{ + GSList *p; + + for( p = object_class->argument_table_traverse; p; p = p->next ) { + VipsArgumentClass *arg_class = + (VipsArgumentClass *) p->data; + VipsArgument *argument = (VipsArgument *) arg_class; + GParamSpec *pspec = argument->pspec; + + /* We have many props on the arg table ... filter out the + * ones for this class. + */ + if( g_object_class_find_property( + G_OBJECT_CLASS( object_class ), + g_param_spec_get_name( pspec ) ) == pspec ) { + void *result; + + if( (result = + fn( object_class, pspec, arg_class, a, b )) ) + return( result ); + } + } + + return( NULL ); +} + /* Create a VipsArgumentInstance for each installed argument property. Ideally * we'd do this during _init() but g_object_class_find_property() does not seem * to work then :-( so we have to delay it until first access. See @@ -1244,14 +1271,15 @@ vips_object_set_argument_from_string( VipsObject *object, VipsObject *new_object; VipsImage *out; - /* Use VipsFile to build images, then pull @out from it and + /* Use VipsForeign to build images, then pull @out from it and * use that to set the value. */ if( !(new_object = vips_object_new_from_string( oclass, value )) ) return( -1 ); - if( vips_object_build( new_object ) ) { + if( vips_cache_operation_build( + (VipsOperation **) &new_object ) ) { g_object_unref( new_object ); return( -1 ); } @@ -1283,6 +1311,9 @@ vips_object_set_argument_from_string( VipsObject *object, vips_object_new_from_string( oclass, value )) ) return( -1 ); + /* Not necessarily a VipsOperation subclass so we don't use + * the cache. We could have a separate case for this. + */ if( vips_object_build( new_object ) ) { g_object_unref( new_object ); return( -1 ); @@ -1430,7 +1461,7 @@ vips_object_get_argument_to_string( VipsObject *object, VipsObject *new_object; VipsImage *in; - /* Use VipsFile to make a saver, set 'in' on that and run + /* Use VipsForeign to make a saver, set 'in' on that and run * build to make it write. */ if( !(new_object = @@ -1441,7 +1472,8 @@ vips_object_get_argument_to_string( VipsObject *object, g_object_set( new_object, "in", in, NULL ); g_object_unref( in ); - if( vips_object_build( new_object ) ) { + if( vips_cache_operation_build( + (VipsOperation **) &new_object ) ) { g_object_unref( new_object ); return( -1 ); } diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index ced0063b..9a7deae3 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -74,13 +74,12 @@ typedef struct { gboolean required; /* show required args or optional */ gboolean oftype; /* "is of type" message */ int n; /* Arg number */ -} VipsOperationPrint; +} VipsOperationClassPrint; static void * -vips_operation_print_arg( VipsObject *object, GParamSpec *pspec, - VipsArgumentClass *argument_class, - VipsArgumentInstance *argument_instance, - VipsBuf *buf, VipsOperationPrint *print ) +vips_operation_class_print_arg( VipsObjectClass *object_class, + GParamSpec *pspec, VipsArgumentClass *argument_class, + VipsBuf *buf, VipsOperationClassPrint *print ) { /* Only show construct args ... others are internal. */ @@ -109,6 +108,46 @@ vips_operation_print_arg( VipsObject *object, GParamSpec *pspec, return( NULL ); } +static void +vips_operation_print_usage( VipsOperationClass *class, VipsBuf *buf ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + + VipsOperationClassPrint print; + + /* First pass through args: show the required names. + */ + vips_buf_appendf( buf, " %s ", object_class->nickname ); + print.message = NULL; + print.required = TRUE; + print.oftype = FALSE; + print.n = 0; + vips_argument_class_map( object_class, + (VipsArgumentClassMapFn) vips_operation_class_print_arg, + buf, &print ); + vips_buf_appends( buf, "\n" ); + + /* Show required types. + */ + print.message = "where:"; + print.required = TRUE; + print.oftype = TRUE; + print.n = 0; + vips_argument_class_map( object_class, + (VipsArgumentClassMapFn) vips_operation_class_print_arg, + buf, &print ); + + /* Show optional args. + */ + print.message = "optional arguments:"; + print.required = FALSE; + print.oftype = TRUE; + print.n = 0; + vips_argument_class_map( object_class, + (VipsArgumentClassMapFn) vips_operation_class_print_arg, + buf, &print ); +} + #ifdef VIPS_DEBUG static void * vips_operation_call_argument( VipsObject *object, GParamSpec *pspec, @@ -137,53 +176,16 @@ vips_operation_call_argument( VipsObject *object, GParamSpec *pspec, static void vips_operation_print( VipsObject *object, VipsBuf *buf ) { +#ifdef VIPS_DEBUG VipsOperation *operation = VIPS_OPERATION( object ); VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); - VipsOperationPrint print; -#ifdef VIPS_DEBUG printf( "%s args:\n", object_class->nickname ); vips_argument_map( VIPS_OBJECT( operation ), (VipsArgumentMapFn) vips_operation_call_argument, NULL, NULL ); #endif /*VIPS_DEBUG*/ - /* First pass through args: show the required names. - */ - vips_buf_appendf( buf, " %s ", object_class->nickname ); - print.message = NULL; - print.required = TRUE; - print.oftype = FALSE; - print.n = 0; - vips_argument_map( VIPS_OBJECT( operation ), - (VipsArgumentMapFn) vips_operation_print_arg, buf, &print ); - vips_buf_appends( buf, "\n" ); - - /* Show required types. - */ - print.message = "where:"; - print.required = TRUE; - print.oftype = TRUE; - print.n = 0; - vips_argument_map( VIPS_OBJECT( operation ), - (VipsArgumentMapFn) vips_operation_print_arg, buf, &print ); - - /* Show optional args. - */ - print.message = "optional arguments:"; - print.required = FALSE; - print.oftype = TRUE; - print.n = 0; - vips_argument_map( VIPS_OBJECT( operation ), - (VipsArgumentMapFn) vips_operation_print_arg, buf, &print ); -} - -static int -vips_operation_build( VipsObject *object ) -{ - if( VIPS_OBJECT_CLASS( vips_operation_parent_class )->build( object ) ) - return( -1 ); - - return( 0 ); + VIPS_OBJECT_CLASS( vips_operation_parent_class )->print( object, buf ); } static void @@ -196,9 +198,10 @@ vips_operation_class_init( VipsOperationClass *class ) gobject_class->dispose = vips_operation_dispose; vobject_class->nickname = "operation"; - vobject_class->description = _( "VIPS operations" ); + vobject_class->description = _( "operations" ); vobject_class->print = vips_operation_print; - vobject_class->build = vips_operation_build; + + class->print_usage = vips_operation_print_usage; } static void @@ -208,6 +211,17 @@ vips_operation_init( VipsOperation *operation ) */ } +void +vips_operation_class_print_usage( VipsOperationClass *operation_class ) +{ + char str[2048]; + VipsBuf buf = VIPS_BUF_STATIC( str ); + + operation_class->print_usage( operation_class, &buf ); + printf( "%s", _( "usage:" ) ); + printf( "\n%s", vips_buf_all( &buf ) ); +} + VipsOperation * vips_operation_new( const char *name ) { @@ -896,6 +910,9 @@ vips_call_argv( VipsOperation *operation, int argc, char **argv ) return( -1 ); } + /* We can't use the operation cache, we need to be able to change the + * operation pointer. The cache probably wouldn't help anyway. + */ if( vips_object_build( VIPS_OBJECT( operation ) ) ) return( -1 ); diff --git a/tools/vips.c b/tools/vips.c index 2f42ba80..50919a09 100644 --- a/tools/vips.c +++ b/tools/vips.c @@ -1073,7 +1073,8 @@ main( int argc, char **argv ) if( vips_call_argv( operation, argc - 1, argv + 1 ) ) { if( argc == 1 ) - vips_object_print( VIPS_OBJECT( operation ) ); + vips_operation_class_print_usage( + VIPS_OPERATION_GET_CLASS( operation ) ); vips_object_unref_outputs( VIPS_OBJECT( operation ) ); g_object_unref( operation );