diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 436638ff..8d00dce5 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -627,10 +627,10 @@ static void * vips_foreign_find_load_stream_sub( void *item, void *a, void *b ) { VipsForeignLoadClass *load_class = VIPS_FOREIGN_LOAD_CLASS( item ); - VipsStreamInput *input = VIPS_STREAM_INPUT( a ); + VipsStreami *streami = VIPS_STREAMI( a ); if( load_class->is_a_stream && - load_class->is_a_stream( input ) ) + load_class->is_a_stream( streami ) ) return( load_class ); return( NULL ); @@ -638,7 +638,7 @@ vips_foreign_find_load_stream_sub( void *item, void *a, void *b ) /** * vips_foreign_find_load_stream: - * @input: stream to load from + * @streami: stream to load from * * Searches for an operation you could use to load a stream. To see the * range of buffer loaders supported by your vips, try something like: @@ -651,14 +651,14 @@ vips_foreign_find_load_stream_sub( void *item, void *a, void *b ) * error. */ const char * -vips_foreign_find_load_stream( VipsStreamInput *input ) +vips_foreign_find_load_stream( VipsStreami *streami ) { VipsForeignLoadClass *load_class; if( !(load_class = (VipsForeignLoadClass *) vips_foreign_map( "VipsForeignLoad", vips_foreign_find_load_stream_sub, - input, NULL )) ) { + streami, NULL )) ) { vips_error( "VipsForeignLoad", "%s", _( "stream is not in a known format" ) ); return( NULL ); @@ -723,15 +723,15 @@ vips_foreign_is_a_buffer( const char *loader, const void *data, size_t size ) /** * vips_foreign_is_a_stream: * @loader: name of loader to use for test - * @input: stream to test + * @streami: stream to test * - * Return %TRUE if @input can be loaded by @loader. @loader is something + * Return %TRUE if @streami can be loaded by @loader. @loader is something * like "tiffload_stream" or "VipsForeignLoadTiffStream". * * Returns: %TRUE if @data can be loaded by @stream. */ gboolean -vips_foreign_is_a_stream( const char *loader, VipsStreamInput *input ) +vips_foreign_is_a_stream( const char *loader, VipsStreami *streami ) { const VipsObjectClass *class; VipsForeignLoadClass *load_class; @@ -740,7 +740,7 @@ vips_foreign_is_a_stream( const char *loader, VipsStreamInput *input ) return( FALSE ); load_class = VIPS_FOREIGN_LOAD_CLASS( class ); if( load_class->is_a_stream && - load_class->is_a_stream( input ) ) + load_class->is_a_stream( streami ) ) return( TRUE ); return( FALSE ); diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index 40bf67df..de481f00 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -79,11 +79,11 @@ int vips__tiff_write_buf( VipsImage *in, VipsRegionShrink region_shrink, int level, gboolean lossless ); -gboolean vips__istiff_stream( VipsStreamInput *input ); -gboolean vips__istifftiled_stream( VipsStreamInput *input ); -int vips__tiff_read_header_stream( VipsStreamInput *input, VipsImage *out, +gboolean vips__istiff_stream( VipsStreami *streami ); +gboolean vips__istifftiled_stream( VipsStreami *streami ); +int vips__tiff_read_header_stream( VipsStreami *streami, VipsImage *out, int page, int n, gboolean autorotate ); -int vips__tiff_read_stream( VipsStreamInput *input, VipsImage *out, +int vips__tiff_read_stream( VipsStreami *streami, VipsImage *out, int page, int n, gboolean autorotate ); extern const char *vips__foreign_tiff_suffs[]; @@ -163,25 +163,25 @@ extern const char *vips__rad_suffs[]; extern const char *vips__jpeg_suffs[]; -int vips__jpeg_write_stream( VipsImage *in, VipsStreamOutput *output, +int vips__jpeg_write_stream( VipsImage *in, VipsStreamo *streamo, int Q, const char *profile, gboolean optimize_coding, gboolean progressive, gboolean strip, gboolean no_subsample, gboolean trellis_quant, gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ); -int vips__jpeg_read_stream( VipsStreamInput *input, VipsImage *out, +int vips__jpeg_read_stream( VipsStreami *streami, VipsImage *out, gboolean header_only, int shrink, int fail, gboolean autorotate ); -int vips__isjpeg_stream( VipsStreamInput *input ); +int vips__isjpeg_stream( VipsStreami *streami ); -int vips__png_ispng_stream( VipsStreamInput *input ); -int vips__png_header_stream( VipsStreamInput *input, VipsImage *out ); -int vips__png_read_stream( VipsStreamInput *input, VipsImage *out, +int vips__png_ispng_stream( VipsStreami *streami ); +int vips__png_header_stream( VipsStreami *streami, VipsImage *out ); +int vips__png_read_stream( VipsStreami *streami, VipsImage *out, gboolean fail ); -gboolean vips__png_isinterlaced_stream( VipsStreamInput *input ); +gboolean vips__png_isinterlaced_stream( VipsStreami *streami ); extern const char *vips__png_suffs[]; -int vips__png_write_stream( VipsImage *in, VipsStreamOutput *output, +int vips__png_write_stream( VipsImage *in, VipsStreamo *streamo, int compress, int interlace, const char *profile, VipsForeignPngFilter filter, gboolean strip, gboolean palette, int colours, int Q, double dither ); @@ -198,14 +198,14 @@ extern const VipsWebPNames vips__webp_names[]; extern const int vips__n_webp_names; extern const char *vips__webp_suffs[]; -int vips__iswebp_stream( VipsStreamInput *input ); +int vips__iswebp_stream( VipsStreami *streami ); -int vips__webp_read_header_stream( VipsStreamInput *input, VipsImage *out, +int vips__webp_read_header_stream( VipsStreami *streami, VipsImage *out, int page, int n, double scale ); -int vips__webp_read_stream( VipsStreamInput *input, VipsImage *out, +int vips__webp_read_stream( VipsStreami *streami, VipsImage *out, int page, int n, double scale ); -int vips__webp_write_stream( VipsImage *image, VipsStreamOutput *output, +int vips__webp_write_stream( VipsImage *image, VipsStreamo *streamo, int Q, gboolean lossless, VipsForeignWebpPreset preset, gboolean smart_subsample, gboolean near_lossless, int alpha_q, int reduction_effort, diff --git a/libvips/foreign/pngload.c b/libvips/foreign/pngload.c index 475ea70c..723a3ccc 100644 --- a/libvips/foreign/pngload.c +++ b/libvips/foreign/pngload.c @@ -57,7 +57,7 @@ typedef struct _VipsForeignLoadPngStream { /* Load from a stream. */ - VipsStreamInput *input; + VipsStreami *input; } VipsForeignLoadPngStream; @@ -153,7 +153,7 @@ G_DEFINE_TYPE( VipsForeignLoadPng, vips_foreign_load_png, static gboolean vips_foreign_load_png_is_a( const char *filename ) { - VipsStreamInput *input; + VipsStreami *input; gboolean result; if( !(input = vips_stream_input_new_from_filename( filename )) ) @@ -167,7 +167,7 @@ vips_foreign_load_png_is_a( const char *filename ) static VipsForeignFlags vips_foreign_load_png_get_flags_filename( const char *filename ) { - VipsStreamInput *input; + VipsStreami *input; VipsForeignFlags flags; if( !(input = vips_stream_input_new_from_filename( filename )) ) @@ -197,7 +197,7 @@ vips_foreign_load_png_header( VipsForeignLoad *load ) { VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; - VipsStreamInput *input; + VipsStreami *input; if( !(input = vips_stream_input_new_from_filename( png->filename )) ) return( -1 ); @@ -215,7 +215,7 @@ vips_foreign_load_png_load( VipsForeignLoad *load ) { VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; - VipsStreamInput *input; + VipsStreami *input; if( !(input = vips_stream_input_new_from_filename( png->filename )) ) return( -1 ); @@ -285,7 +285,7 @@ G_DEFINE_TYPE( VipsForeignLoadPngBuffer, vips_foreign_load_png_buffer, static gboolean vips_foreign_load_png_buffer_is_a_buffer( const void *buf, size_t len ) { - VipsStreamInput *input; + VipsStreami *input; gboolean result; if( !(input = vips_stream_input_new_from_memory( buf, len )) ) @@ -301,7 +301,7 @@ vips_foreign_load_png_buffer_get_flags( VipsForeignLoad *load ) { VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) load; - VipsStreamInput *input; + VipsStreami *input; VipsForeignFlags flags; if( !(input = vips_stream_input_new_from_memory( buffer->buf->data, @@ -324,7 +324,7 @@ vips_foreign_load_png_buffer_header( VipsForeignLoad *load ) { VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) load; - VipsStreamInput *input; + VipsStreami *input; if( !(input = vips_stream_input_new_from_memory( buffer->buf->data, buffer->buf->length )) ) @@ -343,7 +343,7 @@ vips_foreign_load_png_buffer_load( VipsForeignLoad *load ) { VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) load; - VipsStreamInput *input; + VipsStreami *input; if( !(input = vips_stream_input_new_from_memory( buffer->buf->data, buffer->buf->length )) ) @@ -469,7 +469,7 @@ vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... ) * Returns: 0 on success, -1 on error. */ int -vips_pngload_stream( VipsStreamInput *input, VipsImage **out, ... ) +vips_pngload_stream( VipsStreami *input, VipsImage **out, ... ) { va_list ap; int result; diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 180585ff..9d962b3a 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -189,7 +189,7 @@ typedef struct _VipsForeignLoadClass { * This function should return %TRUE if the stream contains an image of * this type. */ - gboolean (*is_a_stream)( VipsStreamInput *stream ); + gboolean (*is_a_stream)( VipsStreami *streami ); /* Get the flags from a filename. * @@ -240,14 +240,14 @@ GType vips_foreign_load_get_type(void); const char *vips_foreign_find_load( const char *filename ); const char *vips_foreign_find_load_buffer( const void *data, size_t size ); -const char *vips_foreign_find_load_stream( VipsStreamInput *stream ); +const char *vips_foreign_find_load_stream( VipsStreami *streami ); VipsForeignFlags vips_foreign_flags( const char *loader, const char *filename ); gboolean vips_foreign_is_a( const char *loader, const char *filename ); gboolean vips_foreign_is_a_buffer( const char *loader, const void *data, size_t size ); gboolean vips_foreign_is_a_stream( const char *loader, - VipsStreamInput *stream ); + VipsStreami *streami ); void vips_foreign_load_invalidate( VipsImage *image ); @@ -369,7 +369,7 @@ int vips_jpegload( const char *filename, VipsImage **out, ... ) int vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) __attribute__((sentinel)); -int vips_jpegsave_stream( VipsImage *in, VipsStreamOutput *output, ... ) +int vips_jpegsave_stream( VipsImage *in, VipsStreamo *streamo, ... ) __attribute__((sentinel)); int vips_jpegsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); @@ -399,14 +399,14 @@ typedef enum { VIPS_FOREIGN_WEBP_PRESET_LAST } VipsForeignWebpPreset; -int vips_webpload_stream( VipsStreamInput *input, VipsImage **out, ... ) +int vips_webpload_stream( VipsStreami *streami, VipsImage **out, ... ) __attribute__((sentinel)); int vips_webpload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_webpload_buffer( void *buf, size_t len, VipsImage **out, ... ) __attribute__((sentinel)); -int vips_webpsave_stream( VipsImage *in, VipsStreamOutput *output, ... ) +int vips_webpsave_stream( VipsImage *in, VipsStreamo *streamo, ... ) __attribute__((sentinel)); int vips_webpsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); @@ -481,7 +481,7 @@ int vips_tiffload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_tiffload_buffer( void *buf, size_t len, VipsImage **out, ... ) __attribute__((sentinel)); -int vips_tiffload_stream( VipsStreamInput *input, VipsImage **out, ... ) +int vips_tiffload_stream( VipsStreami *streami, VipsImage **out, ... ) __attribute__((sentinel)); int vips_tiffsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); @@ -549,13 +549,13 @@ typedef enum /*< flags >*/ { VIPS_FOREIGN_PNG_FILTER_ALL = 0xF8 } VipsForeignPngFilter; -int vips_pngload_stream( VipsStreamInput *input, VipsImage **out, ... ) +int vips_pngload_stream( VipsStreami *streami, VipsImage **out, ... ) __attribute__((sentinel)); int vips_pngload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... ) __attribute__((sentinel)); -int vips_pngsave_stream( VipsImage *in, VipsStreamOutput *output, ... ) +int vips_pngsave_stream( VipsImage *in, VipsStreamo *streamo, ... ) __attribute__((sentinel)); int vips_pngsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index 5d26a69b..b72e8898 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -447,7 +447,7 @@ VipsImage *vips_image_new_from_memory_copy( const void *data, size_t size, VipsImage *vips_image_new_from_buffer( const void *buf, size_t len, const char *option_string, ... ) __attribute__((sentinel)); -VipsImage *vips_image_new_from_stream( VipsStreamInput *input, +VipsImage *vips_image_new_from_stream( VipsStreami *streami, const char *option_string, ... ) __attribute__((sentinel)); VipsImage *vips_image_new_matrix( int width, int height ); VipsImage *vips_image_new_matrixv( int width, int height, ... ); @@ -471,7 +471,7 @@ int vips_image_write_to_buffer( VipsImage *in, const char *suffix, void **buf, size_t *size, ... ) __attribute__((sentinel)); int vips_image_write_to_stream( VipsImage *in, - const char *suffix, VipsStreamOutput *output, ... ) + const char *suffix, VipsStreamo *streamo, ... ) __attribute__((sentinel)); void *vips_image_write_to_memory( VipsImage *in, size_t *size ); diff --git a/libvips/include/vips/resample.h b/libvips/include/vips/resample.h index 5d0501ed..8acac343 100644 --- a/libvips/include/vips/resample.h +++ b/libvips/include/vips/resample.h @@ -79,7 +79,7 @@ int vips_thumbnail_buffer( void *buf, size_t len, VipsImage **out, __attribute__((sentinel)); int vips_thumbnail_image( VipsImage *in, VipsImage **out, int width, ... ) __attribute__((sentinel)); -int vips_thumbnail_stream( VipsStreamInput *input, VipsImage **out, +int vips_thumbnail_stream( VipsStreami *streami, VipsImage **out, int width, ... ) __attribute__((sentinel)); diff --git a/libvips/iofuncs/streami.c b/libvips/iofuncs/streami.c index 2ec6cedb..1fb9eaf2 100644 --- a/libvips/iofuncs/streami.c +++ b/libvips/iofuncs/streami.c @@ -33,17 +33,15 @@ /* TODO * - * - catch out of range seeks - * - some sanity thing to prevent endless streams filling memory? * - filename encoding - * - seek END and _size need thinking about with pipes ... perhaps we should - * force-read the whole thing in + * - are we detecting EOF correctly? what about interrupted reads? perhaps + * we should check errno as well * - _size() needs to be a vfunc too (libtiff needs it) * - _miminize() is a vfunc, so _open() must be as well * + perhaps minimise should not be a vfunc? is it really useful for * subclasses? - * + perhaps make _open() / _close() into vfuncs and don't expose minimise - * - only fetch length once + * + perhaps make _open() / _close() into vfuncs on stream (not streami) and + * don't expose minimise * - need to be able to set is_pipe via constructor * - test we can really change all behaviour in the subclass ... add callbacks * as well to make it simpler for language bindings @@ -592,7 +590,7 @@ vips_streami_read( VipsStreami *streami, void *buffer, size_t length ) VIPS_DEBUG_MSG( " %zd bytes from cache\n", available ); } - /* Any more bytes required? Call the read() vfunc. + /* Any more bytes requested? Call the read() vfunc. */ if( length > 0 ) { ssize_t n; @@ -626,30 +624,63 @@ vips_streami_read( VipsStreami *streami, void *buffer, size_t length ) return( bytes_read ); } -/* Read the entire pipe into memory and turn this into a memory source stream. +/* Read a pipe to at least a position. -1 means read to end of stream. Does + * not chenge read_position. */ static int -vips_streami_read_whole_pipe( VipsStreami *streami ) +vips_streami_pipe_read_to_position( VipsStreami *streami, gint64 target ) { gint64 old_read_position; unsigned char buffer[4096]; unsigned char *data; - VIPS_DEBUG_MSG( "vips_streami_read_whole_pipe:\n" ); + VIPS_DEBUG_MSG( "vips_streami_pipe_read_position:\n" ); vips_streami_sanity( streami ); + if( streami->decode ) { + vips_error( vips_stream_name( VIPS_STREAM( streami ) ), + "%s", _( "can't seek pipe after " + "pixel decode begins" ) ); + return( -1 ); + } + old_read_position = streami->read_position; - if( vips_streami_rewind( streami ) ) - return( -1 ); /* TODO ... add something to prevent unbounded streams filling memory. */ - while( vips_streami_read( streami, buffer, 4096 ) > 0 ) - ; + while( target == -1 || + streami->read_position < target ) { + ssize_t read; + + read = vips_streami_read( streami, buffer, 4096 ); + if( read == -1 ) + return( -1 ); + if( read == 0 ) + break; + } streami->read_position = old_read_position; + vips_streami_sanity( streami ); + + return( 0 ); +} + +/* Read the entire pipe into memory and turn this into a memory source stream. + */ +static int +vips_streami_pipe_to_memory( VipsStreami *streami ) +{ + unsigned char *data; + + VIPS_DEBUG_MSG( "vips_streami_pipe_to_memory:\n" ); + + vips_streami_sanity( streami ); + + if( vips_streami_pipe_read_to_position( streami, -1 ) ) + return( -1 ); + /* Move header_bytes into the memory blob and set up as a memory * source. */ @@ -678,6 +709,12 @@ vips_streami_map( VipsStreami *streami, size_t *length_out ) vips_streami_sanity( streami ); + /* Pipes need to be converted to memory streams. + */ + if( streami->is_pipe && + vips_streami_pipe_to_memory( streami ) ) + return( NULL ); + /* Memory source ... easy! */ if( streami->blob ) { @@ -685,12 +722,6 @@ vips_streami_map( VipsStreami *streami, size_t *length_out ) data = vips_blob_get( streami->blob, &length ); } - else if( streami->is_pipe ) { - if( vips_streami_read_whole_pipe( streami ) ) - return( NULL ); - - data = vips_blob_get( streami->blob, &length ); - } else { /* A streami that supports mmap. */ @@ -734,7 +765,8 @@ vips_streami_seek( VipsStreami *streami, gint64 offset, int whence ) case SEEK_END: if( streami->length == -1 && - vips_streami_read_whole_pipe( streami ) ) + streami->is_pipe && + vips_streami_pipe_to_memory( streami ) ) return( -1 ); new_pos = streami->length + offset; @@ -747,29 +779,18 @@ vips_streami_seek( VipsStreami *streami, gint64 offset, int whence ) break; } + /* Don't allow out of range seeks. + */ + if( new_pos < 0 || + (streami->length != -1 && new_pos >= streami->length) ) { + vips_error( vips_stream_name( VIPS_STREAM( streami ) ), + _( "bad seek to %" G_GINT64_FORMAT ), new_pos ); + return( -1 ); + } + if( streami->is_pipe ) { - /* We can seek on non-seekable streams during the header phase. - */ - if( streami->decode ) { - vips_error( STREAM_NAME( streami ), - "%s", _( "can't rewind after decode begins" ) ); + if( vips_streami_pipe_read_to_position( streami, new_pos ) ) return( -1 ); - } - - g_assert( streami->header_bytes ); - - /* We may not have read up to the new position. - */ - while( streami->read_position < new_pos ) { - unsigned char buffer[4096]; - ssize_t read; - - read = vips_streami_read( streami, buffer, 4096 ); - if( read < 0 ) - return( -1 ); - if( read == 0 ) { - } - } } else { if( (new_pos = class->seek( streami, offset, whence )) == -1 ) @@ -877,13 +898,12 @@ vips_streami_size( VipsStreami *streami ) vips_streami_sanity( streami ); - if( stream->descriptor >= 0 && - (size = vips_file_length( stream->descriptor )) >= 0 ) - return( size ); - else if( streami->blob ) - return( VIPS_AREA( streami->blob )->length ); - else if( streami->header_bytes ) - return( streami->header_bytes->len ); - else + if( streami->length == -1 && + streami->is_pipe && + vips_streami_pipe_to_memory( streami ) ) return( -1 ); + + vips_streami_sanity( streami ); + + return( streami->length ); }