diff --git a/libvips/include/vips/stream.h b/libvips/include/vips/stream.h index 8c6767f1..27a76b7d 100644 --- a/libvips/include/vips/stream.h +++ b/libvips/include/vips/stream.h @@ -173,24 +173,15 @@ typedef struct _VipsStreamiClass { /* Subclasses can define these to implement other streami methods. */ - /* Read up to N bytes from the stream into the supplied buffer, - * returning the number of bytes actually read. - * - * -1 on error, 0 on EOF. + /* Read from the stream into the supplied buffer, args exactly as + * read(2). */ ssize_t (*read)( VipsStreami *, void *, size_t ); - /* Map the entire stream into memory, for example with mmap(). Return - * the base and size of the mapped area. + /* Seek to a certain position, args exactly as lseek(2). * - * If this is not defined, the file will be read in with repeated - * calls to ->read(). - * - * NULL on error. - */ - const void *(*map)( VipsStreami *, size_t * ); - - /* Seek to a certain position, args exactly as lseek(2). + * Unseekable streams should just return -1. VipsStreami will then + * seek by _read()ing bytes into memory as required. */ gint64 (*seek)( VipsStreami *, gint64 offset, int ); @@ -207,11 +198,6 @@ typedef struct _VipsStreamiClass { */ int (*unminimise)( VipsStreami * ); - /* Length of object in bytes. If this is a pipe, it will force the - * whole object to be read into memory, so use cautiously. - */ - gint64 (*size)( VipsStreami * ); - } VipsStreamiClass; GType vips_streami_get_type( void ); diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index 7d5a3416..a70e0b29 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -277,7 +277,6 @@ GSList *vips__gslist_gvalue_merge( GSList *a, const GSList *b ); char *vips__gslist_gvalue_get( const GSList *list ); gint64 vips__seek( int fd, gint64 pos, int whence ); -gboolean vips__can_seek( int fd ); int vips__ftruncate( int fd, gint64 pos ); int vips_existsf( const char *name, ... ) __attribute__((format(printf, 1, 2))); diff --git a/libvips/iofuncs/streami.c b/libvips/iofuncs/streami.c index 8dcfb98f..7cbf96c5 100644 --- a/libvips/iofuncs/streami.c +++ b/libvips/iofuncs/streami.c @@ -34,6 +34,8 @@ /* TODO * * - filename encoding + * - gaussblur is missing the vector path again argh + * - can we map and then close the fd? * - are we detecting EOF correctly? what about interrupted reads? perhaps * we should check errno as well * - need to be able to set is_pipe via constructor @@ -203,6 +205,7 @@ vips_streami_build( VipsObject *object ) { VipsStream *stream = VIPS_STREAM( object ); VipsStreami *streami = VIPS_STREAMI( object ); + VipsStreamiClass *class = VIPS_STREAMI_GET_CLASS( streami ); VIPS_DEBUG_MSG( "vips_streami_build: %p\n", streami ); @@ -234,28 +237,28 @@ vips_streami_build( VipsObject *object ) */ if( stream->descriptor != -1 ) { /* Can we seek? If not, this is some kind of pipe. + * + * We must call the class method directly: if we go via + * vips_streami_seek() we'll trigger seek emulation on pipes. */ - if( !vips__can_seek( stream->descriptor ) ) { + vips_error_freeze(); + if( class->seek( stream->descriptor, 0, SEEK_CUR ) == -1 ) { VIPS_DEBUG_MSG( " not seekable\n" ); streami->is_pipe = TRUE; } + vips_error_thaw(); - /* Try and get the length. Don't bother for pipes. + /* Try and get the length, as long as we're seekable. */ if( !streami->is_pipe && - (streami->length = - vips_file_length( stream->descriptor )) == -1 ) + (streami->length = vips_streami_size( streami )) == -1 ) return( -1 ); } - /* Need to save the header for pipe-style sources. + /* If we can seek, we won't need to save header bytes. */ - if( streami->is_pipe ) - streami->header_bytes = g_byte_array_new(); - - /* We always want a sniff buffer. - */ - streami->sniff = g_byte_array_new(); + if( !streami->is_pipe ) + VIPS_FREEF( g_byte_array_unref, streami->header_bytes ); return( 0 ); } @@ -267,44 +270,7 @@ vips_streami_read_real( VipsStreami *streami, void *data, size_t length ) VIPS_DEBUG_MSG( "vips_streami_read_real:\n" ); - if( streami->blob ) { - VipsArea *area = VIPS_AREA( streami->blob ); - ssize_t available = VIPS_MIN( length, - area->length - streami->read_position ); - - if( available <= 0 ) - return( 0 ); - - memcpy( data, area->data + streami->read_position, available ); - - return( available ); - } - else if( stream->descriptor != -1 ) { - return( read( stream->descriptor, data, length ) ); - } - else { - g_assert( 0 ); - return( -1 ); - } -} - -static const void * -vips_streami_map_real( VipsStreami *streami, size_t *length ) -{ - VipsStream *stream = VIPS_STREAM( streami ); - - const void *file_baseaddr; - - g_assert( streami->length > 0 ); - - if( !(file_baseaddr = vips__mmap( stream->descriptor, - FALSE, streami->length, 0 )) ) - return( NULL ); - - if( length ) - *length = streami->length; - - return( file_baseaddr ); + return( read( stream->descriptor, data, length ) ); } static gint64 @@ -312,39 +278,9 @@ vips_streami_seek_real( VipsStreami *streami, gint64 offset, int whence ) { VipsStream *stream = VIPS_STREAM( streami ); - gint64 new_pos; - VIPS_DEBUG_MSG( "vips_streami_seek_real:\n" ); - if( streami->blob ) { - switch( whence ) { - case SEEK_SET: - new_pos = offset; - break; - - case SEEK_CUR: - new_pos = streami->read_position + offset; - break; - - case SEEK_END: - new_pos = streami->length + offset; - break; - - default: - g_assert_not_reached(); - break; - } - } - else if( stream->descriptor != -1 ) { - new_pos = vips__seek( stream->descriptor, offset, whence ); - } - else { - vips_error( vips_stream_name( stream ), - "%s", _( "not seekable" ) ); - return( -1 ); - } - - return( new_pos ); + return( vips__seek( stream->descriptor, offset, whence ) ); } static void @@ -444,6 +380,8 @@ vips_streami_pipe_to_memory( VipsStreami *streami ) VIPS_DEBUG_MSG( "vips_streami_pipe_to_memory:\n" ); + g_assert( streami->is_pipe ); + vips_streami_sanity( streami ); if( vips_streami_pipe_read_to_position( streami, -1 ) ) @@ -511,6 +449,8 @@ static void vips_streami_init( VipsStreami *streami ) { streami->length = -1; + streami->sniff = g_byte_array_new(); + streami->header_bytes = g_byte_array_new(); } /** @@ -700,6 +640,20 @@ vips_streami_read( VipsStreami *streami, void *buffer, size_t length ) VIPS_DEBUG_MSG( " %zd bytes from cache\n", available ); } +revise this: + if( streami->blob ) { + VipsArea *area = VIPS_AREA( streami->blob ); + ssize_t available = VIPS_MIN( length, + area->length - streami->read_position ); + + if( available <= 0 ) + return( 0 ); + + memcpy( data, area->data + streami->read_position, available ); + + return( available ); + } + /* Any more bytes requested? Call the read() vfunc. */ if( length > 0 ) { @@ -735,6 +689,32 @@ vips_streami_read( VipsStreami *streami, void *buffer, size_t length ) return( bytes_read ); } +static const void * +vips_streami_map_real( VipsStreami *streami, size_t *length ) +{ + VipsStream *stream = VIPS_STREAM( streami ); + + const void *file_baseaddr; + + g_assert( streami->length > 0 ); + + if( !(file_baseaddr = vips__mmap( stream->descriptor, + FALSE, streami->length, 0 )) ) + return( NULL ); + + if( length ) + *length = streami->length; + + return( file_baseaddr ); +} + +static void +vips_streami_unmap_real( VipsStreami *streami, size_t *length ) +{ + VipsStream *stream = VIPS_STREAM( streami ); + +} + const void * vips_streami_map( VipsStreami *streami, size_t *length_out ) { @@ -800,6 +780,12 @@ vips_streami_seek( VipsStreami *streami, gint64 offset, int whence ) vips_streami_sanity( streami ); + /* Not tagged as a pipe? Attempt to use the seek method. + */ + if( !streami->is_pipe ) { + if( (new_pos = class->seek( streami, offset, whence )) == -1 ) + return( -1 ); + switch( whence ) { case SEEK_SET: new_pos = offset; @@ -834,6 +820,27 @@ vips_streami_seek( VipsStreami *streami, gint64 offset, int whence ) return( -1 ); } +revise this: + if( streami->blob ) { + switch( whence ) { + case SEEK_SET: + new_pos = offset; + break; + + case SEEK_CUR: + new_pos = streami->read_position + offset; + break; + + case SEEK_END: + new_pos = streami->length + offset; + break; + + default: + g_assert_not_reached(); + break; + } + } + if( streami->is_pipe ) { if( vips_streami_pipe_read_to_position( streami, new_pos ) ) return( -1 ); @@ -907,12 +914,15 @@ vips_streami_size( VipsStreami *streami ) VipsStreamiClass *class = VIPS_STREAMI_GET_CLASS( streami ); gint64 size; + gint64 read_position; VIPS_DEBUG_MSG( "vips_streami_size:\n" ); vips_streami_sanity( streami ); - size = class->size( streami ); + read_position = vips_streami_seek( streami, 0, SEEK_CUR ); + size = vips_streami_seek( streami, 0, SEEK_END ); + vips_streami_seek( streami, read_position, SEEK_SET ); vips_streami_sanity( streami ); diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index 0d51440c..1aeb26f1 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -1062,20 +1062,6 @@ vips__seek( int fd, gint64 pos, int whence ) return( new_pos ); } -/* Does an fd support seek? Pipes do not, for example. - */ -gboolean -vips__can_seek( int fd ) -{ - gboolean result; - - vips_error_freeze(); - result = vips__seek( fd, 0, SEEK_CUR ) != -1; - vips_error_thaw(); - - return( result ); -} - /* Need our own ftruncate(), since ftruncate() on win32 can't do long files. DANGER ... this moves the file pointer to the end of file on win32,